Idea behind "[...] makes pointer from integer without a cast"

Does somebody know what's the idea behind this?

  • Is it mostly to allow prehistoric code to be compiled without errors?
  • Or just to comply to the standard? Then latter maybe needs some fixing.

It is to comply with the standard in the sense that the standard requires conforming implementations to diagnose such issues, as @R.. describes in his answer. Implementations are not required to reject programs on account of such issues, however. As for why some compilers instead accept such programs, that would need to be evaluated on a per-implementation basis, but this quotation from the first edition of K&R may shed a bit of light:

5.6 Pointers are not Integers

You may notice in older C programs a rather cavalier attitude toward copying pointers. It has generally been true that on most machines a pointer may be assigned to an integer and back again; no scaling or conversion takes place, and no bits are lost. Regrettably, this has led to the taking of liberties with routines that return pointers which are then merely passed to other routines -- the requisite pointer declarations are often left out.

(Kernighan & Ritchie, The C Programming Language, 1st ed., 1978)

Notice in the first place that this long predates even C89. I'm a bit amused today that the authors were then talking about "older" C programs. But note too that even at that time, the C language as defined by K&R did not formally permit implicit conversion between pointers and integers (though it did permit casting between them).

Nevertheless, there were programs that relied on implicit conversion anyway, apparently because it happened to work on the targeted implementations. It was attractive, by some people's standards at the time, in conjunction with primordial C's implicit typing rules. One could let a variable or function intended to return or store a pointer default to type int by omitting its declaration altogether, and as long as it was interpreted as a pointer wherever it ultimately was used, everything usually happened to work as intended.

I'm inclined to guess that everything continuing to work as intended, thereby supporting backwards compatibility, was a consideration for compiler developers in continuing to accept implicit conversions, so that's "allow[ing] prehistoric code to be compiled." I note, however, that these days code with implicit conversions of this kind are much less likely to work as intended than they used to be, for many machines these days have 64-bit pointers but only 32-bit ints.


The behaviour of assigning an arithmetic type to a pointer is not well formed in the C standard. (See the answer provided by R.. for relevant sections.)

Your compiler (or the settings you're using) have decided to treat that as a warning.

Compilers have default settings and often support language extensions and those may be quite liberal.

Notice for anything outside the language specification it's up to the implementers of the compiler to decide what's an error or if they're going to interpret it as a language extension and (hopefully) issue a warning that the the code is off the offical piste.

I agree that's not best. My recommendation would be to treat is an error because it almost certainly is and casting an int to a pointer is the standard supported way of being explicit and getting the same result (e.g. int *n).

I think you're using GCC and it's notorious for "helpfully" compiling things that it could better serve you by rejecting and making you use standard constructs.

Enable all warnings (-Wall on the gcc command-line) and make sure you understand and address them all appropriately.


Per 6.5.2.2 Function Calls, ¶ 7:

If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, taking the type of each parameter to be the unqualified version of its declared type

The relevant text in 6.5.16.1 Simple Assignment is:

Constraints

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.

None of these allow the left operand as a pointer and the right operand as an integer. Thus, such an assignment (and by the first quoted text above, the function call) is a constraint violation. This means the compiler is required by the standard to "diagnose" it. However it's up to the compiler what it does beyond that. Yes, an error would be highly preferable, but just printing a warning is a low-quality way to satisfy the requirement to "diagnose" constraint violations like this.

Tags:

C