Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

technical legality of incompatible pointer assignments

The C11 standard ISO/IEC 9899:2011 (E) states the following constraints for simple assignments in §6.5.16.1/1:

One of the following shall hold:

  • the left operand has atomic, qualified, or unqualified arithmetic type, and the right has arithmetic type;
  • the left operand has an atomic, qualified, or unqualified version of a structure or union type compatible with the type of the right;
  • the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
  • the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
  • the left operand is an atomic, qualified, or unqualified pointer, and the right is a null pointer constant; or
  • the left operand has type atomic, qualified, or unqualified _Bool, and the right is a pointer.

I am interested in the case in which both sides are pointers to incompatible types different from void. If I understand correctly, this should at the very least invoke UB, as it violates this constraint. One example for incompatible types should be (according to §6.2.7 and §6.7.2) int and double.

Therefore the following program should be in violation:

int main(void) {
  int a = 17;
  double* p;
  p = &a;
  (void)p;
}

Both gcc and clang warn about "-Wincompatible-pointer-types", but do not abort compilation (compilation with -std=c11 -Wall -Wextra -pedantic).

Similarly, the following program only leads to a "-Wint-conversion" warning, while compiling just fine.

int main(void) {
  int a;
  double* p;
  p = a;
  (void)p;
}

Coming from C++, I expected that either of those test cases would require a cast to compile. Is there any reason why either of the programs would be standards-legal? Or, are there at least significant historic reasons for supporting this code style even when disabling the entertaining GNU C extensions by explicitly using -std=c11 instead of -std=gnu11?

like image 450
gha.st Avatar asked Apr 24 '16 20:04

gha.st


People also ask

Can a pointer to a void pointer be assigned to another pointer?

In ISO C, a pointer to void can be assigned to a pointer of any other type. You do not need to cast the pointer explicitly. C++ allows void pointers to be assigned only to other void pointers.

What happens when a pointer is not aligned correctly?

On more strict processors, if you try to access an object such as an int and the pointer isn't aligned correctly, your program will actually crash on the spot. In Linux or UNIX, this will typically get reported as a bus error. Most of the time, the compiler takes care of all of this for you.

Can you use a constant pointer with a volatile pointer?

You cannot use a constant pointer where a volatile pointer is expected unless you cast a void pointer to the appropriate pointer type before compiling the code. Note: You can use the new and delete operators instead of malloc () and free ().

Why can't you do pointer arithmetic on a pointer in C?

You can’t do pointer arithmetic on it, but internally, that’s what it is. Even an array is just pointer arithmetic in disguise. In C it is a flimsy disguise; in other languages, it is much more elaborate and convincing. But under the hood, it always involves adding the index to a pointer. Pointers are just what computers do.


3 Answers

Is there any reason why either of the programs would be standards-legal?

These programs are not "standards-legal". They contain constraint violations and you already quoted the right text from the standard.

The compilers conform to the standard by producing a diagnostic for constraint violation. The standard does not require compilation to abort in the case of a constraint violation or other erroneous program.

It doesn't say in as many words, but the only reasonable conclusion is that any executable generated as a result of a program containing a constraint violation has completely undefined behaviour. (I have seen people try to argue otherwise though).

Speculation follows: C (and C++) are used for many purposes; sometimes people want "high level assembler" for their machine and don't care about portability or standards. Presumably the compiler vendors set the defaults to what they think their target audience would prefer.

like image 171
M.M Avatar answered Oct 18 '22 02:10

M.M


The compiler flag (both gcc and clang) to request checks for strict standards conformance and to refuse to compile nonconformant code is -pedantic-errors:

$ gcc -std=c11 -pedantic-errors x.c
x.c: In function ‘main’:
x.c:3:15: error: initialization from incompatible pointer type [-Wincompatible-pointer-types]
   double* p = &a;
               ^

Clang:

$ clang -std=c11 -pedantic-errors x.c
x.c:3:11: error: incompatible pointer types initializing 'double *' with an
      expression of type 'int *' [-Werror,-Wincompatible-pointer-types]
  double* p = &a;
          ^   ~~
1 error generated.

A significant proportion (to say the least) of typical C code in the wild is nonconformant, so -pedantic-errors would cause most C programs and libraries to fail to compile.

like image 38
ecatmur Avatar answered Oct 18 '22 02:10

ecatmur


Your code example and your citation of the standard does not match. The example is initialization and 6.5.16 talks about assignment.

Confusingly the matching-type requirement is in a constraint section 6.5.16 for assignment, but "only" in the semantics section (6.7.9) for for initialization. So the compilers have the "right" not to issue a diagnostic for initialization.

In C, constraint violations only require "diagnostics", the compiler may well continue compilation, but there is no guarantee that the resulting executable is valid.

On my platform, a Debian testing, both compilers give me a diagnostic without any option, so I guess your installation must be quite old and obsolete.

like image 4
Jens Gustedt Avatar answered Oct 18 '22 02:10

Jens Gustedt