Can Code That Is Valid in Both C and C++ Produce Different Behavior When Compiled in Each Language

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

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

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

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

Do external variables always need to be volatile when compiled with gcc?

The calculation &ex_var+16 is not defined by the C standard (because it only defines pointer arithmetic within an object, including to the address just beyond its end) and the assignment *pos++ = 1 is not defined by the C standard (because, for the purposes of the standard, pos does not point to an object). When there is behavior not defined by the C standard on a code path, the standard does not define any behavior on the code path.

You can make the behavior defined, to the extent the compiler can see, by declaring ex_var as an array of unknown size, so that the address calculation and the assignments would be defined if this translation unit were linked with another that defined ex_var to be an array of sufficient size:

extern unsigned int ex_var[];

int main ()
{
ex_var[0] = 56326;

unsigned int *pos = ex_var+16;

for (int i = 0; i < 6; i++ )
{
*pos++ = 1;
}
*(volatile unsigned int*)(0x00100000) = ex_var[0];
}

(Note that *(volatile unsigned int*)(0x00100000) = remains not defined by the C standard, but GCC is intended for some use in bare-metal environments and appears to work with this. Additional compilation switches might be necessary to ensure it is defined for GCC’s purposes.)

This yields assembly that sets ex_var[0] and uses it in the assignment to 0x00100000:

main:
mov DWORD PTR ex_var[rip], 56326

mov eax, DWORD PTR ex_var[rip]
mov DWORD PTR ds:1048576, eax
mov eax, 0
ret

Application behaves differently on different machines

The textbook is wrong if it that’s exactly what it says: %d is a conversion specifier for an int, but move is a long int. The correct call would be:

scanf("%ld", &move)

, with similar corrections to several printf calls.

It can work by coincidence, especially when long and int happen to be the same size (as they are in 64-bit Windows, but not in 64-bit Linux). With the mismatches, however, no particular behaviour is defined for the entire program: the compiler is allowed by the language standard to assume that the kind of illegal actions they represent never happen, and has no obligation whatever as to what a program that performs such an action may do.



Related Topics



Leave a reply



Submit