Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c

I always wondered why warnings passing argument 1 from of 'foo' makes pointer from integer without a cast and alike are only warnings and not errors.

Actually these warnings are almost always errors.

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.

Example:

int foo(int *bar)
{
  *bar = 42;
}

void bar()
{
  int n = 0;
  foo(n);      // this is obviously an error
  ...
}
like image 205
Jabberwocky Avatar asked Feb 19 '19 15:02

Jabberwocky


Video Answer


2 Answers

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.

like image 194
John Bollinger Avatar answered Sep 25 '22 09:09

John Bollinger


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.

like image 27
R.. GitHub STOP HELPING ICE Avatar answered Sep 25 '22 09:09

R.. GitHub STOP HELPING ICE