how - Can code that is valid in both C and C++ produce different behavior when compiled in each language?




return main (12)

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.

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.


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);
...

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.


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)); 
}

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

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;
}

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 allows 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.


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.


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;
}

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++.

#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.


#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.





c