views:

459

answers:

5

Someone told me this bit of code prints 29. Why is that?

int *a = 17; 
printf("%d", a+3);
+34  A: 

Because when you add to a pointer it adds the object size. In this case the object size is 4 (sizeof(int) == 4) -- so 17 + 3 * 4 == 29.

Aaron
Hi Aaron, there is typo in ur answer. It should be 17+ 3*4 = 29.
Zinx
@Zinx... Ha - that error was there for all of 5 seconds and you happened to spot it...
Aaron
Where is this coming from? a is a pointer - you're not dereferencing it, therefore you're not going to get the 17 back out. What this will do, is take the address 12 past wherever 'a' is, and print the "integer" value at that address.
Chris
Actually, it is correct. You're assigning the literal value "17" to the pointer - so it's pointing at memory address 17. After adding 4 ints, it's pointing at memory address 29. So then, when you ask to print out the value of the pointer (as opposed to the value of what it points at), it will print 29.
Anon.
@Anon. What an idiot - I should have realized that when he assigns 17, he's declaring a, not dereferencing it. Thanks!
Chris
The C standard does not guarantee that even reading such a pointer makes sense, and it is undefined behavior. Also, given that `sizeof(int)` can be (and is) different on different environments, and that he's printing a pointer using "%d", anything can happen. That he got 29 is coincidence. Finally, on some machines, such an address could be a trap representation, which means that printf will fail spectacularly.
Alok
Could you explain how reading the number "17" out of a value on the stack could cause a "spectacular failure"? The actual memory address 17 has nothing to do with this.
Anon.
C99 draft, section 6.3.2.3 (emphasis mine): An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, *might not be correctly aligned*, might not point to an entity of the referenced type, and *might be a trap representation*. ("previously defined" is for integer 0). Then it says: Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined.
Alok
The pointer variable is never deferenced, so it does not actually cause a trap, and the alignment does not matter. If the program had another line, `*a=0` it would cause a crash on most hardware -- but it does not have that.
Kevin Panko
@Chris: `(a+3) == 17` but `*(a+3) == crashed program`. That little star makes a big difference!
Kevin Panko
Misalignment doesn't matter, because we don't dereference it. Similarly, neither does it being a trap representation.
Anon.
@Kevin: No, that is not guaranteed. The trap representation is the pointer itself. It is undefined behavior in C to use a trap value, whether you dereference it or not. For example, the CPU might barf immediately just if you copy a trap-representation pointer into a pointer register.
Steve Jessop
Anon, Chris, and Kevin: please read the quote I posted above from the standard. Yes, we all know that "it works", but there is no guarantee from the standard that it will, and there are/could be architectures on which things like this "don't work". In other words, the code is not portable.
Alok
@Alok - you are 100% correct. There is a possibility that this will completely choke or print out a value other than 29, and is unportable. However - on most home computers in the world (wihch are unfortunately x86) using most compilers (at least GCC and Visual Studio) this will not choke and will print 29. With GCC you at least get a warning...
Aaron
@Alok: You need to watch more carefull what you are reading. The text from the standard you quoted applies to *explicit user-specified conversions*. I.e. you can do `int *a = (int *) 17`, but you *can't* do `int *a = 17`. The later is explcitly, strictly and undisputably illegal in C (and in C++). Any compiler that would fail to flag this blatant error would be laughed off the market right away.
AndreyT
@Anon: Incorrect. It is not possible to "assign literal value 17" to a pointer without an explicit cast in C. The code is invalid.
AndreyT
@Andrey: thanks. So, without the case, `int *a = 17` is constraint violation. With the cast, it's implementation defined. The printf call is horribly wrong anyway, because it prints a pointer with %d with no prototype in scope (and even if there was a prototype, one would still need to cast the argument). I am happy to learn the difference between `int *a = 17` and `int *a = (int *) 17`.
Alok
@AndreyT - You are incorrect. That statement is perfectly legal C. I just tried it in GCC 3.4.4 and 4.3.2 - which is a fairly mature compiler and isn't exactly being 'laughed off the market right away.'
Aaron
@Aaron: when you compile with `gcc -W -Wall -ansi -pedantic`, does it warn you? By default, `gcc` compiles a language called "GNU C", not standard C. See `man gcc`, `-std=` option for details.
Alok
@Alok - yes it gives a warning. Warning != 'It is not possible to "assign literal value 17" to a pointer without an explicit cast in C. The code is invalid.' Yes - the code is crap and nonsensical but it's not an error.
Aaron
@Aaron: Incorrect. The statement is illegal in C. GCC gave you a warning. "Warning" is a valid form of diagnostic message in C. From the language point of view there's no difference between warnings and errors. GCC is known for its rampant compiler extensions, that take it far beyond the scope of legal C. Which is why you never use GCC to test the validity of the code and even if you do, you run it with `-ansi -pedantic-errors -Wall` and treat all warnings as potential constraint violations (which require additional research).
AndreyT
@Aaron: But better yet, stop wasting your and other people's time making useless experiments with GCC for that purpose. If you wish to test the validity of the code by trying to compile it, first place to go is Comeau Online, not GCC.
AndreyT
@Aaron: Again, there's no such thing as an "error" in C. When someone says that the code is invalid and non-compilable, it means that the code contains a so called *constraint violation*. When compiler finds a constraint violation it is required to issue a *diagnostic message*. The compiler is not requred to stop comilation, it is just requred to inform you that your code is not valid C. This is exactly what GCC is doing. It told you: it is not C, but I'll compile it anyway somehow. So, once again, just try to memorize it for now (and understand later): `int *a = 17` is not valid C code.
AndreyT
@Aaron: You have been given links to the language standard, you have been given references to Comeau Online compiler, which will provide you with more pedantic diagnostic in this case. If you still don't get it, then sorry, there's nothing I can do.
AndreyT
@AndreyT - You may actually be correct. However your condescending attitude and snide remarks make me think that you're just compensating and that I should ignore you - which is what I will now do.
Aaron
+11  A: 
a+3 == a + (3 * sizeof(int)) == a + 12 == 17 + 12 == 29
JSBangs
A: 

can print anything .. you are setting a pointer to the location '17' in memory ...

fabrizioM
Not quite. If he tried to *dereference* `a` he'd almost certainly get a segfault, but he's printing the value of the pointer as an int.
JSBangs
@JS: Not quite. There is no guarantee that 17 is a valid address, and it could be a trap representation. Merely reading such a pointer is an error (which he does by doing `a+3`).
Alok
Undefined behavior, for sure. It could print anything and be conformant. Heck, it could reformat all your .doc files as haiku and be conformant.
David Thornley
@DavidThornley, this (and the blue-demons-out-your-nose meme in general) makes me want to write a C compiler that aggresively does ridiculous things when presented with undefined behavior.
JSBangs
Or, as they say in `comp.lang.c`, it can make "daemons fly out of your nose"!: http://groups.google.com/group/comp.std.c/msg/dfe1ef367547684b
Alok
In `comp.lang.c` you'd get immediately explained that `int *a = 17` is not legal C and the debate would be over. No demons involved. The only demonic potential this code has is the use of `%d` for printing pointers. But that would come into play only after the `int *a = 17` is fixed.
AndreyT
@Andrey: still, nasal daemons are possible with `int *a = 17`. With `int *a = (int *)17`, it's implementation defined, and I hope there doesn't exist an implementation that says 'if you do this, there will be daemons' :-)
Alok
I believe the nasal demons are demonic as opposed to benevolent (daemonic).
Jonathan Leffler
Does incorrect use of 'demon' lead to nasal demons? I hope not. I don't even *want to* think about nasal daemons!
Alok
+11  A: 

Everyone knows the answer is 23, at least on the 6809.

a+3 == a + (3 * sizeof(int)) == a + 6 == 17 + 6 == 23
Richard Pennington
+1 for the unexpected...
Aaron
+1 for making me remember awful headaches programming motorola 68k ;)
Aurélien Vallée
+1 for the nostalgia factor. I knew the 6809 well, many years ago. Please tell me you're not still using it? :)
Steve Fallows
Headaches with 68K??? It was a dream next to the 6809!
Steve Fallows
@Steve: No, I don't use it today. I wrote my first C compiler for it and still have an HMS emulator in the basement.
Richard Pennington
Back when I had a 6809 machine, I loved it. It was so much easier to program in assembler than my Z80 box. (BTW, this would have worked on my earlier Mac C compilers. It took some time before Lightspeed C, Think C, and Codewarrior C started using 32-bit ints.)
David Thornley
+2  A: 

In C language pointers cannot be initialized with integral values, with the only exception of an Integral Constant Expression that evaluates to integral zero. 17 does not satisfy that requirement.

You code is invalid. It doesn't "print" anything. The question makes no sense whatsoever. Any attempts to analyze this question from the point of view of the pointer arithmetic are ridiculous and just useless waste of time.


ISO/IEC 9899:1999 (Progamming Languages - C)

6.5.16.1 Simple assignment

Constraints

One of the following shall hold:93)

— the left operand has qualified or unqualified arithmetic type and the right has arithmetic type;

— the left operand has a qualified or unqualified version of a structure or union type compatible with the type of the right;

— 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;

— one operand is a pointer to an object or incomplete 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 a pointer and the right is a null pointer constant; or

— the left operand has type _Bool and the right is a pointer.

93) The asymmetric appearance of these constraints with respect to type qualifiers is due to the conversion (specified in 6.3.2.1) that changes lvalues to ‘‘the value of the expression’’ which removes any type qualifiers from the type category of the expression.

AndreyT
LOL! I think it would be hard to come out with a more blatant example of the failure of the SO voting system. Several bogus answers got upvoted as high as 30, while the only correct one got downvoted :)) Is everyone with at least basic knowledge of C already on Christmas vacation?
AndreyT
Except that every compiler I can get my hands on prints out either 29 or 23. ;-) In real life, people assign addresses to pointers all the time. If it didn't work, planes would crash, CT scanners wouldn't, OSes wouldn't operate, etc. A lot of people assume that this "undefined" behaviour does what they want. And, I suspect, a compiler vendor that really made this undefined would have several sad, and former, customers. (... as I go off to set the bypass cache bit on the ethernet driver's buffer address pointer)
Richard Pennington
Firstly, if the language starndard says the code is illegal - it is no longer C, regardless of what your compilers "print". Secondly, you must be ignoring disgnostic messages issued by your compiler. This is not a good idea. When you ignore diagnostic messages, it makes no sense to watch the program "prints". It is meanigless anyway. If your compiler generates no diagnostic, your compiler is laughable garbage.
AndreyT
Thirdly, the first compiler you should try such things with is always Comeau Online. I'm surprised I have to mention something that I thought is common knowledge. Comeau Online correctly reports the error, which closes the debate.
AndreyT
As for "people assign addresses to pointers all the time" - yes, they do. But they do it, as language requires, with an explicit cast. In this case that would be `int *a = (int *) 17`. People who do it "all the time" without a cast, as in the OP, very quickly find themselves flipping burgers. That latter principle (`int *a = 17` => go flip burgers) is the real reason why planes don't crash and CT scanners scan.
AndreyT
I love pushing buttons. ;-) Indeed, all the compiler issued warnings. Doesn't everyone use -Werror? They still printed 29 or 23.
Richard Pennington
+1 I like the flipping burgers comment. Sometimes I wish, after 30 years of embedded programming, that I had a longer stint of flipping burgers. ;-)
Richard Pennington
By the way, I wrote the 6809 C compiler I mentioned in another comment when the 6809 was *new*. Please don't do the math.
Richard Pennington
Great, but the only thing it demonstrates is that in some cases people who write C compilers lack proper knowledge of the language they are supposed to compile. Is it OK? It was OK in 1985, but in 2009 it is inexcusable. And later other people use those compilers to test the correctness of their C code :))), thus perpetrating the cycle of C ignorance.
AndreyT
Since you seem to lack a sense of humor and an ability to read and understand the written word, I'll disengage.
Richard Pennington
The question is not ridiculous. When the `(int *)` cast is added to the literal 17, it becomes legal C code, yes? Which prints the number 29, right? (When sizeof(int) is 4, which it often is.) You may be right that the code is incorrect, but many compilers accept it anyway (and emit diagnostic messages). You made your point, but when you go on to say that this discussion is a waste of time, that is disrespectful.
Kevin Panko
@Kevin: No it doesn't. In order to become legal with `%d` format specifier used in `printf` it also needs the value of the pointer argument to `printf` to be explicitly cast back to `int`. But even that doesn't make a question about the value it might print a valid question, since the whole thing is heavily platform-dependent.
AndreyT
What many people here fail to realize is that on a job interview (for example) this would be a classic trick question. Any candidate that would go into pointer arithmetic given the above code, would be heard through with a polite smile and would be given a "FAIL" mark on the question. "Riddles" like that are specifically crafted to check if you are good enough to note invalid pointer initialization and invalid format specifier. Extra points are given for mentioning that `printf` (variadic function) requires a forward declaration before use.
AndreyT
@AndreyT, +1 from me and thanks for your comments.
Alok
I wonder how many interviewers would also want to hear that it is implementation-defined whether `(int*)12` is a trap representation, and that if it is, the behaviour of the rest of the code is undefined. Probably not many, but I think it's worth bearing in mind that not all interviewers will necessarily demand the same answer to this question. If you're being interviewed for a job programming Windows in C, and this code compiles on MSVC and Intel compilers, they might not care whether you know that it's illegal, they might just want the Windows answer.
Steve Jessop