-1
char* p = "hello"; printf(" %c %c %c %c", *p, *++p, *p++, *p);

output:

l l h h 

I have read the below thread but still not able to find why the output is like " l l h h". Could anyone please answer.

Pointer expressions: *ptr++, *++ptr and ++*ptr.

Community
  • 1
  • 1
  • Seems pretty weird, I get `e e h e` with your code but when I save each pointer expressions in a character then output the character, it outputs `h e e l` like it's suppose to. Also got it to output `h e e l` by using 4 `printf` calls for each pointer expression. It does smell like undefined behavior – John Odom Feb 20 '14 at 16:12
  • @JohnOdom It's undefined behaviour so any output (or crash, or no output) is theoretically possible. Anyhow, `clang` gives me a warning from the code and produces `h e e l`… (I kind of wish they'd made the code that triggers the warning deliberately do something outrageous instead.) – Arkku Feb 20 '14 at 16:17

3 Answers3

2

Because the code contains both ++p and p++, it attempts to modify p twice.

These expressions appear in arguments to a function call. The C standard does not specify the order in which function arguments are evaluated.

When a program attempts to modify an object twice in an unordered way, the program is broken. The C 2011 standard says, in clause 6.5, paragraph 2, that when there are two unordered modifications to the same object, the C standard does not impose any requirements on the behavior of the program.

This means the compiler does not have to make this program work in any particular way. The compiler essentially “gives up” and does whatever it happens to do.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • I am wondering why does the C standard not specify the order of evaluation of function arguments. What is the reason to do so? Also, when a function is called, the caller pushes the arguments on the stack starting from the last argument. Doesn't this mean that the arguments are evaluated from the rightmost one to the leftmost by the caller? Then `printf` should process them from left to right starting from the format string which would also tell it how many arguments were passed to it. – ajay Feb 20 '14 at 16:58
  • 1
    @ajay: The reason is (I presume from experience) that C was designed to serve on many platforms. Some platforms already had conventions about how arguments are passed to subroutines, and it was more convenient for them to continuing doing things the way they were. So the C specification was designed to allow flexibility. It is also useful to allow flexibility because it permits optimizations. – Eric Postpischil Feb 20 '14 at 17:07
  • 1
    @ajay: It is **not** generally true that the caller pushes arguments on the stack starting from the last argument. Even on platforms which specify that the last argument is passed in the highest address on the stack, that does not mean that the caller has to compute them in that order. The caller can prepare space on the stack and then fill it in in any order that is useful. Other orders are useful when the arguments are complicated expressions that have sub-parts that might have been computed previously. The code can then combine the sub-parts in whatever ways are convenient. – Eric Postpischil Feb 20 '14 at 17:08
  • So arguments are laid on the stack by the caller from the last one to the first does not mean they are necessarily evaluated in that order. Subtle! This explains things now. Thank you for clearing it up :) – ajay Feb 20 '14 at 17:18
1

This invokes undefined behavior. You may get either expected or unexpected results or program crash or segmentation fault, etc.
The order of evaluation of arguments of printf is not define. They can be evaluated in any order and causing p to modify more than once within two sequence points. This invokes UB.
c-faq: 3.8:

Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored.

haccks
  • 104,019
  • 25
  • 176
  • 264
  • Really _anything_ could happen? – Aki Suihkonen Feb 20 '14 at 16:13
  • @EricPostpischil; OK. Now see the edit. – haccks Feb 20 '14 at 16:13
  • 2
    (Anything within the limits of what a computer program can do, anyhow, i.e., if it is possible to set the computer on fire programmatically it may, in theory, happen, but luckily this is seldom the case.) – Arkku Feb 20 '14 at 16:14
  • 3
    (a) The reason the behavior is undefined is because the C standard says that the behavior is not defined when there are two unsequenced side effects to an object, in clause 6.5, paragraph 2. (b) Aki Suihkonen’s point is that it is not true that **anything** can happen. The proper statement is that the C standard does not impose any requirements on the behavior. Requirements are still imposed by the operating system, the processor manufacturer, safety laws, and the laws of physics. – Eric Postpischil Feb 20 '14 at 16:19
  • @EricPostpischil; This reason is in C11 standard. My reason is still valid in C99. – haccks Feb 20 '14 at 16:21
  • @haccks: The problem is not (primarily) C 1999 or C 2011. It is that, until the latest edit (and, to some extent, still now), you did not have an unbroken chain: This is your code, these are the rules that apply to it, this is the consequence of the rules. – Eric Postpischil Feb 20 '14 at 17:03
-1

The order of evaluation is undefined, so you should not rely on side effects in one parameter being used to derive the value of another parameter.

In your example it looks like the parameters are evaluated right to left, which mirrors the calling convention of the printf function whereby the first parameter is on the top of the stack. Methods like printf which support variable length argument lists take advantage of this, as the compiler will emit infomration which will allow the function to walk the argument list in left-to-right order.

C supports many different calling conventions. The C calling convention pushes arguments from right to left, but the Pascal calling convention goes from left to right. Other put some parameters into registers and pass the rest on the stack. By not enforcing an order of evaluation the language give greater flexibility to compilers when handling these calling conventions.

Sean
  • 60,939
  • 11
  • 97
  • 136
  • Why do people repeat the whole "vararg functions" and stack bullshit? 1. Varargs functions in most popular ABIs get their arguments exactly the same way as normal functions. 2. Many modern architectures don't even pass the first few arguments on the stack. – Art Feb 20 '14 at 16:25
  • @Art - how arguments are passed has nothing to do with the architecture. It's all down to the calling convention. It you're using the C calling convention then everything will go on the stack, as that's the rule. If you use `__fastcall`, for example, then the first two will go into registers, but once again that has nothing to do with cpu architecture. – Sean Feb 20 '14 at 16:28
  • @Art - also, variable argument methods in C do not get their arguments like normal functions. The compiler pushes the number of arguments onto the stack as a hidden parameter which is retreived by `va_start`. This isn't done for non variable length argument functions. – Sean Feb 20 '14 at 16:33
  • I said "ABI", I'm aware of the difference between CPUs and calling convention. The "architecture" slipped in there, not as a reference to a CPU, but the system architecture. Sorry. I do know that on all 6 different operating systems I've recently worked with arguments are not passed on the stack. Order of argument evaluation is unrelated to the stack. It might be on old ABIs with old compilers, but it isn't on anything modern. If someone is unclear about undefined behavior the worst thing you can do is to confuse him with outdated and irrelevant information. – Art Feb 20 '14 at 16:39
  • It's hardly outdated or irrelevant. Maybe instead of ranting in the comments you could share your vast knowledge in the form of an actual answer. I'd be interested to know which architectures don't pass any arguments on the stack. Just try to avoid swearing in the answer :-) – Sean Feb 20 '14 at 16:43
  • On the systems I work on passing arguments to varargs functions is exactly the same as normal functions. This is why it works to call printf without including stdio.h or in some other way having its prototype. – Art Feb 20 '14 at 16:44
  • That's interesting. Which systems are these? – Sean Feb 20 '14 at 16:45
  • MacOS,Linux,*BSD on x86_64, sparc, sparc64, powerpc, arm. Those are the ones I remember from the top of my head. They pass the initial few arguments in registers. – Art Feb 20 '14 at 16:50
  • Thanks. However, thats an implementation issue and in effect the registers are being used as a stack (RAX then RCX then RDX etc). Also, if the parameters passed won't fit into a register then they'll go on the stack, so it's best to think about it as a stack model, even when it may not always be. – Sean Feb 20 '14 at 17:03