http://codepad.org/WD1oWXL8

#include <cstdio> int main(void) { int *p; scanf("%p", &p); return 0; } 

Line 6: warning: format '% p' ​​expects type 'void **', but argument 2 has type 'int **'

Correct for an explicit caste:

http://codepad.org/TR1XEBKP

 scanf("%p", (void**)&p); 

Line 6: warning: dereferencing type-punched pointer will break strict-aliasing rules

Try void * :

http://codepad.org/pH4lUnF5

 scanf("%p", (void*)&p); 

Line 6: warning: format '% p' ​​expects type 'void **', but argument 2 has type 'void *'

As a result, we feed double castes:

http://codepad.org/1UlOShQJ

 scanf("%p", (void**)(void*)&p); 

Finally compiled.

Question:

Why exactly such diagnostics with pointers was added and what potential problems can a reading of a pointer other than void* cause?

  • And it did not look? stackoverflow.com/questions/27686682/… - Unick
  • @Unick, there is generally past the same. - Qwertiy
  • Um, VC ++ said only that the scanf function is unsafe ... Yes, and here without any warnings ... - Harry
  • @Harry, this gcc's analytics works. In VS, there is no such thing at all, and other compilers are not up to date. - Qwertiy
  • How do you imagine% p? This is a pointer. But besides the memory address, the pointer contains size . Want a pointer to int? Or double? How can the compiler distinguish between them? Therefore, we need an explicit type conversion - DNS

2 answers 2

Your question is tagged with C ++, but still to start a few words about C:

In C, a pointer of type int * can formally have an object representation that is different from the object representation of a pointer of type void * . In the C language, only all pointers to struct types (among themselves), all pointers to union types (among themselves), and also pointers void * and [signed/unsigned] char * (among themselves) have the same object representations (plus, of course their cv-qualified variations). No more matches for object representations of pointers are guaranteed.

This means that an attempt to write a void * value to an int * type pointer through memory re-interpretation (type punning) is not formally correct from the point of view of the language. The language unequivocally says that accessing an object of type int * as to an lvalue of type void * any way other than through a union leads to undefined behavior (see 6.5 / 7). This is the so-called rule (or axiom) strict aliasing.

Therefore, there is no formally portable way to make scanf("%p" directly into an int * type pointer. Even if the object representations of these types are the same, and even if your attempts to "strangle" the compiler warnings were successful, this still does not mean that your program will work correctly. The compiler, based on the above rules strict alising, has every right to believe that your scanf("%p" does not modify the pointer p (because he can not legally modify it), that is to ignore the relationship between the information you ovom scanf and the value of p .

Young Pioneer practitioners from the C sect is such a portable assembler for quite some time managed to ignore strict aliasing considerations and used type punning in their code to the right and left, regarding it as something of a secret knowledge that distinguishes them from the theorists. This continued until GCC and other compilers began actively using the most valuable information based on the axioms of strict aliasing in their optimizations. The howl that rose as a result of the swamps of the “practitioners” of this kind did not finally subside to this day. That's it from vlyapyvaniya in this swamp and trying to protect you compiler with your warnings.

If you want to read the value for your p through scanf , then perhaps the only civilized way is

 int *p; void *v; scanf("%p", &v); p = v; // с явным кастом для C++ 

Or, if you really want to do type punning, then through union

 union { int *p; void *v } u; scanf("%p", &u.v); p = up; 

Type punning through the union was legalized in C99 (TC3, it seems). Of course, with the proviso that all responsibility for the correctness of the resulting object representation falls on you.

The above is directly related to C ++, except that type punning through union in C ++ is not legalized.

  • 1. "In C language, an int * type pointer can formally have an object representation that is different from the object representation of a void * type pointer" - does this really happen at all? 2. " scanf("%p" does not modify the pointer p " - the scanf prototype looks something like this: int scanf(const char *format, ...) , therefore passing &p as a parameter does not violate type punning. 3. Does the union option not have the problems described in clause 1, if there is such an architecture somewhere? - Qwertiy
  • @Qwertiy: 1. Offhand I will not name. However, with a bit of a stretch, it can be said that on platforms with a strict alignment relationship (eg Solaris), the fact that the lower bits of the correct int * pointer must be zero is already an example of "difference in presentation". The point here is not really whether it happens or does not happen, but the fact that strict aliasing rules consider int * and void * incompatible types. - AnT
  • 2. I did not understand this point. If you pass a pointer to p somewhere, then it is clear that p can be easily modified there. And if you pass an incorrectly typed pointer to p , then all the prerequisites for type punning and violations of strict aliasing are in full. In addition, we are talking about scanf , and not about printf . And the compiler can easily have a complete understanding of the semantics of scanf . - AnT
  • 3. Again, a type punning resolution through a union means precisely and only that the compiler has no right to make any assumptions about strict aliasing in the case of union . And the rest of the problem, of course, can be easily. But this is your responsibility. - AnT

Line 6: warning: format '% p' ​​expects type 'void **', but argument 2 has type 'int **' (xs how nice it is to highlight yellow)

The -pedantic-errors flag (also known as -pedantic) is responsible for displaying this error. Go to man and read: ISO C and ISO C ++; Do not follow ISO C and ISO C ++. For ISO C, the following standard version of any -std option is used. blah blah blah. Those. The flag ensures that the code complies with the standard to improve portability to other platforms / compilers.

ISO C ++ forbids implicit casts of pointers. We are going to ISO C ++ and are looking for proof (because at the moment the standard has already exceeded 1500 pages. Of course, I will not do this).

Further, the error "Line 6: warning: dereferencing type-punched pointer will break strict-aliasing rules" is responsible for the -fstrict-aliasing flag. This flag indicates to the compiler that an object of the same type can never be located at the same address as an object of another type, except for the cases when these are similar objects, and void and int are different types.

As a result, you came to the correct decision, first cast int * to void *, then void * to void **. Although I would write this:

 #include <cstdio> int main(void) { void* p = nullptr; scanf("%p", &p); return 0; } 

and then I would cast void * where necessary.

Well, answering the question “Why exactly such diagnostics with pointers was added and what potential problems can a reading of a pointer other than void * cause?”. Such diagnostics was added so that the programmer clearly understood what, where and how he castes (and could not push the blame onto the compiler :)). About the potential problems I can not say, because I have never seen any pointers read by scanf, if you describe in more detail where it is used, then you might think.

PS There is no such crap in C, there the implicit caste to void is allowed, by the way. PPS "dereferencing type-punched pointer will break strict-aliasing rules" by the way I could not get to gcc 5.4.0, the rest of the errors are repeated successfully with the flags specified by @mymedia

references:

G ++ flags, -pedantic -fstrict-aliasing https://gcc.gnu.org/onlinedocs/gcc-3.0/gcc_3.html

ISO C ++ Standard: http://open-std.org/Jtc1/sc22/wg21/docs/papers/2016/n4606.pdf

Here is a small thread on the topic of void * https://stackoverflow.com/questions/23145730/why-does-c-forbid-implicit-conversion-of-void

Habr, about aliasing and bugs https://habrahabr.ru/post/114117/

A little about dereferencing and casting of pointers of unlike types https://stackoverflow.com/questions/26713851/dereferencing-type-punned-pointer-will-break-strict-aliasing-rules-wstrict-ali

  • "ISO C ++ prohibits implicit caste of pointers" - as far as I remember, the scanf prototype looks something like this: int scanf(const char *format, ...) , respectively, there is no caste during transmission and cannot be. This is a diagnosis of the format string and the matching parameters passed to it. - Qwertiy
  • Well, yes,% p expects type void ** - goldstar_labs