7

I am learning C so I tried the below code and am getting an output of 7,6 instead of 6,7. Why?

#include <stdio.h>
int f1(int);
void main()
{
    int b = 5;
    printf("%d,%d", f1(b), f1(b));
}
int f1(int b)
{
    static int n = 5;
    n++;
    return n;
}
Boann
  • 48,794
  • 16
  • 117
  • 146

2 Answers2

13

The order of the evaluation of the function arguments is unspecified in C. (Note there's no undefined behaviour here; the arguments are not allowed to be evaluated concurrently for example.)

Typically the evaluation of the arguments is either from right to left, or from left to right.

As a rule of thumb don't call the same function twice in a function parameter list if that function has side-effects (as it does in your case), or if you pass the same parameter twice which allows something in the calling site to be modified (e.g. passing a pointer).

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • Thanks for your reply can i find more detail article on this topic? – Sanket Wankhede Jun 12 '19 at 09:53
  • 1
    Just Google some of the terms I've mentioned in the answer. – Bathsheba Jun 12 '19 at 09:53
  • This is the very definition of undefined behavior. Undefined behavior is something that is not specified (defined) by the standard. Each compiler can choose how to process. –  Jun 12 '19 at 09:56
  • @Nico238: I'm not sure about that. Why not answer along those lines and see how the community reacts to it? – Bathsheba Jun 12 '19 at 09:57
  • @Bathsheba Because your answer is correct : the order of evaluation is unspecified, and that is what is called undefined behavior. –  Jun 12 '19 at 10:01
  • @Nico238; Without this comment stream starting to sound like the script of a poor Christmas pantomime, "Oh no they're not.". Perhaps ask a question along these lines yourself? I'd upvote it. – Bathsheba Jun 12 '19 at 10:01
  • 8
    @Nico238 "3.4.4 **1 unspecified behavior** use of an unspecified value, or other behavior where this International Standard provides two or more possibilities and imposes no further requirements on which is chosen in any instance. **2 EXAMPLE** An example of unspecified behavior is the order in which the arguments to a function are evaluated." As distinct from "3.4.3 **1 undefined behavior**". – Weather Vane Jun 12 '19 at 10:26
  • 3
    @Nico238: The C standard explicitly defines “undefined behavior” to mean behavior upon which the standard imposes **no** requirements. If, in some situation, the behavior imposes partial but not total requirements, such as requiring evaluations be performed separately (not interleaved at all) but in any order, that is not undefined behavior as the standard defines it. – Eric Postpischil Jun 12 '19 at 10:59
  • Historically, on Intel, parameters to a function were pushed right-to-left, thus implying right-to-left evaluation. This was (is?) used by the `vararg` macros to get the next parameter of a parameter list whose length is a priori not known. – Paul Ogilvie Jun 12 '19 at 11:08
  • @SanketWankhede This might be helpful: [What is the difference between a sequence point and operator precedence?](https://stackoverflow.com/questions/44770170/what-is-the-difference-between-a-sequence-point-and-operator-precedence) – Lundin Jun 12 '19 at 11:09
  • 3
    @Nico238 Undefined behavior and unspecified behavior are both formal terms and they are different. [Undefined, unspecified and implementation-defined behavior](https://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior) – Lundin Jun 12 '19 at 11:17
  • @PaulOgilvie The evaluation order does not necessarily always match the order in which arguments are pushed on the stack, even if a non-optimizing compiler would naturally follow it. For instance, GCC and Clang do not evaluate the arguments in the same order even when targeting the very same historical Intel architecture you were talking about: https://gcc.godbolt.org/z/6l75w1 – Pascal Cuoq Jun 12 '19 at 12:11
  • @PascalCuoq, with "historically" I meant some 20-30 years ago. Only later the standard came along and defined this to be undefined. – Paul Ogilvie Jun 12 '19 at 12:38
0

https://en.cppreference.com/w/c/language/eval_order

Before C11, you must follow Rule (2)

There is a sequence point after evaluation of the first (left) operand and 
before evaluation of the second (right) operand of the following binary 
operators: && (logical AND), || (logical OR), and , (comma).

Because arguments are considered separated by comma operator before C11. This is not optimal because arguments are pushed right to left on some platform. Thus, C11 adds Rule (12) making it unspecified.

A function call that is not sequenced before or sequenced after another 
function call is indeterminately sequenced (CPU instructions that 
constitute different function calls cannot be interleaved, even if the 
functions are inlined)

Even C99 designated initializers still go back to Rule (2), where earlier (left) initializers are resolved before later (right) initializers relative to the comma operator. That is, until C11 adds Rule (13) making it unspecified.

In initialization list expressions, all evaluations are indeterminately 
sequenced

In other words, before Rule (12) and Rule (13), the comma operator from Rule (2) is the specified behavior. Rule (2) leads to inefficient code that cannot be optimized on some platform. There is not enough registers if the number of structure member or function parameter exceed some threshold. That is, "Register Pressure" becomes an issue.

Historically, aggregate type initializers and function arguments falls back to the comma operator. In C11, they specifically add the definition that commas in those aggregate type initializers and function arguments are not "comma operators" so that Rule (12) and Rule (13) makes sense, and that Rule (2) is not applied.

Abraham Le
  • 114
  • 3