714

C and C++ have many differences, and not all valid C code is valid C++ code.
(By "valid" I mean standard code with defined behavior, i.e. not implementation-specific/undefined/etc.)

Is there any scenario in which a piece of code valid in both C and C++ would produce different behavior when compiled with a standard compiler in each language?

To make it a reasonable/useful comparison (I'm trying to learn something practically useful, not to try to find obvious loopholes in the question), let's assume:

  • Nothing preprocessor-related (which means no hacks with #ifdef __cplusplus, pragmas, etc.)
  • Anything implementation-defined is the same in both languages (e.g. numeric limits, etc.)
  • We're comparing reasonably recent versions of each standard (e.g. say, C++98 and C90 or later)
    If the versions matter, then please mention which versions of each produce different behavior.
Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
user541686
  • 205,094
  • 128
  • 528
  • 886
  • It would help if you provided the compiler versions and compiler command line parameters? – Marc Feb 23 '09 at 23:52
  • other way around: in C, structs are not scopes, so struct A inside struct B is named by "struct A" so C will answer 512 and C++ will say 8. – Johannes Schaub - litb Feb 23 '09 at 23:53
  • 17
    By the way, it can be useful to program in a dialect which is C and C++ at the same time. I've done this in the past and one one current project: the TXR language. Interestingly, the developers of the Lua language did the same thing, and they call this dialect "Clean C". You get the benefit of better compile time checking and possibly additional useful diagnostics from C++ compilers, yet retain the C portability. – Kaz Oct 15 '12 at 07:59
  • `int main() { printf("Hi!\n"); /* no return here */ }` is different in C90 (not in C99) and C++ (try `echo $?`). But this is not valid and UB in C90 case. – JJJ Oct 15 '12 at 10:37
  • 9
    I merged the older question into this question since this has more views and upvoted answers. This is still an example of a non-constructive question, but it's quite borderline since yes, it does teach SO users something. I'm closing it as not constructive only to reflect the state of the question before the merge. Feel free to disagree and re-open. – George Stocker Oct 16 '12 at 00:37
  • 19
    Voting to reopen as I think it can be objectively answered with a "yes" followed by an example (as proved below). I think it is constructive in that people can learn relevant behaviour from it. – Anders Abel Oct 16 '12 at 15:39
  • 6
    @AndersAbel The pure number of answers, all of which are correct, demonstrates unambiguously that it remains a make-a-list question. There was no way you could have asked this question without getting a list. – dmckee --- ex-moderator kitten Oct 16 '12 at 22:19
  • @GeorgeStocker *"I merged the older question into this question since this has more views and upvoted answers."* That simply encourages people to *not search* before asking. If it's a duplicate you just say *"Oh! I didn't know."* and because the site is a hundred times bigger now the new one wins. – dmckee --- ex-moderator kitten Oct 16 '12 at 22:21
  • 2
    @dmckee For what it's worth, I agree with you. However, the C++ tag people are... Shall we say... [feisty](http://meta.stackexchange.com/questions/144359/why-is-the-definitive-c-book-list-question-allowed-but-other-book-recommend/144363#144363). – George Stocker Oct 17 '12 at 01:19
  • http://stackoverflow.com/questions/2172943/size-of-character-a-in-c-c – P.P Feb 17 '13 at 02:18
  • http://stackoverflow.com/questions/2038200/write-a-program-that-will-print-c-if-compiled-as-an-ansi-c-program-and-c/ – jamesdlin Aug 31 '16 at 05:39
  • Besides `auto`, it seems there should be a way to exploit some of the C++ keywords that aren't keywords in C (or vice versa): `new`, `delete`, `operator`, `using`, etc. – Adrian McCarthy Apr 19 '18 at 23:36
  • @AdrianMcCarthy: I'm not sure that's possible... for that to "work" you'd need the exact same expression using those words to be interpret them as both keywords and as identifiers, at least unless you're using a trick already in one of the answers (in which case I'd argue it's a duplicate trick). This is mostly a syntactic thing rather than a semantic thing... and it doesn't seem possible given that C syntax is almost entirely a subset of C++ syntax (even more so than the semantics). But I'm not sure, I can't quite entirely rule it out... – user541686 Jul 27 '18 at 19:31

19 Answers19

488

Here is an example that takes advantage of the difference between function calls and object declarations in C and C++, as well as the fact that C90 allows the calling of undeclared functions:

#include <stdio.h>

struct f { int x; };

int main() {
    f();
}

int f() {
    return printf("hello");
}

In C++ this will print nothing because a temporary f is created and destroyed, but in C90 it will print hello because functions can be called without having been declared.

In case you were wondering about the name f being used twice, the C and C++ standards explicitly allow this, and to make an object you have to say struct f to disambiguate if you want the structure, or leave off struct if you want the function.

Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • wont `int f()` get inlined by the compiler ? – krichard Oct 15 '12 at 10:44
  • 8
    Strictly speaking under C this will not compile, because the declaration of "int f()" is after the definition of "int main()" :) – Sogartar Oct 15 '12 at 11:11
  • 16
    @Sogartar, really? http://codepad.org/STSQlUhh C99 compilers will give you a warning, but they'll still let you compile it. – jonvuri Oct 15 '12 at 11:29
  • 24
    @Sogartar in C functions are allowed to be implicitly declared. – Alex B Oct 15 '12 at 11:48
  • 1
    @muntoo No, the most vexing parse is where the programmer tries to declare a variable of type `vector` and writes `vector v();`, but the compiler treats it as the declaration of a function taking no parameters returning a `vector`. There isn't a name for the one I wrote AFAIK, just the combo of C requiring `struct` and C++ not, and C allowing function calls without previous declarations, and C++ not. – Seth Carnegie Jan 03 '13 at 18:13
  • 12
    @AlexB Not in C99 and C11. –  Jan 19 '14 at 22:25
  • 6
    @jrajav Those are not C99 compilers, then. A C99 compiler detects undeclared identifiers as a syntax error. A compiler that doesn't do that is either a C89 compiler, or a pre-standard or another kind of non-conformant compiler. –  Jan 19 '14 at 22:26
  • 2
    @H2CO3 OP mentioned that C90 is an acceptable baseline, so it's still valid. Besides, C89 is *still* widespread. – Alex B Jan 20 '14 at 11:13
  • As an aside, C does not allow `struct`s without any member, that's an extension. – Deduplicator Jun 20 '15 at 12:39
  • Fun fact: to get gcc to actually reject that code as C, you need `-std=c99 -pedantic-errors`. Otherwise it compiles with just a warning about the implicit declaration, even with `-std=c11 -pedantic`. See it on [the Godbolt compiler explorer](https://godbolt.org/g/rf4MFn). The [compiler docs](https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html) just talk about the ISO standard "requiring a diagnostic". – Peter Cordes Dec 23 '16 at 02:57
  • 10
    @user529758 - They may well be C99 compilers. A C99 which detects undeclared identifiers is required to treat it as a syntax error, and is required to "emit a diagnostic"; it is *not* required to fail to compile the file. – Martin Bonner supports Monica Aug 09 '18 at 14:26
467

For C++ vs. C90, there's at least one way to get different behavior that's not implementation defined. C90 doesn't have single-line comments. With a little care, we can use that to create an expression with entirely different results in C90 and in C++.

int a = 10 //* comment */ 2 
        + 3;

In C++, everything from the // to the end of the line is a comment, so this works out as:

int a = 10 + 3;

Since C90 doesn't have single-line comments, only the /* comment */ is a comment. The first / and the 2 are both parts of the initialization, so it comes out to:

int a = 10 / 2 + 3;

So, a correct C++ compiler will give 13, but a strictly correct C90 compiler 8. Of course, I just picked arbitrary numbers here -- you can use other numbers as you see fit.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 37
    WHOA this is mind-blowing!! Of all possible things I would never have thought comments could be used to change behavior haha. +1 – user541686 Oct 15 '12 at 06:06
  • 90
    even without the `2`, it would read as `10 / + 3` which is valid (unary +). – Benoit Oct 15 '12 at 07:44
  • 12
    Now for fun, modify it so that C and C++ both calculate different arithmetic expressions the evaluate to the same result. – Ryan C. Thompson Oct 15 '12 at 09:59
  • 22
    @RyanThompson Trivial. s/2/1/ – sehe Oct 15 '12 at 11:53
  • 5
    @Mehrdad Am I wrong or comments are preprocessor-related? They should thus be excluded as a possible answer from your question! ;-) – Ale Oct 07 '14 at 23:08
  • @Ale Correct, but maybe too much of a nit-pick. I think the "not cpp" rule was meant more as "no cpp directives". – OJFord Apr 08 '15 at 12:02
  • 1
    Related: [How does the below program output `C89` when compiled in C89 mode and `C99` when compiled in C99 mode?](https://stackoverflow.com/q/31115453/3049655) – Spikatrix May 28 '17 at 14:23
418

The following, valid in C and C++, is going to (most likely) result in different values in i in C and C++:

int i = sizeof('a');

See Size of character ('a') in C/C++ for an explanation of the difference.

Another one from this article:

#include <stdio.h>

int  sz = 80;

int main(void)
{
    struct sz { char c; };

    int val = sizeof(sz);      // sizeof(int) in C,
                               // sizeof(struct sz) in C++
    printf("%d\n", val);
    return 0;
}
Donald Duck
  • 8,409
  • 22
  • 75
  • 99
Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180
  • 11
    Definitely wasn't expecting this one! I was hoping for something a little more dramatic but this is still useful, thanks. :) +1 – user541686 Oct 14 '12 at 23:58
  • You might be interested in reading [this article](http://david.tribble.com/text/cdiffs.htm). There should be more. – Alexey Frunze Oct 14 '12 at 23:59
  • @SethCarnegie Though rarely, it definitely can (added "most likely" to the answer). – Alexey Frunze Oct 15 '12 at 00:02
  • @SethCarnegie Given the likelihood of `sizeof(int)==1`, I wouldn't be so sure. – Alexey Frunze Oct 15 '12 at 00:04
  • @SethCarnegie: You have an excellent point, but in this one I'll make an exception here since this seems actually useful in practice. I put that restriction there to avoid getting useless language-lawyer answers; this one certainly doesn't fall in that category and it seems useful so I still think it's a good answer. – user541686 Oct 15 '12 at 00:06
  • @Mehrdad oh, I didn't realise it was you that asked this question. Had I known that when I read your initial comment supporting the answer I wouldn't have said anything. Sorry. – Seth Carnegie Oct 15 '12 at 00:06
  • 19
    +1 the second example is a good one for the fact that C++ doesn't require `struct` before struct names. – Seth Carnegie Oct 15 '12 at 00:31
  • 1
    @Andrey I thought the same a while ago and tested it and it worked on GCC 4.7.1 without the std, contrary to my expectation. Is that a bug in GCC? – Seth Carnegie Oct 16 '12 at 19:35
  • 3
    @SethCarnegie: A non-conforming program need not fail to work, but it is not guaranteed to work either. – Andrey Vihrov Oct 16 '12 at 21:35
  • 1
    @AndreyVihrov yes, I know. I'm asking if it's the program that isn't conforming, or the compiler. – Seth Carnegie Oct 16 '12 at 21:42
  • 7
    `struct sz { int i[2];};` would mean that C and C++ *have* to produce different values. (Whereas a DSP with sizeof(int) == 1, *could* produce the same value). – Martin Bonner supports Monica Jan 09 '17 at 13:52
  • Great answer. Wikipedia has a section on exactly this question: https://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B#Constructs_that_behave_differently_in_C_and_C++ It mentions the differences you've pointed out, among others, but unfortunately doesn't give many further examples – Max Barraclough May 05 '18 at 17:27
194

C90 vs. C++11 (int vs. double):

#include <stdio.h>

int main()
{
  auto j = 1.5;
  printf("%d", (int)sizeof(j));
  return 0;
}

In C auto means local variable. In C90 it's ok to omit variable or function type. It defaults to int. In C++11 auto means something completely different, it tells the compiler to infer the type of the variable from the value used to initialize it.

vonbrand
  • 11,412
  • 8
  • 32
  • 52
detunized
  • 15,059
  • 3
  • 48
  • 64
  • 22
    @SethCarnegie: Yeah, it's a storage class; it's what happens by default when you omit it, so no one used it, and they changed its meaning. I think it's `int` by default. This is clever! +1 – user541686 Oct 15 '12 at 01:48
  • 3
    One might argue that that's not a valid comparison; you're comparing an obsolete version of C to the current version of C++. – Keith Thompson Oct 15 '12 at 02:27
  • 4
    @KeithThompson Eh? C11 still has auto, which has been in C since its conception. – Jim Balter Oct 15 '12 at 02:37
  • 7
    C11 does not have implicit-`int`. – R.. GitHub STOP HELPING ICE Oct 15 '12 at 02:42
  • 24
    @KeithThompson Ah, I guess you mean the inferred `int`. Still, in the real world, where there is tons of legacy code and the market leader still hasn't implemented C99 and has no intent to do so, talk of "an obsolete version of C" is absurd. – Jim Balter Oct 15 '12 at 02:42
  • 12
    "Every variable MUST have an explicit storage class. Yours truly, upper management." – btown Oct 15 '12 at 15:28
131

Another example that I haven't seen mentioned yet, this one highlighting a preprocessor difference:

#include <stdio.h>
int main()
{
#if true
    printf("true!\n");
#else
    printf("false!\n");
#endif
    return 0;
}

This prints "false" in C and "true" in C++ - In C, any undefined macro evaluates to 0. In C++, there's 1 exception: "true" evaluates to 1.

godlygeek
  • 1,489
  • 1
  • 8
  • 5
114

Per C++11 standard:

a. The comma operator performs lvalue-to-rvalue conversion in C but not C++:

   char arr[100];
   int s = sizeof(0, arr);       // The comma operator is used.

In C++ the value of this expression will be 100 and in C this will be sizeof(char*).

b. In C++ the type of enumerator is its enum. In C the type of enumerator is int.

   enum E { a, b, c };
   sizeof(a) == sizeof(int);     // In C
   sizeof(a) == sizeof(E);       // In C++

This means that sizeof(int) may not be equal to sizeof(E).

c. In C++ a function declared with empty params list takes no arguments. In C empty params list mean that the number and type of function params is unknown.

   int f();           // int f(void) in C++
                      // int f(*unknown*) in C
Kirill Kobelev
  • 10,252
  • 6
  • 30
  • 51
  • The first one is also implementation-defined like Alexey's. But +1. – Seth Carnegie Oct 15 '12 at 01:00
  • 1
    @Seth, All material above is taken directly from Annex C.1 of the C++11 standard. – Kirill Kobelev Oct 15 '12 at 01:13
  • Yes but it is still implementation-defined. `sizeof(char*)` could be 100 in which case the first example would produce the same observable behaviour in C and C++ (i.e. though the method of obtaining `s` would be different, `s` would end up being 100). The OP mentioned that this type of implementation-defined behaviour was fine as he was just wanting to avoid language-lawyer answers, so the first one is fine by his exception. But the second one is good in any case. – Seth Carnegie Oct 15 '12 at 01:31
  • 5
    There is an easy fix -- just change the example to: `char arr[sizeof(char*)+1]; int s = sizeof(0, arr);` – Mankarse Oct 15 '12 at 11:49
  • 5
    To avoid implementation-defined differences, you could also use `void *arr[100]`. In this case an element is the same size as a pointer to the same element, so as long as there are 2 or more elements, the array must be larger than the address of its first element. – finnw Oct 15 '12 at 12:48
  • @SethCarnegie or just do the same thing twice for different array lengths, since sizeof(char*) can't be the same as both of them. – Owen Oct 18 '12 at 00:17
  • Ad a): In C it's just called "lvalue conversion". Values in C are either lvalues or "not an lvalue". – Kerrek SB Jan 19 '14 at 23:42
  • I think in order to make `sizeof(a) == sizeof(int);` wrong in C++ you can assign a value larger than INT_MAX (or perhaps UINT_MAX), so that the underlying type must become the next larger one. -- Splendid examples. – Peter - Reinstate Monica Mar 13 '20 at 01:01
61

This program prints 1 in C++ and 0 in C:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int d = (int)(abs(0.6) + 0.5);
    printf("%d", d);
    return 0;
}

This happens because there is double abs(double) overload in C++, so abs(0.6) returns 0.6 while in C it returns 0 because of implicit double-to-int conversion before invoking int abs(int). In C, you have to use fabs to work with double.

Pavel Chikulaev
  • 841
  • 6
  • 12
  • 6
    had to debug code of someone else with that issue. Oh How I loved that. Anyway your program is printing 0 in C++ too. C++ have to use the header "cmath" see comparison first one returnin 0 http://ideone.com/0tQB2G 2nd one returning 1 http://ideone.com/SLeANo – CoffeDeveloper Nov 30 '14 at 11:00
  • Glad/sorry to hear that I'm not the only who find this difference via debugging. Just tested in VS2013, an empty with only file with this content will output 1 if extension is .cpp, and 0 if extension is .c. Looks like is included indirectly in VS. – Pavel Chikulaev Dec 02 '14 at 03:05
  • And looks like in VS C++, includes C++ stuff into global namespace, where as for GCC it is not. Unsure which is standard behavior however. – Pavel Chikulaev Dec 02 '14 at 03:21
  • no it is not, but lot of users would not be able to compile code written for visual studio if they don't add missing includes;) and even worse, there's the chance users won't get compile errors but strange numerical behaviour due to that non standard behaviour (which could be disabled from options but is enabled by default) – CoffeDeveloper Dec 02 '14 at 13:43
  • 4
    This particular code sample is implementation-dependent: `stdlib.h` only defines `abs(int)` and `abs(long)`; the version `abs(double)` is declared by `math.h`. So this program may still call the `abs(int)` version. It's an implementation detail whether `stdlib.h` also causes `math.h` to be included. (I think it would be a bug if `abs(double)` were called, but other aspecs of `math.h` were not included). – M.M May 09 '16 at 04:24
  • 1
    A secondary issue is that although the C++ standard does appear to say that including `` also includes the additional overloads; in practice it turns out that all the major compilers don't include those overloads unless the form `` is used. – M.M May 09 '16 at 04:25
  • And although most libraries would define the overloads in both the `std::` and the global namespaces, it's possible that the overloads would be declared only in `std::`, so this is really implementation-defined. (I still upvoted, because it's a difference that happens in real code.) – Adrian McCarthy Apr 19 '18 at 23:05
42

Another sizeof trap: boolean expressions.

#include <stdio.h>
int main() {
    printf("%d\n", (int)sizeof !0);
}

It equals to sizeof(int) in C, because the expression is of type int, but is typically 1 in C++ (though it's not required to be). In practice they are almost always different.

Alex B
  • 82,554
  • 44
  • 203
  • 280
  • 6
    One `!` should be enough for a `bool`. – Alexey Frunze Oct 15 '12 at 14:15
  • 5
    !! is the int to boolean conversion operator :) – EvilTeach Apr 19 '17 at 16:41
  • 1
    `sizeof(0)` is `4` in both C and C++ because `0` is an integer rvalue. `sizeof(!0)` is `4` in C and `1` in C++. Logical NOT operates on operands of type bool. If the int value is `0` it is implicitly converted to `false` (a bool rvalue), then it is flipped, resulting in `true`. Both `true` and `false` are bool rvalues in C++ and the `sizeof(bool)` is `1`. However in C `!0` evaluates to `1`, which is an rvalue of type int. C programming language has no bool data type by default. – Galaxy Jun 21 '18 at 22:24
42
#include <stdio.h>

int main(void)
{
    printf("%d\n", (int)sizeof('a'));
    return 0;
}

In C, this prints whatever the value of sizeof(int) is on the current system, which is typically 4 in most systems commonly in use today.

In C++, this must print 1.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
28

An old chestnut that depends on the C compiler, not recognizing C++ end-of-line comments...

...
int a = 4 //* */ 2
        +2;
printf("%i\n",a);
...
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
dmckee --- ex-moderator kitten
  • 98,632
  • 24
  • 142
  • 234
28

The C++ Programming Language (3rd Edition) gives three examples:

  1. sizeof('a'), as @Adam Rosenfield mentioned;

  2. // comments being used to create hidden code:

    int f(int a, int b)
    {
        return a //* blah */ b
            ;
    }
    
  3. Structures etc. hiding stuff in out scopes, as in your example.

Donald Duck
  • 8,409
  • 22
  • 75
  • 99
derobert
  • 49,731
  • 15
  • 94
  • 124
25

Another one listed by the C++ Standard:

#include <stdio.h>

int x[1];
int main(void) {
    struct x { int a[2]; };
    /* size of the array in C */
    /* size of the struct in C++ */
    printf("%d\n", (int)sizeof(x)); 
}
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
22

Inline functions in C default to external scope where as those in C++ do not.

Compiling the following two files together would print the "I am inline" in case of GNU C but nothing for C++.

File 1

#include <stdio.h>

struct fun{};

int main()
{
    fun();  // In C, this calls the inline function from file 2 where as in C++
            // this would create a variable of struct fun
    return 0;
}

File 2

#include <stdio.h>
inline void fun(void)
{
    printf("I am inline\n");
} 

Also, C++ implicitly treats any const global as static unless it is explicitly declared extern, unlike C in which extern is the default.

M.M
  • 138,810
  • 21
  • 208
  • 365
fkl
  • 5,412
  • 4
  • 28
  • 68
  • I really don't think so. Probably you have missed the point. It's not about definition of struct st which is merely used to make the code valid c++. The point is that it highlights different behavior of inline functions in c vs c++. Same applies to extern. None of these is discussed in any of the solutions. – fkl Oct 15 '12 at 16:01
  • 2
    What is the different behavior of inline functions and `extern` that is demonstrated here? – Seth Carnegie Oct 15 '12 at 16:10
  • It is written pretty clearly. "Inline functions in c default to external scope where as those in c++ are not (code shows that). Also C++ implicitly treats any const global as file scope unless it is explicitly declared extern, unlike C in which extern is the default. A similar example can be created for that". I am puzzled - Is it not understandable? – fkl Oct 15 '12 at 16:12
  • 13
    @fayyazkl The behaviour shown is only because of the difference of lookup (`struct fun` vs `fn`) and has nothing to do whether the function is inline. The result is identical if you remove `inline` qualifier. – Alex B Oct 15 '12 at 23:36
  • 3
    In ISO C this program is ill formed: `inline` was not added until C99, but in C99 `fun()` may not be called without a prototype in scope. So I assume this answer only applies to GNU C. – M.M May 09 '16 at 04:28
  • The inline/(not) static difference will bite you in C with a duplicate symbol link time error if you have inline (but not static) functions in a header included by more than source file. – Peter - Reinstate Monica Mar 13 '20 at 01:08
17
#include <stdio.h>

struct A {
    double a[32];
};

int main() {
    struct B {
        struct A {
            short a, b;
        } a;
    };
    printf("%d\n", sizeof(struct A));
    return 0;
}

This program prints 128 (32 * sizeof(double)) when compiled using a C++ compiler and 4 when compiled using a C compiler.

This is because C does not have the notion of scope resolution. In C structures contained in other structures get put into the scope of the outer structure.

wefwefa3
  • 3,872
  • 2
  • 29
  • 51
16
struct abort
{
    int x;
};

int main()
{
    abort();
    return 0;
}

Returns with exit code of 0 in C++, or 3 in C.

This trick could probably be used to do something more interesting, but I couldn't think of a good way of creating a constructor that would be palatable to C. I tried making a similarly boring example with the copy constructor, that would let an argument be passed, albeit in a rather non-portable fashion:

struct exit
{
    int x;
};

int main()
{
    struct exit code;
    code.x=1;

    exit(code);

    return 0;
}

VC++ 2005 refused to compile that in C++ mode, though, complaining about how "exit code" was redefined. (I think this is a compiler bug, unless I've suddenly forgotten how to program.) It exited with a process exit code of 1 when compiled as C though.

  • Your second example using exit, doesn't compile on gcc or g++, unfortunately. It's a good idea, though. – Sean Feb 24 '09 at 02:55
  • 1
    `exit(code)` is a valid declaration of a variable `code` of type `exit`, apparently. (See "most vexing parse", which is a different but similar issue). – user253751 Mar 25 '15 at 08:57
8

Don't forget the distinction between the C and C++ global namespaces. Suppose you have a foo.cpp

#include <cstdio>

void foo(int r)
{
  printf("I am C++\n");
}

and a foo2.c

#include <stdio.h>

void foo(int r)
{
  printf("I am C\n");
}

Now suppose you have a main.c and main.cpp which both look like this:

extern void foo(int);

int main(void)
{
  foo(1);
  return 0;
}

When compiled as C++, it will use the symbol in the C++ global namespace; in C it will use the C one:

$ diff main.cpp main.c
$ gcc -o test main.cpp foo.cpp foo2.c
$ ./test 
I am C++
$ gcc -o test main.c foo.cpp foo2.c
$ ./test 
I am C
user23614
  • 566
  • 4
  • 4
5
int main(void) {
    const int dim = 5; 
    int array[dim];
}

This is rather peculiar in that it is valid in C++ and in C99, C11, and C17 (though optional in C11, C17); but not valid in C89.

In C99+ it creates a variable-length array, which has its own peculiarities over normal arrays, as it has a runtime type instead of compile-time type, and sizeof array is not an integer constant expression in C. In C++ the type is wholly static.


If you try to add an initializer here:

int main(void) {
    const int dim = 5; 
    int array[dim] = {0};
}

is valid C++ but not C, because variable-length arrays cannot have an initializer.

-1

Empty structures have size 0 in C and 1 in C++:

#include <stdio.h>

typedef struct {} Foo;

int main()
{
    printf("%zd\n", sizeof(Foo));
    return 0;
}
Daniel Frużyński
  • 2,091
  • 19
  • 28
-2

This concerns lvalues and rvalues in C and C++.

In the C programming language, both the pre-increment and the post-increment operators return rvalues, not lvalues. This means that they cannot be on the left side of the = assignment operator. Both these statements will give a compiler error in C:

int a = 5;
a++ = 2;  /* error: lvalue required as left operand of assignment */
++a = 2;  /* error: lvalue required as left operand of assignment */

In C++ however, the pre-increment operator returns an lvalue, while the post-increment operator returns an rvalue. It means that an expression with the pre-increment operator can be placed on the left side of the = assignment operator!

int a = 5;
a++ = 2;  // error: lvalue required as left operand of assignment
++a = 2;  // No error: a gets assigned to 2!

Now why is this so? The post-increment increments the variable, and it returns the variable as it was before the increment happened. This is actually just an rvalue. The former value of the variable a is copied into a register as a temporary, and then a is incremented. But the former value of a is returned by the expression, it is an rvalue. It no longer represents the current content of the variable.

The pre-increment first increments the variable, and then it returns the variable as it became after the increment happened. In this case, we do not need to store the old value of the variable into a temporary register. We just retrieve the new value of the variable after it has been incremented. So the pre-increment returns an lvalue, it returns the variable a itself. We can use assign this lvalue to something else, it is like the following statement. This is an implicit conversion of lvalue into rvalue.

int x = a;
int x = ++a;

Since the pre-increment returns an lvalue, we can also assign something to it. The following two statements are identical. In the second assignment, first a is incremented, then its new value is overwritten with 2.

int a;
a = 2;
++a = 2;  // Valid in C++.
Galaxy
  • 2,363
  • 2
  • 25
  • 59