1

In the code mentioned below sum1 variable is getting 46 as the answer when operators precedence left to right.But in sum2 answer is getting as 48 and it's precedence is right to left. Why those answers are getting different.

#include <stdio.h>

int func(int *k){
    *k+=4;
    return 3 * (*k)-1;
}

void main() {
    int i = 10, j = 10, sum1, sum2;
    sum1 = (i / 2) + func(&i);
    sum2 = func(&j)+(j/2);
    printf("%d\n",sum1);
    printf("%d",sum2);
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 1
    This is known as [unspecified behavior](https://stackoverflow.com/questions/2397984). The compiler is free to evaluate the operands to `+` in any order it pleases. So it can call the function first, or do the division first. – user3386109 Jan 31 '20 at 18:40
  • This question is relevant: https://stackoverflow.com/questions/18516510/sequence-point-from-function-call – Andrew Henle Jan 31 '20 at 18:40
  • 1
    This question is also relevant, even more so than the one above: [**In C99, is f()+g() undefined or merely unspecified?**](https://stackoverflow.com/questions/3951017/in-c99-is-fg-undefined-or-merely-unspecified) – Andrew Henle Jan 31 '20 at 18:47
  • @AndrewHenle Right, it's definitely unspecified behavior. Whether or not it's undefined is debatable, but moot, given that code with unspecified behavior is not useful (it can easily be refactored so that the behavior is fully specified). – user3386109 Jan 31 '20 at 18:47

3 Answers3

1

In the expression (i / 2) + func(&i), the compiler (or the C implementation generally) is free to evaluate either i / 2 first or func(&i) first. Similarly, in func(&j) + (j/2), the compiler is free to evaluate func(&j) or j/2 first.

Precedence is irrelevant. Precedence tells us how an expression is structured, but it does not fully determine the order in which it is evaluated. Precedence tells us that, in a * b + c * d, the structure must be (a * b) + (c * d). In a + b + c, precedence, in the form of left-to-right association for +, tells us the structure must be (a + b) + c. It does not tell that a must be evaluated before c. For example, in a() + b() + c(), the structure is (a() + b()) + c(), but the compiler may call the functions in any order, holding their results in temporary registers if needed, and then add the results.

In func(&j)+(j/2), there is no right-to-left precedence or association. No rule in the C standard says j/2 must be evaluated before func(&j).

A compiler might tend to evaluate subexpressions from left to right, in the absence of other constraints. However, various factors may alter that. For example, if one subexpression appears multiple times, the compiler might evaluate it early and retain its value for reuse. Essentially, the compiler builds a tree structure describing the expressions it needs to evaluate and then seeks optimal ways to evaluate them. It does not necessarily proceed left to right, and you cannot rely on any particular evaluation order.

Questions about Sequencing

The C standard has a rule, in C 2018 6.5 2, that says if a modification to an object, as occurs to i in the statement *k+=4;, is unsequenced relative to a value computation using the same object, as occurs for i in i / 2, then the behavior is undefined. However, this problem does not occur in this code because the modification and the value computation are indeterminately sequenced, not unsequenced: 6.5.2.2 10 says “… Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to execution of the called function.” C 5.1.2.3 3 says “… Evaluations A and B are indeterminately sequenced when A is sequenced either before or after B, but it is unspecified which…”

Community
  • 1
  • 1
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
-1

The program has undefined behavior because the order of evaluation of operands in an additive operator is unspecified and such evaluations of operands are unsequenced.

Pay attention to that within the assignment expression the variables i and j are being changed and these changes are not sequenced. Either i / 2 or j / 2 can be evaluated before the function call or vice versa.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/207008/discussion-on-answer-by-vlad-from-moscow-why-sum1-46-and-sum2-48). – Bhargav Rao Feb 01 '20 at 01:55
-1

The & preceding the variable name (&i or &j) is sending a pointer meaning that any changes to the variable are saved. In this case, the func call takes a variable and adds 4 to it then returns some value based on that variable. The function still changed the variable's value and since the equation is being processed left to right, each subsequent use of the variable has that change reflected.

sum1 = (i/2) +func(&i) (where i = 10)
--> sum1 = 5 + func(&i)
--> sum1 = 5 + 41 (and now i = 14)

sum2 = func(&j) + (j/2) (where j = 10)
--> sum2 = 41 + (j/2) (and now j = 14)
--> sum2 = 41 + 7