Function Argument Evaluation Order in C – x, x++ Is Undefined

In what order does C evaluate function arguments? The honest answer: whatever order the compiler likes — the standard leaves argument evaluation order unspecified. That alone is survivable. But combine it with a side effect, as in printf("%d %d", x, x++), and you cross into undefined behavior. We ran this exact code — from two questions in our C Programming Quiz App — on gcc and clang, and got different output from each compiler. Here’s the proof, and the rules behind it.

The Quiz Questions

Question 1:

int x = 5;
printf("%d %d", x, x++);
  1. 5 5
  2. 6 5
  3. 5 6
  4. Undefined behavior

Question 2 — same trap, pre-increment flavor:

int a = 5;
printf("%d %d", a, ++a);
  1. 5 6
  2. 6 6
  3. 5 5
  4. Undefined behavior

The Correct Answer to Both: Undefined Behavior

Two rules combine here:

  1. Argument evaluation order is unspecified. C never promises left-to-right. A compiler may evaluate x++ before x, or after, or interleave them.
  2. The side effect is unsequenced. One argument modifies x while another reads it, with no sequence point between them. Per C11 §6.5p2, an unsequenced side effect plus a use of the same object is undefined behavior — not merely “one of two possible outputs”.

Don’t take the standard’s word for it — take the compilers’. Same source file, real runs:

$ gcc -Wall quiz1.c        # gcc 13.3, Ubuntu 24.04
quiz1.c:5:27: warning: operation on 'x' may be undefined [-Wsequence-point]
$ ./quiz1
6 5

$ clang -Wall quiz1.c      # Apple clang 21, macOS
quiz1.c:5:27: warning: unsequenced modification and access to 'x' [-Wunsequenced]
$ ./quiz1
5 5

gcc printed 6 5; clang printed 5 5. The pre-increment version splits the same way: gcc prints 6 6, clang prints 5 6. One program, two mainstream compilers, two different answers — the definition of code you can’t ship.

Why Each Wrong Answer Is Wrong

Why not “5 5”?

This assumes left-to-right evaluation: read x (5), then x++ yields 5. That’s exactly what clang did — and exactly what gcc didn’t. An answer that depends on which compiler you happen to use isn’t an answer.

Why not “6 5”?

This assumes right-to-left: x++ first (yields 5, x becomes 6), then read x (6). gcc’s observed behavior — historically common because arguments were pushed onto the stack right-to-left. Still just an implementation accident, contradicted by clang on the very same code.

Why not “5 6”?

For the first question this trace requires reading x before the increment but printing the incremented value second — plausible only by misreading what x++ yields (it yields the old value). For the pre-increment version, “5 6” is clang’s real output (++a yields the new value, 6) — but again, gcc disagrees, printing “6 6”. Whenever two conforming compilers disagree, the question has no defined answer.

Unspecified vs. Undefined — the Distinction That Matters

This question is a two-layer trap, and the layers are worth separating:

  • Unspecified behavior: the standard offers a set of valid options and doesn’t say which one you get. Argument evaluation order by itself is unspecified — printf("%d %d", f(), g()) may call f or g first, but each call happens exactly once and the program is still correct.
  • Undefined behavior: all bets off. The moment one argument modifies an object another argument readsx and x++ — you’re not choosing between “5 5” and “6 5” anymore. The standard permits anything, including outputs matching neither trace.

The fix is the same as for every sequencing bug — do the modification in its own statement:

int old = x;
x++;
printf("%d %d", x, old);   /* well-defined on every compiler: 6 5 */

How to Catch This Bug

gcc -Wall file.c       # warning: operation on 'x' may be undefined [-Wsequence-point]
clang -Wall file.c     # warning: unsequenced modification and access to 'x' [-Wunsequenced]

Both compilers flag it at compile time with standard warnings. If your build treats warnings as errors (-Werror), this bug can’t even compile — the cheapest possible fix.

Frequently Asked Questions

Does C evaluate function arguments left to right?

No — the order is unspecified. clang typically evaluates left-to-right, gcc typically right-to-left, and both are conforming. Never write code whose meaning depends on argument evaluation order.

Is printf(“%d %d”, x, x++) just compiler-dependent?

It’s worse than that: it’s undefined behavior, because x++ modifies x while another argument reads it, unsequenced. “Compiler-dependent” would mean each compiler gives you one of the valid answers. Undefined means there is no valid answer.

Is f(g(), h()) also a problem?

Not by itself. The order of calling g and h is unspecified, but function calls don’t overlap, so unless g and h touch the same data, the program is fine. The undefined-behavior line is crossed when arguments have unsequenced side effects on the same object.

Related Reading

Recommended Books

These are questions #113 and #145 in the C Programming Quiz App — 155 questions with explanations covering operators, pointers, memory, and more.
Download on Google Play →

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>