在 c 和 c + + 中都有效的代码在用每种语言编译时会产生不同的行为吗?

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.

转载于:https://stackoverflow.com/questions/12887700/can-code-that-is-valid-in-both-c-and-c-produce-different-behavior-when-compile

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

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

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.

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.

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

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.

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

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.

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 correct C compiler 8. Of course, I just picked arbitrary numbers here -- you can use other numbers as you see fit.

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.

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.

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.

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