5

I can't seem to make sense of a GCC compiler warning I get when I try to assign a void * value to a intptr_t variable. Specifically, when I compile with -std=c99 -pedantic, I get the following warning regarding the initialization of variable z on line 7:

warning: initialization makes integer from pointer without a cast [-Wint-conversion]

Here is the source code:

#include <stdint.h>

int main(void){
        unsigned int x = 42;
        void *y = &x;

        intptr_t z = y; /* warning: initialization makes integer from pointer without a cast [-Wint-conversion] */

        return 0;
}

Naturally, if I explicitly cast y to intptr_t then the warning disappears. However, I confused why the warning is present for implicit conversions when the whole purpose of intptr_t is in the conversion and manipulation of void * values.

From section 7.18.1.4 of the C99 standard:

The following type designates a signed integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer:

intptr_t

Am I misinterpreting the standard, or is GCC simply overly pedantic in its "integer from pointer" check in this case?

Vilhelm Gray
  • 11,516
  • 10
  • 61
  • 114
  • Is this [previous question](http://stackoverflow.com/questions/9492798/using-intptr-t-instead-of-void) relevant? – Weather Vane Oct 13 '16 at 19:25
  • 3
    It's not overly pedantic. Implicit conversions are just a common enough source of error for the warning to exist. Think about how it can become a pain when overload resolution is involved. So if you really know what you are doing, inform the compiler by doing a (hopefully c++-styled) cast. – StoryTeller - Unslander Monica Oct 13 '16 at 19:29
  • 1
    `intptr_t` is a normal integer type. The only guarantee is that it can hold all relevant information of a `void *`, which is not guaranteed for the other integer types. So the warning is correct. Why do you want to use an integer in the first place? Normally one does this for low-level arithmetic- or bit-operations on an address. Then `uintptr_t` is strongly recommended. Otherwise think thrice(!) if you really want an integer to hold a pointer. – too honest for this site Oct 13 '16 at 19:30
  • It's a warning, not an error. If the cast was required, you would have gotten an error. – user2357112 Oct 13 '16 at 19:35
  • [Another answer](http://programmers.stackexchange.com/questions/290574/what-is-the-motivation-for-casting-a-pointer-into-a-integer) I found useful re. `intptr_t` – cxw Oct 13 '16 at 19:35
  • 3
    @StoryTeller: Considering that this is C99, a C++-style cast would be a bad idea. – user2357112 Oct 13 '16 at 19:36
  • @Olaf I first encountered the warning when attempting to pass an address to a library function which expects a `intptr_t`. I wasn't aware of the use of `intptr_t` until this moment. – Vilhelm Gray Oct 13 '16 at 19:36
  • 1
    @user2357112: Very bad advice! Warnings in C should be treated very seriously! – too honest for this site Oct 13 '16 at 19:41
  • @user2357112, yikes. Missed the tags. Well c-style cast it is, for all its shortcomings. (Although I've seen some programmers hide such casts behind macros to make them stand out in code review, I like that approach). – StoryTeller - Unslander Monica Oct 13 '16 at 19:41
  • @Olaf: I didn't say to ignore the warning. I just said it's not an error. – user2357112 Oct 13 '16 at 20:05
  • @user2357112: Many warnings in C **are** errors! They are "just" warnings, because C is weakly typed and the standard tolerates shooting into your foot mostly for legacy reasons. A beginner shoudl always treat them as errors. Good coding-standards require to enable most warnings and treat them as errors for good reasons. – too honest for this site Oct 13 '16 at 21:00
  • @Olaf: There's an important difference between a warning you should take as seriously as an error, and an actual compilation error. Get them mixed up, and you end up really confused when code that you thought couldn't possibly compile ends up running anyway. – user2357112 Oct 13 '16 at 21:25
  • 1
    @user2357112: `initptr_t` is a `typedef` for one of the standard integer types, as any other type, except the 5 (6 for unsigned) standard integer types. The compiler does not know about the additional implication of `intptr_t` for good reason. Most times assigning a pointer to an integer **is** an error (forgot to dereference the pointer). The compiler will emit a warnings if yor assign a pointer to `char`, too. That is also an integer type. You think that warning can also be ignored? – too honest for this site Oct 13 '16 at 21:55
  • @user2357112: If you write professional C code, you really appreciate such additional static code analysis modern compilers do. The only reason the standard does not make them errors is semi-bugged legacy code. Which gave C the bad reputation it actually has. – too honest for this site Oct 13 '16 at 21:57
  • @Olaf: At no point have I said to ignore warnings. – user2357112 Oct 13 '16 at 22:01

1 Answers1

5

Summing up! Apologies in advance for any errors — please leave me a comment.

In C99:

  • Any pointer can be converted to an integer type.1
  • You might want to do that, e.g., if you are implementing your own operating system!
  • Conversions between pointers and integers can go horribly wrong,1 so are usually not what you want.
  • Therefore, the compiler warns you when you convert pointers to integers without casting. This is not overly pedantic, but to save you from undefined behaviour.
  • intptr_t (and uintptr_t, and likewise throughout) is just an integer type,2 so it is subject to the same risks as any other pointer-to-integer conversion. Therefore, you get the same warning.
  • However, with intptr_t, you at least know that the conversion from a pointer won't truncate any bits. So those are the types to use — with explicit casts — if you really need the integer values of pointers.

    • The spec1, #6 says that

      ... the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined.

      With intptr_t, the result can be represented in the integer type. Therefore, the behaviour is not undefined, but merely implementation-defined. That is (as far as I know) why those types are safe to use for receiving values from pointers.

Edit

Reference 1, below, is part of section 6.3, "Conversions." The spec says:3

Several operators convert operand values from one type to another automatically. This subclause specifies the result required from such an implicit conversion...

and refers to section 6.5.4 for a discussion of explicit casts. Therefore, the discussion in Reference 1 indeed covers implicit casts from any pointer type to intptr_t. By my reading, then, an implicit cast from void * to intptr_t is legal, and has an implementation-defined result.1, 4

Regarding whether the explicit cast should be used, gcc -pedantic thinks it should, and there must be a good reason! :) I personally agree that the explicit cast is more clear. I am also of the school of thought that code should compile without warnings if at all possible, so I would add the explicit cast if it were my code.

References

1C99 draft (since I don't have a copy of the final spec), sec. 6.3.2.3 #5 and #6).

2Id., sec. 7.18.1.4

3Id., sec. 6.3

4Id., sec. 3.4.1, defines "implementation-defined behavior" as "unspecified behavior where each implementation documents how the choice is made." The implication is that the conversion is legal, but that the result may be different on one platform than on another.

Community
  • 1
  • 1
cxw
  • 16,685
  • 2
  • 45
  • 81
  • I see now from paragraphs #5 and #6 of section 6.3.2.3 that there are inherent risks in the conversions between integers and pointers (and thus `intptr_t` and `void *`). Would it be correct to say that the conversion from `void *` to `intptr_t` to `void *` does not risk *undefined behavior* (as mentioned in paragraph #6) because `intptr_t` is defined as capable of representing the result of the `void *` conversion? – Vilhelm Gray Oct 13 '16 at 20:13
  • Just to clarify: implicit conversion is valid for `void *` to `intptr_t` (albeit with an implementation-defined result), but an explicit cast is often recommended to make the intention of the code clear. Is that correct? – Vilhelm Gray Oct 14 '16 at 12:46