Differences between C and C++

B

BGB

Incorrect.

A prototype is just a function declaration that specifies the types of
the arguments. A non-prototype declaration always declares (perhaps
implicitly in C90, but still unambiguously) the return type.

This is not a prototype:

float func();

but it does declare that the return type is float.

In C90, if you call a function with no declaration at all (prototype or
otherwise), the compiler will assume that it returns an int -- but in
C99 that's a constraint violation.

yeah, I meant this case, where the function is just called with nothing
declaring it is in scope...

if the return type is not int (say it is a float or double, or a pointer
on some systems), then one gets a mangled or garbage return value, which
isn't so good.


MSVC defaults to assuming that any non-visible function returns int, so
it follows C90 rules by default...

also, last I checked it doesn't allow mixed statements and declarations
(all declarations needing to be at the start of the block).

but, it does have long long and '//' at least...
 
D

Dr Nick

James Kuyper said:
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.

The C idiom that makes the most use of this, IMO, and the one that would
be far uglier with casts, is the callback function such as is used in
qsort.

Ironically, looking for an example on the web to steal to show what I
meant, almost all the first hits used unnecessary and ugly casts (and
several said you had to use casts). But you know the sort of thing I
mean:

int callback_function(void *stuff) {
struct data *d = stuff;
/* now do things with all the members of d */

I've got generic "walk a list" functions for example that do this all
the time. These would be harder to do a general search-and-replace on
than malloc as well.
 
M

Malcolm McLean

And how would you convert the constant ADAMS (a president) to type
state?  If (state) ADAMS is legal, then surely (state) WASHINGTON
is ambiguous.
state x;

x = (state) (president) ADAMS;

that will take the constant ADAMS, resolve it to the president enum
(as opposed to something connected with Genesis), and then convert the
result to a state.

it's awkward, but it's not something you should be doing. It's hard to
think why a state enum would contain the same value as the index of a
president.

(state) ADAMS would trigger an error, because ADAMS isn't in the enum
state namespace. However president prez = ADAMS; state x = (state)
prez is OK.
 
M

Malcolm McLean

(type-name)expression already means something so I'd say that changing
that meaning is not very C-like.  
But we're not changing the meaning of the (type_name), we're changing
the meaning of the expression.

WASHINGTON is no longer an expression, unless it is in a context where
the type can be determined.
 
B

Ben Bacarisse

Malcolm McLean said:
But we're not changing the meaning of the (type_name), we're changing
the meaning of the expression.

WASHINGTON is no longer an expression, unless it is in a context where
the type can be determined.

This is the trouble with underspecified suggestions -- whatever I guess
you mean, you'll probably mean something else. It's still true that
what used to look like (type-name)expression has changed -- because you
want to alter what an expression is, rather than my guess that you were
re-defining some specific casts.

C does not that the strong typing you seem to need. You'll have to
alter several key parts of the language or live with a lot of explicit
conversions. Even

for (state s = FIRST; s <= LAST; s++) ...

won't work unless you redefine integer promotion (or make some other
change to the language that you've not yet communicated).
 
T

Tobias Blass

This is the trouble with underspecified suggestions -- whatever I guess
you mean, you'll probably mean something else. It's still true that
what used to look like (type-name)expression has changed -- because you
want to alter what an expression is, rather than my guess that you were
re-defining some specific casts.

C does not that the strong typing you seem to need. You'll have to
alter several key parts of the language or live with a lot of explicit
conversions. Even

for (state s = FIRST; s <= LAST; s++) ...

won't work unless you redefine integer promotion (or make some other
change to the language that you've not yet communicated).
I don't really understand why (state) WASHINGTON is better than just naming the
constant STATE_WASHINGTON. This is some kind of manual namespace, breaks no
existing code, doesn't require any change to the language and isn't even more to
type or harder to read. Could anyone explain me what this strong typed enum
is useful for?
 
B

Ben Bacarisse

Tobias Blass said:
I don't really understand why (state) WASHINGTON is better than just
naming the constant STATE_WASHINGTON. This is some kind of manual
namespace, breaks no existing code, doesn't require any change to the
language and isn't even more to type or harder to read. Could anyone
explain me what this strong typed enum is useful for?

In languages that have the right sort of type system, the big win is
type-safety. As you can probably tell from this exchange, I don't think
C has the right sort of type system to make this work -- at least not
without some unpleasant contortions.
 
M

Malcolm McLean

 Even

  for (state s = FIRST; s <= LAST; s++) ...

won't work unless you redefine integer promotion (or make some other
change to the language that you've not yet communicated).
s isn't an integer, it's an enum state. It's more similar to a char
than to an integer.

FIRST and LAST are no longer expressions, they become expressions when
attached to the nearest type, which is the enum state, s. So that
resolves them to the enum state namespace.
 
B

BartC

[Overloaded enum names]
Unless you change lots of other rules I suspect it will be quite
common. Consider 'area[WASHINGTON]' for example. You'd have to
re-define the integer promotions and where they occur.

You're right. I've struggled with this myself with language design.

You quickly run into more and more problems, and end up with a poor
imitation of Ada.

Taking the easy solution, requiring all names to be fully qualified, means
you have to write State.WASHINGTON everywhere; you might as well not bother
and just define it as State_WASHINGTON in the first place!

(Although perhaps writing State.WASHINGTON is a more grown-up way of dealing
with such names, if it doesn't encroach on the namespaces of struct members.
A bit like writing int:32 instead of int32_t...)
 
B

Ben Bacarisse

Malcolm McLean said:
s isn't an integer, it's an enum state. It's more similar to a char
than to an integer.

FIRST and LAST are no longer expressions, they become expressions when
attached to the nearest type, which is the enum state, s. So that
resolves them to the enum state namespace.

Yes, you said that before. Are you disagreeing with what I said?
 
I

Ian Collins

s isn't an integer, it's an enum state. It's more similar to a char
than to an integer.

If that is the case, how would you define s++?
FIRST and LAST are no longer expressions, they become expressions when
attached to the nearest type, which is the enum state, s. So that
resolves them to the enum state namespace.

Then you have to define the comparison operators for state.
 
J

Joel C. Salomon

And how would you convert the constant ADAMS (a president) to type
state? If (state) ADAMS is legal, then surely (state) WASHINGTON
is ambiguous.

Actually, (state) ADAMS is ambiguous too; does it resolve to 1 or 5? ;)

I’m not just being pedantic. Fundamentally, this is an issue of
context-dependent names. (E.g., does the symbol “Dracula†refer to the
book by Bram Stoker, the fictional vampire, or the historical Vlad ÈšepeÈ™?)

C doesn’t lend itself to this sort of distinction very well. IIRC, some
*very* early implementations didn’t even give struct members their own
name-space; i.e.,

struct foo {int bar; int bas;};
struct quux {int bas; int bar;};

would produce a conflict.

C’s fixed that long since, but it’s still an issue for enums. In C++
this limitation got so in the way that the name-space resolution
operator :: needed to be invented, but C can get by without it.

A C-like language with a nice treatment of the name-space issue would be
interesting. And by “nice†I mean that its macros fit in as well. (I
don’t mean D — it’s plenty interesting, but much more C++-like.)

—Joel
 
B

BartC

Joel C. Salomon said:
On 02/17/2011 11:00 AM, Keith Thompson wrote:
I’m not just being pedantic. Fundamentally, this is an issue of
context-dependent names. (E.g., does the symbol “Dracula†refer to the
book by Bram Stoker, the fictional vampire, or the historical Vlad ÈšepeÈ™?)

C doesn’t lend itself to this sort of distinction very well. IIRC, some
*very* early implementations didn’t even give struct members their own
name-space; i.e.,

struct foo {int bar; int bas;};
struct quux {int bas; int bar;};

would produce a conflict.
C’s fixed that long since, but it’s still an issue for enums.

Struct members are not often used out of context; there is always a parent
struct type around taking care of it:

p.A
q->B

A,B *must* correspond to the members of struct types p and *q. So they are
straightforward to deal with.

Enum names are different; they can be used independently, or mixed with
ordinary int types, as well as their parent enum types, or even other enum
types, but there is no special operator like "." or "->" to link the enum
name to it's specific parent type:

enum colours {red,green,blue};
enum lights {green,amber,red};

if (red<green) // true or false?
In C++
this limitation got so in the way that the name-space resolution
operator :: needed to be invented, but C can get by without it.

Namespaces are useful and very easy to deal with. C partly gets by without
it by building the namespace qualifier into each name, using the "_"
operator...
 
A

Anders Wegge Keller

Joel C. Salomon said:
C doesn’t lend itself to this sort of distinction very well. IIRC,
some *very* early implementations didn’t even give struct members
their own name-space; i.e.,

struct foo {int bar; int bas;};
struct quux {int bas; int bar;};

I have a war story to tell about the SCO ODS compiler, that is
related to that issue:

struct foo {int baz; int qix; int qqq;}
struct bar {int baz; int qix; int zzz;}

struct bar* bptr;
...
bptr->qqq = 42;

Not only did the code compile without a warning, but it also managed
to function (by sheer luck) in production for at least 6 years.
 
B

Ben Bacarisse

Joel C. Salomon said:
C doesn’t lend itself to this sort of distinction very well. IIRC, some
*very* early implementations didn’t even give struct members their own
name-space; i.e.,

struct foo {int bar; int bas;};
struct quux {int bas; int bar;};

would produce a conflict.

Yes, but as I recall the same name could occur in two structs provided
both were of the same type and at the same offset. Also, you could use
the members from one struct to access another! In effect, all structs
were in a giant anonymous union. This was useful because there were no
union types in this early C.

<snip>
 
B

Ben Bacarisse

Anders Wegge Keller said:
I have a war story to tell about the SCO ODS compiler, that is
related to that issue:

struct foo {int baz; int qix; int qqq;}
struct bar {int baz; int qix; int zzz;}

struct bar* bptr;
...
bptr->qqq = 42;

Not only did the code compile without a warning, but it also managed
to function (by sheer luck) in production for at least 6 years.

It is probably not luck. This was how C was, and it was designed to be
like this. Early C had no unions and this sort of trick was not
uncommon. OK, what you show is not a "trick" but using members from one
struct to access an object of another struct type was not uncommon.
 
M

Malcolm McLean

If that is the case, how would you define s++?
We've got a choice.
If we restrict an enum state to holding state type, then s++ would
iterate through the states. WYOMING + 1 would also be allowed as a
special case, just as you can move a pointer one past the end of its
array. If TEXAS left the union, so we change the program and create a
hole, s= TENNESSEE, s++ would set s to UTAH, skipping over the deleted
value for TEXAS.
However that's not a very C-like rule. It's probably better to allow
state to hold illegal values. This means that TENNESSEE++ gives us a
non-state.
The following code
for(s = ALABAMA; s <= WYOMING; s++) tax(s);
will now trigger a war.
Then you have to define the comparison operators for state.
That's easy enough. An enum is usually used for an ordered list.
 
A

Anders Wegge Keller

It is probably not luck. This was how C was, and it was designed to
be like this.

Given the diagnostics the compiler otherwise gives, this is not a
behaviour by design. And that it worked was genuine luck, since the
offset of the missing member ended up being 0, thus overwriting
another struct member.
Early C had no unions and this sort of trick was not uncommon. OK,
what you show is not a "trick" but using members from one struct to
access an object of another struct type was not uncommon.

I'd rather call it a compiler bug, where a symbol definition somehow
crept from one hash to another.
 
B

Ben Bacarisse

pete said:
I have "C Reference Manual ­ 2"

If we are looking at the same document, that is the page number!
by Dennis M. Ritchie
and I see that "union" was not a keyword by the time that
that was written, but I don't know when that was.

I have seen it cited as:

D. M. Ritchie, C Reference Manual.  Bell Labs, Jan. 1974.

but the document it self has no date on it.
 
D

David Thompson

It is in most of the contexts I've seen it. In fact, the only C code
I've ever personally seen that used C++ keywords was written to use
those keywords because the author deliberately wanted to make it
difficult to port to C++. He felt he was doing future maintenance
programmers a service, by discouraging them from going over to the "dark
side".
I have for-real used 'old' and 'new' in routines/snippets dealing with
a change. Also I have often used 'inline' for an input line buffer or
sometimes pointer; that was a problem for C++ and also C99. I think I
had a handful of 'restrict' which was also a problem for C99.

I don't think I used 'class' or 'friend'; I occasionally used
'template' 'delete' 'public' 'private' but I think not 'protected'.
I used 'try' fairly often, and 'catch' 'throw' sometimes with a
purpose similar to C++ but (necessarily) syntactically different.
I used 'bool' a fair bit but only with the same intent as C++ and C99,
and where I used 'wchar_t' or 'bitand' etc. it was with same meaning.

I'm pretty sure I never used 'explicit' 'namespace' 'operator' or any
of the *_cast's. I don't think I used 'virtual' but I can imagine
reasonable applications that would have.

Of course all of these were or could have been fixed easily and with
negligible impact, consistent with your main argument.
 

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

No members online now.

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,114
Latest member
GlucoPremiumReview
Top