27

Possible Duplicate:
Correct format specifier to print pointer (address)?

When printing a pointer using printf, is it necessary to cast the pointer to void *? In other words, in code like

#include <stdio.h>
int main() {
    int a;
    printf("address of a = %p\n", &a);
}

should the argument really be (void *) &a? gcc doesn't seem to give any warnings when no explicit cast is made.

Community
  • 1
  • 1
Joshua Green
  • 1,517
  • 1
  • 10
  • 14
  • why would it give a warning when you are giving a parameter to printf which accepts anything and retrieves the data specified by %p, which is the address that points to a. – TigOldBitties Jan 29 '12 at 20:14
  • `printf` is not typesafe - should why should its variables give a jammy dodger - it its up to you. Welcomen to OOP. – Ed Heal Jan 29 '12 at 20:16
  • @TigOldBitties: Because gcc does static analysis based on `printf` format strings. – Ben Voigt Jan 29 '12 at 20:22
  • 4
    Yes, in C you **must** cast to `void*` (I have no idea about C++). Read 7.19.6.1 in [the C99 Standard](http://port70.net/~nsz/c/c99/n1256.html#7.19.6.1): "p --- The argument **shall** be a pointer to void". – pmg Jan 29 '12 at 20:23
  • Also, for the record, that;s not just the case in `printf`. You also don't have to cast pointers to `void*` in any other case where a `void*` is expected. – Mr Lister Jan 29 '12 at 20:24
  • @pmg They mean that it must be a pointer. Conversion to `void*` is automatic, as long as it's a pointer. – Mr Lister Jan 29 '12 at 20:25
  • 4
    @MrLister: Nope, that's false. The reason you have to cast for `printf` is that it's a variadic function. The prototype cannot tell the compiler the type that's expected for each argument, so implicit conversions to `void *` will not help you here. You really do need the cast. – R.. GitHub STOP HELPING ICE Jan 29 '12 at 20:26
  • @MrLister if you follow the standard religiously, then you have to always cast it to `void*`. When the standard says something, it's not optional. Just because it works when you don't do it doesn't mean you're doing it right. – Seth Carnegie Jan 29 '12 at 20:27
  • @MrLister: no, they mean what they say ... that's why they are the Standard: by design as little as possible of the contents of the Standard is open to interpretation. – pmg Jan 29 '12 at 20:27
  • 4
    @SethCarnegie: I think the word "religiously" is misleading. If you don't cast to `void*`, there are real systems where it will fail. – Keith Thompson Jan 29 '12 at 20:29
  • @KeithThompson: I agree totally. Every time I see someone qualify a statement about what C requires with "religiously" or "strictly speaking", or "if you want to be exactly right" or anything of that sort, I cringe. There's no reason not to just say "you must do this" and refer people to the relevant part of the standard. – R.. GitHub STOP HELPING ICE Jan 29 '12 at 20:31
  • @TigOldBitties, `gcc` is quite happy to give warnings about `printf` mismatches. Indeed, the reason I'm wondering about this is that I recently corrected a bunch of code by replacing statements like `printf("0x%x", &a)` with `printf("%p", &a)` in order to eliminate `gcc`'s warnings. I've found plenty of code around the internet that _doesn't_ explicitly cast to `void *`, so I wasn't sure if that was automatic. – Joshua Green Jan 29 '12 at 20:31
  • @KeithThompson, R.., I just say that because I've never seen such a system. I could be wrong and it's only my computers that don't fail because I don't cast a pointer to `void*` before passing it to `printf`. Out of curiosity, what is an example of such a system? – Seth Carnegie Jan 29 '12 at 20:33
  • @Seth: The classic example is a machine with word-based addressing, where pointer values 0x1001 and 0x1002 would refer to consecutive *words*, not *bytes*. On such a machine, you need a larger type (or maybe some bit twiddling hacks if some high bits of pointer values don't need to be used) for character pointer types (and `void *` is essentially a character pointer type with special conversion semantics). – R.. GitHub STOP HELPING ICE Jan 29 '12 at 20:36
  • Please, can anybody give an example of a system where `&a` can be different from `(void*)&a`? Please? – Mr Lister Jan 29 '12 at 20:36
  • I believe pointers to functions and pointers to objects have different representations on the AS/400, for instance. *In the old times of DOS and segmented-memory you could have near and far pointers ...* – pmg Jan 29 '12 at 20:36
  • 1
    @SethCarnegie: I don't have a concrete example. I have worked on systems where `void*` and `int*` have different representations (Cray T90, native addresses point to 64-bit words, `void*` and `char*` store a byte offset in the high-order 3 bits of the pointer, all handled slowly in software), but even on that system a valid `int*` pointer is also a valid `void*` pointer to the same address. But I believe there are similar systems where the byte offset is *not* stored in the pointer itself, and `void*` is bigger than `int*`. – Keith Thompson Jan 29 '12 at 20:37
  • @Joshua: GCC has a lot of cases where they warn about things that are blessed by the standard as intentional features, and fail to warn about dangerous things that should be warnings (often because GCC assigns well-defined behavior to them and their people are lazy and like to use such incorrect constructs). I wouldn't put too much faith in GCC's warnings for determining if C code is correct... – R.. GitHub STOP HELPING ICE Jan 29 '12 at 20:38
  • @R Oh, you were typing that just as I was asking. why would, on a machine like that, `&a` push a different value on the stack than `(void*)&a` would? – Mr Lister Jan 29 '12 at 20:39
  • @MrLister: `&a` and `(void*)&a` are *always* different. They're of different types. They happen to have the same representation on most systems. Whether there are systems where their representations differ or not, the standard is carefully and deliberately written to allow for conforming implementations on such systems. And any time your code's behavior is not defined by the standard, an aggressively optimizing compiler may break your code's behavior, because it's permitted to assume that the behavior is defined. – Keith Thompson Jan 29 '12 at 20:41
  • @KeithThompson that's basically what I meant. It seems to me like one of those things that you can disobey the standard on, and _almost always_ have no problems. Of course, problems arise when someone ports your code to the Cray T90 or goes back in time to the Old Times Of DOS. But you generally know ahead of time whether your code will be running on one of those unusual systems. If you are writing code for your own personal use or are doing things that will restrict the systems your code runs on to "normal" systems then you will be fine. Don't take it the wrong way, it's all "IMO", not a law. – Seth Carnegie Jan 29 '12 at 20:42
  • @SethCarnegie: No, you don't always know. What if your code is ported to a future system with different pointer sizes? Avoid the "All the world's a VAX" disease (http://www.seebs.net/c/10com.html #10). – Keith Thompson Jan 29 '12 at 20:47
  • @pmg I know about the difference between `near` and `far`, and how pointers can be either near or far by default (depending on the memory model), but I wasn't convinced that, for instance, `void*` could be `far` by default and `int*` `near`. But never mind about that now. I've got to get busy; a lot of C programs to adjust. – Mr Lister Jan 29 '12 at 20:48
  • 2
    @Keith: I don't think the issue is future systems with different pointer sizes. That backwardness is behind us, thankfully. The issue nowadays is optimization. For the sake of performing aggressive optimization, the compiler is allowed to assume your program is only performing operations which have well-defined results. This may lead to optimizations that cause programs having undefined behavior, *even if that undefined behavior seems innocuous from a naive standpoint*, to horribly break. – R.. GitHub STOP HELPING ICE Jan 29 '12 at 20:50
  • OK, it seems that I should be casting to `void *` even if it's unlikely I'll ever run this on a system for which it actually matters. I'll make the requisite changes as soon as possible. Thanks all! – Joshua Green Jan 29 '12 at 20:52
  • 1
    @R..: I don't know what future systems are going to look like. I humbly suggest that you don't either. Some new development that we can't yet anticipate might make differently-sized pointers extremely useful again. We at least should not assume that that won't happen. – Keith Thompson Jan 29 '12 at 20:53

3 Answers3

23

Yes, the cast to void* is required.

int a;
printf("address of a = %p\n", &a);

&a is of type int*; printf's "%p" format requires an argument of type void*. The int* argument is not implicitly converted to void*, because the declaration of printf doesn't provide type information for parameters other than the first (the format string). All arguments after the format string have the default argument promotions applied to them; these promotions do not convert int* to void*.

The likely result is that printf sees an argument that's really of type int* and interprets it as if it were of type void*. This is type-punning, not conversion, and it has undefined behavior. It will likely happen to work if int* and void* happen to have the same representation, but the language standard does not guarantee that, even by implication. And the type-punning I described is only one possible behavior; the standard says literally nothing about what can happen.

(If you do the same thing with a non-variadic function with a visible prototype, so the compiler knows at the point of the call that the parameter is of type void*, then it will generate code to do an implicit int*-to-void* conversion. That's not the case here.)

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
4

Is this a C or a C++ question? For C++, it seems that according to 5.2.2 [expr.call] paragraph 7 there isn't any implicit conversion to void*. It seems that C99's 6.5.2.2 paragraph 6 also doesn't imply any explicit promotion of pointer types. This would mean that an explicit cast to void* is required as pointer types can have different size (at least in C++): if the layout of the different pointer types isn't identical you'd end up with undefined behavior. Can someone point out where it is guaranteed that a pointer is passed with the appropriate size when using variable argument lists?

Of course, being a C++ programmer this isn't much of a problem: just don't use functions with variable number of arguments. That's not a viable approach in C, though.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • It's not a size issue, all object pointers are the same size (since the standard does require them to round-trip). But the representation is not necessarily the same. – Ben Voigt Jan 29 '12 at 20:33
  • 2
    @BenVoigt: No, all object pointers are not required to be the same size. `void*` must be big enough to hold the converted value of any object pointer without loss of information, but converting `char*` to `int*` and back again is not required to yield the original value. The only round-trip requirement is `anything*` to `void*` and back again. (`char*` is also covered by this, since it must have the same representation as `void*`.) – Keith Thompson Jan 29 '12 at 20:42
  • @KeithThompson: On closer reading, it depends on the alignment. If `int` doesn't have a stronger alignment requirement than `char`, then `char*` to `int*` and back again is required to yield the original value. 5.2.10p7 "Converting a prvalue of type "pointer to `T1`" to the type "pointer to `T2`" (where `T1` and `T2` are object types and where the alignment requirements of `T2` are no stricter than those of `T1`) and back to its original type yields the original pointer value." – Ben Voigt Jan 29 '12 at 20:49
2

I think it might be necessary to cast. Are we certain that the size of pointers is always the same? I'm sure I read on stackoverflow recently that the size (or maybe just the alignment?) of a struct* can be different to that of a union*. This would suggest that one or both can be different from the size of a void*.

So even if the value doesn't change much, or at all, in the conversion, maybe the cast is needed to ensure the size of the pointer itself is correct.

In print, %p expects a void* so you should explicitly cast it. If you don't do so, and if you are lucky then the pointer size and pointer representation might save the day. But you should explicitly cast it to be certain - anything else is technically undefined behaviour.

Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88
  • 1
    Your answer addresses mechanisms by which the failure might happen in an implementation, but the key point is that the interface contact requires you to pass the right types to `printf`, and failure to follow this requirement results in UB. – R.. GitHub STOP HELPING ICE Jan 29 '12 at 20:29
  • @H2CO3, it's not me doing the downvoting anywhere on this question. At least one other person is putting forward alternative theories, and maybe there are many others who are lurking and downvoting. – Aaron McDaid Jan 29 '12 at 20:29
  • I didn't downvote, but I could see how an answer of the form "I'm not sure but it could be this way..." could be seen by others as worthy of downvotes. In any case, sadly this is the best answer so far, and the only one that's not completely wrong. – R.. GitHub STOP HELPING ICE Jan 29 '12 at 20:32
  • @R: Hopefully this question finishes being closed as a duplicate; the other question has correct authoritative answers. – Ben Voigt Jan 29 '12 at 20:35
  • 1
    @R. my message was directed at somebody else who accused me of downvoting every answer other than my own. Anyway, I'm not very happy with my answer either, but I felt compelled to post it after seeing the other answers. I'm hoping I can upvote other, better written, answers in due course. – Aaron McDaid Jan 29 '12 at 20:36
  • @BenVoigt, I've now also voted to close as duplicate. Will I lose the upvotes I got? :-) – Aaron McDaid Jan 29 '12 at 20:40
  • @aaron: No, you won't lose the rep. But some people hold answering and voting to close as being poor etiquette. IMO, writing an answer before a dupe has been located is fine. – Ben Voigt Jan 29 '12 at 20:43