Differences between C and C++

K

Keith Thompson

James Kuyper said:
It was Keith who first talked about "re-writes", and he prefixed that
word with the adjective "minor" - that makes it pretty hard to justify
assuming he was talking about large changes.

Kenneth only used the word "changed", without any adjectives to
suggest how big of a change he was talking about. I think you read
more into his statement than he put into it.

Actually, James, I was quoting you (which is why I used quotation
marks):

Most C programs can, with minor re-writes, be converted into
code which will compile with either language, with exactly
the same meaning in both languages. It's relatively easy to
deliberately write code for which this is not true; but such
code is usually clearly contrived.
 
J

Joshua Maurice

It was Keith who first talked about "re-writes", and he prefixed that
word with the adjective "minor" - that makes it pretty hard to justify
assuming he was talking about large changes.

Kenneth only used the word "changed", without any adjectives to suggest
how big of a change he was talking about. I think you read more into his
statement than he put into it.

I don't think it's worth any of our time past this to start a quote
argument war over such a minor detail when we're all agreeing. I'm not
out to call anyone wrong for its own purpose. If it makes it better, I
was wrong, and I apologize for misreading and mischaracterizing his
position.
 
C

Chris H

Seebs said:
UK English and US English are pretty comparable. There are cases where
they differ. Heck, there are sentences which have their meaning completely
reversed between the two languages. But most people tend to view them as
so similar that some call them "dialects" of the same "language".

I'd tend to view the "common subset" parts of C and C++ as being about like
US and UK English. It would be foolhardy to ignore the differences, but that
doesn't mean they're a huge deal for most writers.

The small differences can and have been fatal
 
I

Ian Collins

My main point was that C code which doesn't cast malloc() is hardly
"contrived".

Whether or not grepping and changing all occurrences of
malloc-and-friends to include the cast is to be considered a "minor
re-write" (or desirable if the code is still to be considered "C") is up
for debate.

The times where I have built C projects as C++, I have substituted
malloc and friends with a wrapper (say xmalloc) where the wrapper is a
macro for malloc in C and an inline template function in C++. I found
that "cleaner" than smattering the code with casts. Yes it does require
some effort, but the cases where I do this (originally to get better
static checking, more recently for testing) the results justify the effort.
And this doesn't even take into account "void*" members in structs.

:)
 
K

Keith Thompson

Kenneth Brody said:
The original statement to which I was responding has been lost somewhere
along the line of quoting, so here it is again:


My main point was that C code which doesn't cast malloc() is hardly
"contrived".
Agreed.

Whether or not grepping and changing all occurrences of malloc-and-friends
to include the cast is to be considered a "minor re-write" (or desirable if
the code is still to be considered "C") is up for debate.

I would certainly consider adding the cast to be a "minor re-write".
It's an almost mechanical and easily reversible process. (Compare this
to the level of re-write that would be needed to convert a typical C++
program to C, or C to Fortran, or ...)

My guess is that most C programs don't have all that many malloc()
calls; many of them will certainly call malloc() many many times, but
there are probably only a few such calls in the source.

I'd say that the *only* reason to do such a rewrite, minor or not, would
be to make C code compilable as C++ -- and there aren't a whole lot of
cases where that's a desirable thing to do in the first place.

In summary, given a reasonably large C program, making it compilable as
C++ is likely to require changing only a few lines of code here and
there, in ways that don't require a lot of effort; the whole process
is likely to be more tedious than difficult. There are a few cases
where the same code is valid C and C++ with different meanings,
but I don't think those are likely to be an issue in non-contrived
programs. (In practice, I suspect that porting from one compiler
to another will give you more problems, unless the code was carefully
written to be portable in the first place.)

The resulting code will probably not be well-written C, and it almost
certainly won't be well-written C++, but that's not the issue. The
point is that, stylistic issues aside, C is *nearly* a subset of C++.

(And yes, I know that "nearly a subset" implies "not a subset".)
And this doesn't even take into account "void*" members in structs.

I'm not sure how that changes anything. Can you be more specific?
 
J

James Kuyper

On 02/14/2011 04:53 PM, Kenneth Brody wrote:
[> On 2/08/2011 08:07 PM, James Kuyper wrote:]
....
My main point was that C code which doesn't cast malloc() is hardly
"contrived".

And I was not suggesting that it was.
Whether or not grepping and changing all occurrences of
malloc-and-friends to include the cast is to be considered a "minor
re-write" (or desirable if the code is still to be considered "C") is up
for debate.

And I'm strongly in favor of considering it a "minor re-write". Since
the purpose of my comment was to point out that the overwhelming
proportion of the features of C are also features of C++, the fact that
the code after the re-write has undesirable features from the point of
view of single-language programming is irrelevant. My main point was
that the conversion is possible; and doesn't require large changes in
the code. That would be downright impossible if C and C++ were truly
independent, only weakly connected by some shared history.
And this doesn't even take into account "void*" members in structs.

That's just another special case of the same general rule: in C,
conversions to and from void* occur implicitly, in C++, they must be
explicit. The C rule is far more convenient, and fits in well with the
intended uses of void*. The C++ rule provides better type safety, an
issue that carries more weight in C++ programming than in C. It's
certainly a difference that needs to be attended to; but it's only a
tiny issue by comparison with the issues that would need to be dealt
with when converting a program between two really different languages,
such as, for example, C and Haskell.
 
K

Keith Thompson

Kenneth Brody said:
My understanding of C++ (and I fully admit I may be wrong) is that you can't
convert between "void*" and "something_else*" without an explicit cast.

Right. (Actually, I think there's a little bit of looseness, but not as
much as in C.) I was just wondering what was special about struct
members.
 
M

Malcolm McLean

My guess is that most C programs don't have all that many malloc()
calls; many of them will certainly call malloc() many many times, but
there are probably only a few such calls in the source.
I use malloc() very heavily.

Partly to avoid magic constants and arbitrary limits to input length
in code, partly because it's neat to simply write

CURSOR *create_cursor(char *type);
void movecursor(CURSOR *cur, WINDOW *win, int x, int y);
void destroy_cursor(CURSOR *cur);

The struct might actually just be x, y co-ordinates and an enumerated
type for the graphic. But it's easier and neater to encapsulate it, by
allocating it on the heap.
 
B

Ben Pfaff

Malcolm McLean said:
I use malloc() very heavily.

Partly to avoid magic constants and arbitrary limits to input length
in code, partly because it's neat to simply write

CURSOR *create_cursor(char *type);
void movecursor(CURSOR *cur, WINDOW *win, int x, int y);
void destroy_cursor(CURSOR *cur);

The struct might actually just be x, y co-ordinates and an enumerated
type for the graphic. But it's easier and neater to encapsulate it, by
allocating it on the heap.

I personally try to avoid malloc() in a situation where it is not
really needed. The main reason is that any malloc() ordinarily
has to have a corresponding free(), which makes it harder to see
that code is correct and complicates error handling. Performance
is another reason, although it has become less important to me
over time.
 
I

Ian Collins

I use malloc() very heavily.

You can use it heavily without many instances in the code.
Partly to avoid magic constants and arbitrary limits to input length
in code, partly because it's neat to simply write

CURSOR *create_cursor(char *type);
void movecursor(CURSOR *cur, WINDOW *win, int x, int y);
void destroy_cursor(CURSOR *cur);

Which probably backs Keith's conjecture, yes you use dynamic allocation
a lot, but you don't have many instances of "malloc" in your code.
 
B

BGB

I was under the impression that an empty parameter list in a function
(as above) is allowed so that legacy code is not broken and that we
should now write

int main(void) {...}

Then, I am only a recreational programmer and and as long as the
program works as intended I am a happy camper.

this claim is mostly for C, but matters get a little more complex if C++
is involved (AFAIK, the "(void)" syntax is not officially supported in C++).

"()" works as expected in both languages (despite the slightly different
semantics between C and C++, as it is most commonly used they are
functionally equivalent), and IMO people demanding the use of "(void)"
are making a fuss over nothing.

or such...
 
I

Ian Collins

this claim is mostly for C, but matters get a little more complex if C++
is involved (AFAIK, the "(void)" syntax is not officially supported in
C++).

It is supported (8.3.5/2 "The parameter list (void) is equivalent to the
empty parameter list.").
 
B

Ben Bacarisse

BGB said:
this claim is mostly for C, but matters get a little more complex if
C++ is involved (AFAIK, the "(void)" syntax is not officially
supported in C++).

No, it's official.
"()" works as expected in both languages (despite the slightly
different semantics between C and C++, as it is most commonly used
they are functionally equivalent),

If you know what to expect, yes, () works as expected. Yes, () has
different semantics between C and C++. I'd say they are not
"functionally equivalent", but without a definition of the term that is
just a quibble.
and IMO people demanding the use of
"(void)" are making a fuss over nothing.

Unless you are making some very specific point that I've missed, using
(void) in C function declarations is important. Using (void) makes
the declaration a prototype and forces the implementation to diagnose
mismatched calls. () on its own does not do this.

<snip>
 
K

Keith Thompson

BGB said:
this claim is mostly for C, but matters get a little more complex if C++
is involved (AFAIK, the "(void)" syntax is not officially supported in C++).

"()" works as expected in both languages (despite the slightly different
semantics between C and C++, as it is most commonly used they are
functionally equivalent), and IMO people demanding the use of "(void)"
are making a fuss over nothing.

or such...

In C, empty parentheses in a function declaration:

int foo()

mean that the function takes an unspecified number and type(s) of
argument(s). If the function takes no arguments, it's better to
say so explicitly:

int foo(void)

If empty parentheses appear in a declaration that's part of a
definition:

int foo()
{
/* ... */
}

then the function has no parameters -- but the compiler still isn't
required to check that any calls pass no arguments, and it's still
better to be explicit.

In the special case of main, calls are not usually an issue, but the
standard requires
int main(void)
or
int main(int argc, char *argv[])
"or equivalent; or in some other implementation-defined manner."

Here's where it gets a little tricky. The above is worded as a
"shall" requirement outside a constraint (see C99 5.1.2.2.1),
which means that violating it causes the program's behavior
to be undefined.

For the reasons I've mentioned above, I argue that "int main()" is
not *equivalent* to "int main(void)", so unless the implementation
documents that it accepts "int main()", the behavior of such a
program is undefined. In every C implementation I've seen (not that
many), a program with "int main()" works the same way as a program
with "int main(void)" (largely, I suppose, for historical reasons,
given that pre-ANSI C didn't have the "void" keyword). This is,
of course, one possible result of undefined behavior.

If you care about conforming to the C standard, write "int main(void)".
If you don't care quite as much, you can almost certainly get away
with "int main()", but that's not the way I'd go.

<OT>
In C++, "int main()" is the preferred form. "int main(void)" is
also accepted for C compatibility. If you care about making your
C++ code compilable as C, you should think carefully about why you
care about it. If you *still* care about it, you can safely use
"int main(void)" in either language, with no risk of misbehavior
on any conforming implementation. If you just want to write C++,
use "int main()", and give your source file a suffix that will
prevent any pure C compiler from accepting it.
</OT>
 
B

BGB

It is supported (8.3.5/2 "The parameter list (void) is equivalent to the
empty parameter list.").

ok, I am no expert on the C++ standard, so I wrote "AFAIK" there...
 
B

BGB

No, it's official.

yes, ok.

I had thought it was a more just a common extension (much like "//"
support in pre-C99 compilers...).

If you know what to expect, yes, () works as expected. Yes, () has
different semantics between C and C++. I'd say they are not
"functionally equivalent", but without a definition of the term that is
just a quibble.

they are functionally equivalent in that both can be called without any
arguments and will exhibit the same behavior.

there will be a difference at which point one calls the function with
arguments (in one case the compiler will reject the code making the call
and in the other it wont). however, if one assumes that the code is
in-error (by calling a function which doesn't take arguments with an
arguments list), then this doesn't result in a behavioral difference in
the code to be run.

Unless you are making some very specific point that I've missed, using
(void) in C function declarations is important. Using (void) makes
the declaration a prototype and forces the implementation to diagnose
mismatched calls. () on its own does not do this.

yes, it does do this, but this is not much different than passing or not
passing "-Werror-implicit-function-declaration" to GCC.

then one may use MSVC, which lacks this feature, but its absence doesn't
compromise the ability to write code, even though it is a little more
annoying having the compiler silently accept and produce erroneous
results with an incorrect arguments list... (I would prefer if MSVC had
an option to do similar, but oh well...).


interestingly though, I tend to prefer "()" more for aesthetic reasons
("(void)" looks a little more ugly IMO...), but interestingly enough
have also determined that I rarely make use of functions which don't
take arguments in C (I suspect part of this may be due to an avoidance
of storing stateful data in global variables, and for usually using
passed-in structs for storing state, thus leaving most no-argument
functions neither able to do or return much of anything useful).

hence, most of my use of "()" tends to be with non-C languages, even
despite C being my main language for writing code...


or such...
 
B

BGB

You can use it heavily without many instances in the code.


Which probably backs Keith's conjecture, yes you use dynamic allocation
a lot, but you don't have many instances of "malloc" in your code.

I just ran a check on a part of my project's codebase (via grep and a
linecounter):
493 lines of code using malloc with no cast;
144 lines of code using malloc with a cast;
637 total malloc lines.

now, checking for my GC (garbage collector):
263 lines calling "gcalloc()";
176 lines calling "gctalloc()";
439 total GC allocation lines.

size of code in question:
490715 lines in 857 files (C);
43954 lines in 157 files (H).

percentage of lines that are a malloc call: 0.13%
percentage of lines that are a GC allocation call: 0.09%
combined percentage of lines which allocate memory: 0.22%


now (from headers):
lines using "()" 459;
lines using "(void)" 24;
lines declaring a function prototype 15281.

percentage of no-argument functions: 3.16%


or such...
 
I

Ian Collins

now (from headers):
lines using "()" 459;
lines using "(void)" 24;
lines declaring a function prototype 15281.

percentage of no-argument functions: 3.16%

So you really are banking up problems!

One of the original reasons I put a C project through a C++ compiler was
to spot the (considerable number) of functions being called with the
wrong parameters.

I really really wish K&R style prototypes were removed completely from C.
 
C

Chris H

Ian Collins <ian- said:
So you really are banking up problems!

One of the original reasons I put a C project through a C++ compiler
was to spot the (considerable number) of functions being called with
the wrong parameters.


So why not do it properly and use a static analyser?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,085
Messages
2,570,597
Members
47,218
Latest member
GracieDebo

Latest Threads

Top