Can it be assumed a evaluation order of the function parameters when calling it in C ? According to the following program, it seems that there is not a particular order when I executed it.
#include <stdio.h>
int main()
{
int a[] = {1, 2, 3};
int * pa;
pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
/* Result: a[0] = 3 a[1] = 2 a[2] = 2 */
pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(pa),*(++pa));
/* Result: a[0] = 2 a[1] = 2 a[2] = 2 */
pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(++pa), *(pa));
/* a[0] = 2 a[1] = 2 a[2] = 1 */
}
No, function parameters are not evaluated in a defined order in C.
See Martin York's answers to What are all the common undefined behaviour that c++ programmer should know about?.
Order of evaluation of function arguments is unspecified, from C99 §6.5.2.2p10:
The order of evaluation of the
function designator, the actual
arguments, and subexpressions within
the actual arguments is unspecified,
but there is a sequence point before
the actual call.
Similar wording exists in C89.
Additionally, you are modifying pa multiple times without intervening sequence points which invokes undefined behavior (the comma operator introduces a sequence point but the commas delimiting the function arguments do not). If you turn up the warnings on your compiler it should warn you about this:
$ gcc -Wall -W -ansi -pedantic test.c -o test
test.c: In function ‘main’:
test.c:9: warning: operation on ‘pa’ may be undefined
test.c:9: warning: operation on ‘pa’ may be undefined
test.c:13: warning: operation on ‘pa’ may be undefined
test.c:13: warning: operation on ‘pa’ may be undefined
test.c:17: warning: operation on ‘pa’ may be undefined
test.c:17: warning: operation on ‘pa’ may be undefined
test.c:20: warning: control reaches end of non-void function
Just to add some experiences.
The following code:
int i=1;
printf("%d %d %d\n", i++, i++, i);
results in
2 1 3 - using g++ 4.2.1 on Linux.i686
1 2 3 - using SunStudio C++ 5.9 on Linux.i686
2 1 3 - using g++ 4.2.1 on SunOS.x86pc
1 2 3 - using SunStudio C++ 5.9 on SunOS.x86pc
1 2 3 - using g++ 4.2.1 on SunOS.sun4u
1 2 3 - using SunStudio C++ 5.9 on SunOS.sun4u
Can it be assumed a evaluation order of the function parameters when calling it in C ?
No, it can not be assumed if, it is unspecified behavior, the draft C99 standard in section6.5 paragraph 3 says:
The grouping of operators and operands is indicated by the syntax.74) Except as specified
later (for the function-call (), &&, ||, ?:, and comma operators), the order of evaluation of subexpressions and the order in which side effects take place are both unspecified.
It also says except as specified later and specifically sites function-call (), so we see that later on the draft standard in section 6.5.2.2 Function calls paragraph 10 says:
The order of evaluation of the function designator, the actual arguments, and
subexpressions within the actual arguments is unspecified, but there is a sequence point
before the actual call.
This program also exhibits undefined behavior since you are modifying pa more than once between sequence points. From draft standard section 6.5 paragraph 2:
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 read only to determine the value to be stored.
it cites the following code examples as being undefined:
i = ++i + 1;
a[i++] = i;
Important to note that although the comma operator does introduce sequence points, the comma used in function calls is a separator and not the comma operator. If we look at section 6.5.17 Comma operator paragraph 2 says:
The left operand of a comma operator is evaluated as a void expression; there is a
sequence point after its evaluation.
but paragraph 3 says:
EXAMPLE As indicated by the syntax, the comma operator (as described in this subclause) cannot appear in contexts where a comma is used to separate items in a list (such as arguments to functions or lists of initializers).
Without knowing this, having warnings turned on with gcc using at least -Wall would have provided a message similar to:
warning: operation on 'pa' may be undefined [-Wsequence-point]
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
^
and by default clang will warn with a message similar to:
warning: unsequenced modification and access to 'pa' [-Wunsequenced]
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
~ ^
In general it is important to understand how to use your tools in the most effective way, getting to know the flags available for warnings is important, for gcc you can find that information here. Some flags that are useful and will save you a lot of trouble in the long run and are common to both gcc and clang are -Wextra -Wconversion -pedantic. For clang understanding -fsanitize can be very helpful. For example -fsanitize=undefined will catch many instances of undefined behavior at runtime.
As others already said, the order in which function arguments are evaluated is unspecified, and there is no sequence point between evaluating them. Because you change pa subsequently while passing each argument, you change and read pa twice in between two sequence points. That's actually undefined behavior. I found a very nice explanation in the GCC manual, which i think might be helpful:
The C and C++ standards defines the order in which expressions in a C/C++ program are evaluated in terms of sequence points, which represent a partial ordering between the execution of parts of the program: those executed before the sequence point, and those executed after it. These occur after the evaluation of a full expression (one which is not part of a larger expression), after the evaluation of the first operand of a &&, ||, ? : or , (comma) operator, before a function is called (but after the evaluation of its arguments and the expression denoting the called function), and in certain other places. Other than as expressed by the sequence point rules, the order of evaluation of subexpressions of an expression is not specified. All these rules describe only a partial order rather than a total order, since, for example, if two functions are called within one expression with no sequence point between them, the order in which the functions are called is not specified. However, the standards committee have ruled that function calls do not overlap.
It is not specified when between sequence points modifications to the values of objects take effect. Programs whose behavior depends on this have undefined behavior; the C and C++ standards specify that “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 read only to determine the value to be stored.”. If a program breaks these rules, the results on any particular implementation are entirely unpredictable.
Examples of code with undefined behavior are a = a++;, a[n] = b[n++] and a[i++] = i;. Some more complicated cases are not diagnosed by this option, and it may give an occasional false positive result, but in general it has been found fairly effective at detecting this sort of problem in programs.
The standard is worded confusingly, therefore there is some debate over the precise meaning of the sequence point rules in subtle cases. Links to discussions of the problem, including proposed formal definitions, may be found on the GCC readings page, at http://gcc.gnu.org/readings.html.
Modifying a variable more than once in a expression is undefined behavior.
So you might get different results on different compilers. So avoid modifying a variable more than once.
Grant's answer is correct, it's undefined.
BUT,,,
By your example, your compiler seems to be evaluating in right-to-left order (unsurprisingly, the order that arguments are pushed onto the stack). If you can do other tests to show that the order is maintained consistently even with optimizations enabled, and if you're only going to stick with that one version of the compiler, you can safely assume right-to-left ordering.
It's totally non-portable and a horrible, horrible thing to do, though.
Related
Can it be assumed a evaluation order of the function parameters when calling it in C ? According to the following program, it seems that there is not a particular order when I executed it.
#include <stdio.h>
int main()
{
int a[] = {1, 2, 3};
int * pa;
pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
/* Result: a[0] = 3 a[1] = 2 a[2] = 2 */
pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(pa),*(++pa));
/* Result: a[0] = 2 a[1] = 2 a[2] = 2 */
pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(++pa), *(pa));
/* a[0] = 2 a[1] = 2 a[2] = 1 */
}
No, function parameters are not evaluated in a defined order in C.
See Martin York's answers to What are all the common undefined behaviour that c++ programmer should know about?.
Order of evaluation of function arguments is unspecified, from C99 §6.5.2.2p10:
The order of evaluation of the
function designator, the actual
arguments, and subexpressions within
the actual arguments is unspecified,
but there is a sequence point before
the actual call.
Similar wording exists in C89.
Additionally, you are modifying pa multiple times without intervening sequence points which invokes undefined behavior (the comma operator introduces a sequence point but the commas delimiting the function arguments do not). If you turn up the warnings on your compiler it should warn you about this:
$ gcc -Wall -W -ansi -pedantic test.c -o test
test.c: In function ‘main’:
test.c:9: warning: operation on ‘pa’ may be undefined
test.c:9: warning: operation on ‘pa’ may be undefined
test.c:13: warning: operation on ‘pa’ may be undefined
test.c:13: warning: operation on ‘pa’ may be undefined
test.c:17: warning: operation on ‘pa’ may be undefined
test.c:17: warning: operation on ‘pa’ may be undefined
test.c:20: warning: control reaches end of non-void function
Just to add some experiences.
The following code:
int i=1;
printf("%d %d %d\n", i++, i++, i);
results in
2 1 3 - using g++ 4.2.1 on Linux.i686
1 2 3 - using SunStudio C++ 5.9 on Linux.i686
2 1 3 - using g++ 4.2.1 on SunOS.x86pc
1 2 3 - using SunStudio C++ 5.9 on SunOS.x86pc
1 2 3 - using g++ 4.2.1 on SunOS.sun4u
1 2 3 - using SunStudio C++ 5.9 on SunOS.sun4u
Can it be assumed a evaluation order of the function parameters when calling it in C ?
No, it can not be assumed if, it is unspecified behavior, the draft C99 standard in section6.5 paragraph 3 says:
The grouping of operators and operands is indicated by the syntax.74) Except as specified
later (for the function-call (), &&, ||, ?:, and comma operators), the order of evaluation of subexpressions and the order in which side effects take place are both unspecified.
It also says except as specified later and specifically sites function-call (), so we see that later on the draft standard in section 6.5.2.2 Function calls paragraph 10 says:
The order of evaluation of the function designator, the actual arguments, and
subexpressions within the actual arguments is unspecified, but there is a sequence point
before the actual call.
This program also exhibits undefined behavior since you are modifying pa more than once between sequence points. From draft standard section 6.5 paragraph 2:
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 read only to determine the value to be stored.
it cites the following code examples as being undefined:
i = ++i + 1;
a[i++] = i;
Important to note that although the comma operator does introduce sequence points, the comma used in function calls is a separator and not the comma operator. If we look at section 6.5.17 Comma operator paragraph 2 says:
The left operand of a comma operator is evaluated as a void expression; there is a
sequence point after its evaluation.
but paragraph 3 says:
EXAMPLE As indicated by the syntax, the comma operator (as described in this subclause) cannot appear in contexts where a comma is used to separate items in a list (such as arguments to functions or lists of initializers).
Without knowing this, having warnings turned on with gcc using at least -Wall would have provided a message similar to:
warning: operation on 'pa' may be undefined [-Wsequence-point]
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
^
and by default clang will warn with a message similar to:
warning: unsequenced modification and access to 'pa' [-Wunsequenced]
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
~ ^
In general it is important to understand how to use your tools in the most effective way, getting to know the flags available for warnings is important, for gcc you can find that information here. Some flags that are useful and will save you a lot of trouble in the long run and are common to both gcc and clang are -Wextra -Wconversion -pedantic. For clang understanding -fsanitize can be very helpful. For example -fsanitize=undefined will catch many instances of undefined behavior at runtime.
As others already said, the order in which function arguments are evaluated is unspecified, and there is no sequence point between evaluating them. Because you change pa subsequently while passing each argument, you change and read pa twice in between two sequence points. That's actually undefined behavior. I found a very nice explanation in the GCC manual, which i think might be helpful:
The C and C++ standards defines the order in which expressions in a C/C++ program are evaluated in terms of sequence points, which represent a partial ordering between the execution of parts of the program: those executed before the sequence point, and those executed after it. These occur after the evaluation of a full expression (one which is not part of a larger expression), after the evaluation of the first operand of a &&, ||, ? : or , (comma) operator, before a function is called (but after the evaluation of its arguments and the expression denoting the called function), and in certain other places. Other than as expressed by the sequence point rules, the order of evaluation of subexpressions of an expression is not specified. All these rules describe only a partial order rather than a total order, since, for example, if two functions are called within one expression with no sequence point between them, the order in which the functions are called is not specified. However, the standards committee have ruled that function calls do not overlap.
It is not specified when between sequence points modifications to the values of objects take effect. Programs whose behavior depends on this have undefined behavior; the C and C++ standards specify that “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 read only to determine the value to be stored.”. If a program breaks these rules, the results on any particular implementation are entirely unpredictable.
Examples of code with undefined behavior are a = a++;, a[n] = b[n++] and a[i++] = i;. Some more complicated cases are not diagnosed by this option, and it may give an occasional false positive result, but in general it has been found fairly effective at detecting this sort of problem in programs.
The standard is worded confusingly, therefore there is some debate over the precise meaning of the sequence point rules in subtle cases. Links to discussions of the problem, including proposed formal definitions, may be found on the GCC readings page, at http://gcc.gnu.org/readings.html.
Modifying a variable more than once in a expression is undefined behavior.
So you might get different results on different compilers. So avoid modifying a variable more than once.
Grant's answer is correct, it's undefined.
BUT,,,
By your example, your compiler seems to be evaluating in right-to-left order (unsurprisingly, the order that arguments are pushed onto the stack). If you can do other tests to show that the order is maintained consistently even with optimizations enabled, and if you're only going to stick with that one version of the compiler, you can safely assume right-to-left ordering.
It's totally non-portable and a horrible, horrible thing to do, though.
Can it be assumed a evaluation order of the function parameters when calling it in C ? According to the following program, it seems that there is not a particular order when I executed it.
#include <stdio.h>
int main()
{
int a[] = {1, 2, 3};
int * pa;
pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
/* Result: a[0] = 3 a[1] = 2 a[2] = 2 */
pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(pa),*(++pa));
/* Result: a[0] = 2 a[1] = 2 a[2] = 2 */
pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(++pa), *(pa));
/* a[0] = 2 a[1] = 2 a[2] = 1 */
}
No, function parameters are not evaluated in a defined order in C.
See Martin York's answers to What are all the common undefined behaviour that c++ programmer should know about?.
Order of evaluation of function arguments is unspecified, from C99 §6.5.2.2p10:
The order of evaluation of the
function designator, the actual
arguments, and subexpressions within
the actual arguments is unspecified,
but there is a sequence point before
the actual call.
Similar wording exists in C89.
Additionally, you are modifying pa multiple times without intervening sequence points which invokes undefined behavior (the comma operator introduces a sequence point but the commas delimiting the function arguments do not). If you turn up the warnings on your compiler it should warn you about this:
$ gcc -Wall -W -ansi -pedantic test.c -o test
test.c: In function ‘main’:
test.c:9: warning: operation on ‘pa’ may be undefined
test.c:9: warning: operation on ‘pa’ may be undefined
test.c:13: warning: operation on ‘pa’ may be undefined
test.c:13: warning: operation on ‘pa’ may be undefined
test.c:17: warning: operation on ‘pa’ may be undefined
test.c:17: warning: operation on ‘pa’ may be undefined
test.c:20: warning: control reaches end of non-void function
Just to add some experiences.
The following code:
int i=1;
printf("%d %d %d\n", i++, i++, i);
results in
2 1 3 - using g++ 4.2.1 on Linux.i686
1 2 3 - using SunStudio C++ 5.9 on Linux.i686
2 1 3 - using g++ 4.2.1 on SunOS.x86pc
1 2 3 - using SunStudio C++ 5.9 on SunOS.x86pc
1 2 3 - using g++ 4.2.1 on SunOS.sun4u
1 2 3 - using SunStudio C++ 5.9 on SunOS.sun4u
Can it be assumed a evaluation order of the function parameters when calling it in C ?
No, it can not be assumed if, it is unspecified behavior, the draft C99 standard in section6.5 paragraph 3 says:
The grouping of operators and operands is indicated by the syntax.74) Except as specified
later (for the function-call (), &&, ||, ?:, and comma operators), the order of evaluation of subexpressions and the order in which side effects take place are both unspecified.
It also says except as specified later and specifically sites function-call (), so we see that later on the draft standard in section 6.5.2.2 Function calls paragraph 10 says:
The order of evaluation of the function designator, the actual arguments, and
subexpressions within the actual arguments is unspecified, but there is a sequence point
before the actual call.
This program also exhibits undefined behavior since you are modifying pa more than once between sequence points. From draft standard section 6.5 paragraph 2:
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 read only to determine the value to be stored.
it cites the following code examples as being undefined:
i = ++i + 1;
a[i++] = i;
Important to note that although the comma operator does introduce sequence points, the comma used in function calls is a separator and not the comma operator. If we look at section 6.5.17 Comma operator paragraph 2 says:
The left operand of a comma operator is evaluated as a void expression; there is a
sequence point after its evaluation.
but paragraph 3 says:
EXAMPLE As indicated by the syntax, the comma operator (as described in this subclause) cannot appear in contexts where a comma is used to separate items in a list (such as arguments to functions or lists of initializers).
Without knowing this, having warnings turned on with gcc using at least -Wall would have provided a message similar to:
warning: operation on 'pa' may be undefined [-Wsequence-point]
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
^
and by default clang will warn with a message similar to:
warning: unsequenced modification and access to 'pa' [-Wunsequenced]
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
~ ^
In general it is important to understand how to use your tools in the most effective way, getting to know the flags available for warnings is important, for gcc you can find that information here. Some flags that are useful and will save you a lot of trouble in the long run and are common to both gcc and clang are -Wextra -Wconversion -pedantic. For clang understanding -fsanitize can be very helpful. For example -fsanitize=undefined will catch many instances of undefined behavior at runtime.
As others already said, the order in which function arguments are evaluated is unspecified, and there is no sequence point between evaluating them. Because you change pa subsequently while passing each argument, you change and read pa twice in between two sequence points. That's actually undefined behavior. I found a very nice explanation in the GCC manual, which i think might be helpful:
The C and C++ standards defines the order in which expressions in a C/C++ program are evaluated in terms of sequence points, which represent a partial ordering between the execution of parts of the program: those executed before the sequence point, and those executed after it. These occur after the evaluation of a full expression (one which is not part of a larger expression), after the evaluation of the first operand of a &&, ||, ? : or , (comma) operator, before a function is called (but after the evaluation of its arguments and the expression denoting the called function), and in certain other places. Other than as expressed by the sequence point rules, the order of evaluation of subexpressions of an expression is not specified. All these rules describe only a partial order rather than a total order, since, for example, if two functions are called within one expression with no sequence point between them, the order in which the functions are called is not specified. However, the standards committee have ruled that function calls do not overlap.
It is not specified when between sequence points modifications to the values of objects take effect. Programs whose behavior depends on this have undefined behavior; the C and C++ standards specify that “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 read only to determine the value to be stored.”. If a program breaks these rules, the results on any particular implementation are entirely unpredictable.
Examples of code with undefined behavior are a = a++;, a[n] = b[n++] and a[i++] = i;. Some more complicated cases are not diagnosed by this option, and it may give an occasional false positive result, but in general it has been found fairly effective at detecting this sort of problem in programs.
The standard is worded confusingly, therefore there is some debate over the precise meaning of the sequence point rules in subtle cases. Links to discussions of the problem, including proposed formal definitions, may be found on the GCC readings page, at http://gcc.gnu.org/readings.html.
Modifying a variable more than once in a expression is undefined behavior.
So you might get different results on different compilers. So avoid modifying a variable more than once.
Grant's answer is correct, it's undefined.
BUT,,,
By your example, your compiler seems to be evaluating in right-to-left order (unsurprisingly, the order that arguments are pushed onto the stack). If you can do other tests to show that the order is maintained consistently even with optimizations enabled, and if you're only going to stick with that one version of the compiler, you can safely assume right-to-left ordering.
It's totally non-portable and a horrible, horrible thing to do, though.
Can it be assumed a evaluation order of the function parameters when calling it in C ? According to the following program, it seems that there is not a particular order when I executed it.
#include <stdio.h>
int main()
{
int a[] = {1, 2, 3};
int * pa;
pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
/* Result: a[0] = 3 a[1] = 2 a[2] = 2 */
pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(pa),*(++pa));
/* Result: a[0] = 2 a[1] = 2 a[2] = 2 */
pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(++pa), *(pa));
/* a[0] = 2 a[1] = 2 a[2] = 1 */
}
No, function parameters are not evaluated in a defined order in C.
See Martin York's answers to What are all the common undefined behaviour that c++ programmer should know about?.
Order of evaluation of function arguments is unspecified, from C99 §6.5.2.2p10:
The order of evaluation of the
function designator, the actual
arguments, and subexpressions within
the actual arguments is unspecified,
but there is a sequence point before
the actual call.
Similar wording exists in C89.
Additionally, you are modifying pa multiple times without intervening sequence points which invokes undefined behavior (the comma operator introduces a sequence point but the commas delimiting the function arguments do not). If you turn up the warnings on your compiler it should warn you about this:
$ gcc -Wall -W -ansi -pedantic test.c -o test
test.c: In function ‘main’:
test.c:9: warning: operation on ‘pa’ may be undefined
test.c:9: warning: operation on ‘pa’ may be undefined
test.c:13: warning: operation on ‘pa’ may be undefined
test.c:13: warning: operation on ‘pa’ may be undefined
test.c:17: warning: operation on ‘pa’ may be undefined
test.c:17: warning: operation on ‘pa’ may be undefined
test.c:20: warning: control reaches end of non-void function
Just to add some experiences.
The following code:
int i=1;
printf("%d %d %d\n", i++, i++, i);
results in
2 1 3 - using g++ 4.2.1 on Linux.i686
1 2 3 - using SunStudio C++ 5.9 on Linux.i686
2 1 3 - using g++ 4.2.1 on SunOS.x86pc
1 2 3 - using SunStudio C++ 5.9 on SunOS.x86pc
1 2 3 - using g++ 4.2.1 on SunOS.sun4u
1 2 3 - using SunStudio C++ 5.9 on SunOS.sun4u
Can it be assumed a evaluation order of the function parameters when calling it in C ?
No, it can not be assumed if, it is unspecified behavior, the draft C99 standard in section6.5 paragraph 3 says:
The grouping of operators and operands is indicated by the syntax.74) Except as specified
later (for the function-call (), &&, ||, ?:, and comma operators), the order of evaluation of subexpressions and the order in which side effects take place are both unspecified.
It also says except as specified later and specifically sites function-call (), so we see that later on the draft standard in section 6.5.2.2 Function calls paragraph 10 says:
The order of evaluation of the function designator, the actual arguments, and
subexpressions within the actual arguments is unspecified, but there is a sequence point
before the actual call.
This program also exhibits undefined behavior since you are modifying pa more than once between sequence points. From draft standard section 6.5 paragraph 2:
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 read only to determine the value to be stored.
it cites the following code examples as being undefined:
i = ++i + 1;
a[i++] = i;
Important to note that although the comma operator does introduce sequence points, the comma used in function calls is a separator and not the comma operator. If we look at section 6.5.17 Comma operator paragraph 2 says:
The left operand of a comma operator is evaluated as a void expression; there is a
sequence point after its evaluation.
but paragraph 3 says:
EXAMPLE As indicated by the syntax, the comma operator (as described in this subclause) cannot appear in contexts where a comma is used to separate items in a list (such as arguments to functions or lists of initializers).
Without knowing this, having warnings turned on with gcc using at least -Wall would have provided a message similar to:
warning: operation on 'pa' may be undefined [-Wsequence-point]
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
^
and by default clang will warn with a message similar to:
warning: unsequenced modification and access to 'pa' [-Wunsequenced]
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
~ ^
In general it is important to understand how to use your tools in the most effective way, getting to know the flags available for warnings is important, for gcc you can find that information here. Some flags that are useful and will save you a lot of trouble in the long run and are common to both gcc and clang are -Wextra -Wconversion -pedantic. For clang understanding -fsanitize can be very helpful. For example -fsanitize=undefined will catch many instances of undefined behavior at runtime.
As others already said, the order in which function arguments are evaluated is unspecified, and there is no sequence point between evaluating them. Because you change pa subsequently while passing each argument, you change and read pa twice in between two sequence points. That's actually undefined behavior. I found a very nice explanation in the GCC manual, which i think might be helpful:
The C and C++ standards defines the order in which expressions in a C/C++ program are evaluated in terms of sequence points, which represent a partial ordering between the execution of parts of the program: those executed before the sequence point, and those executed after it. These occur after the evaluation of a full expression (one which is not part of a larger expression), after the evaluation of the first operand of a &&, ||, ? : or , (comma) operator, before a function is called (but after the evaluation of its arguments and the expression denoting the called function), and in certain other places. Other than as expressed by the sequence point rules, the order of evaluation of subexpressions of an expression is not specified. All these rules describe only a partial order rather than a total order, since, for example, if two functions are called within one expression with no sequence point between them, the order in which the functions are called is not specified. However, the standards committee have ruled that function calls do not overlap.
It is not specified when between sequence points modifications to the values of objects take effect. Programs whose behavior depends on this have undefined behavior; the C and C++ standards specify that “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 read only to determine the value to be stored.”. If a program breaks these rules, the results on any particular implementation are entirely unpredictable.
Examples of code with undefined behavior are a = a++;, a[n] = b[n++] and a[i++] = i;. Some more complicated cases are not diagnosed by this option, and it may give an occasional false positive result, but in general it has been found fairly effective at detecting this sort of problem in programs.
The standard is worded confusingly, therefore there is some debate over the precise meaning of the sequence point rules in subtle cases. Links to discussions of the problem, including proposed formal definitions, may be found on the GCC readings page, at http://gcc.gnu.org/readings.html.
Modifying a variable more than once in a expression is undefined behavior.
So you might get different results on different compilers. So avoid modifying a variable more than once.
Grant's answer is correct, it's undefined.
BUT,,,
By your example, your compiler seems to be evaluating in right-to-left order (unsurprisingly, the order that arguments are pushed onto the stack). If you can do other tests to show that the order is maintained consistently even with optimizations enabled, and if you're only going to stick with that one version of the compiler, you can safely assume right-to-left ordering.
It's totally non-portable and a horrible, horrible thing to do, though.
I recently came across a post What is the correct answer for cout << c++ << c;? and was wondering whether the output of
int c = 0;
printf ("%d %d", c++, c);
is also undefined??
I have studied in lectures that post-fix and prefix operators increment value only after getting a semicolon. So according to me, the output 0 0 is correct !!!
I have studied in lectures that post-fix and prefix operators increment value only after getting a semicolon.
Send your lecturer to me so that I can take a baseball bat to him politely point out his mistake.
Exactly when the side effect of either pre- or postfix ++ and -- is applied is unspecified, apart from the requirement that it happen before the next sequence point. In an expression like
x = a++ * b
a may be updated immediately after a++ has been evaluated, or the update may be deferred until a++ * b has been evaluated and the result assigned to x, or anywhere in between.
This is why expressions like i++ * i++ and printf("%d %d", c++, c) and a[i++] = i and a host of others are all bad juju. You will get different results based on the compiler, optimization settings, surrounding code, etc. The language standard explicitly leaves the behavior undefined so that the compiler is under no obligation to "do the right thing", whatever the right thing may be. Remember, the definition for undefined behavior is
3.4.3
1 undefined behavior
behavior, upon use of a nonportable or erroneous program construct or of erroneous data,
for which this International Standard imposes no requirements
2 NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable
results, to behaving during translation or program execution in a documented manner characteristic of the
environment (with or without the issuance of a diagnostic message), to terminating a translation or
execution (with the issuance of a diagnostic message).
3 EXAMPLE An example of undefined behavior is the behavior on integer overflow.
This is a deliberate design decision - the rationale for leaving the order of these operations unspecified is to give the implementation freedom to rearrange the evaluation order for optimization purposes. However, in exchange for this freedom, certain operations will not have well-defined results.
Note that a compiler is free to try to detect these cases and issue a diagnostic; printf("%d %d", c++, c); would be easy enough to catch, but this would be a bugger to detect in the general case. Imagine if that had been written printf("%d %d", (*p)++, c); if p points to c, then the behavior is undefined, otherwise it's okay. If p is assigned in a different translation unit, then there's no way to know at compile time whether this is a problem or not.
This concept is not difficult to understand, yet it is one of the most consistently misunderstood (and mis-taught) aspects of the C language. No doubt this is why the Java and C# language specifications force a specific evaluation order for everything (all operands are evaluated left-to-right, and all side effects are applied immediately).
I have studied in lectures that post-fix and prefix operators increment value only after getting a semicolon
This is not how the standard describes it. A sequence point is a point in code in which side effects which may have occurred in previous parts of the code have been evaluated. The comma between arguments to a function is not a sequence point, so the behavior there is undefined.
The evaluation order of function arguments is unspecified. There is no guarantee that the arguments to a function will be evaluated in the order (1, 2, N), so there is no guarantee that the increment will be evaluated before the second argument is passed.
So according to me, the output 0 0 is correct !!!
No, the behavior is undefined, so you cannot reasonably claim that the output will be 0 0.
The behavior of the program is undefined because it has violated the requirements of 6.5 Expressions:
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 read only to determine the value to be stored.
c++ and c are both evaluated without an intervening sequence point, and the prior value of c is read both to determine the value to be stored by c++, and to determine the value of the expression c.
The behaviour will be definitely undefined due to the undefined evaluation order of parameters. You can prove this "undefined output" doing some random testing:
printf("%d %d\n", c++, c);
// result: 0 1
printf("%d %d %d\n", c++, c, c++);
// result: 1 2 0
printf("%d %d %d %d\n", c++, c++, c++, c);
// result: 2 1 0 3
printf("%d %d %d %d\n", c++, c, c++, c);
// result: 1 2 0 2
printf("%d %d %d %d\n", c++, c, c, c);
// result: 0 1 1 1
You are right: it is undefined. The reason is that, though it is guaranteed that the three arguments to printf() will be evaluated before printf() is called, the sequence in which the three arguments are evaluated is undefined.
It is technically incorrect that the incrementation occurs only after the semicolon, incidentally. What the standard guarantees is that the incrementation will occur no later than the semicolon. [Actually, in your case, I believe that the standard guarantees that it will occur before control is passed to the printf() function -- but now this answer is starting to spin off into realms of pedantic trivia, so let me let the matter rest there!]
Anyway, in short, you are right. The behavior is undefined.
Update: As #R.. rightly observes, the undefined behavior comes from the lack of a sequence point between arguments. The standard is quite careful regarding the participles unspecified and undefined, so the correction is accepted with thanks.
This program exhibits a combination of both unspecified behavior and undefined behavior. Starting with the unspecified behavior, the draft C99 standard in section6.5 paragraph 3 says:
The grouping of operators and operands is indicated by the syntax.74)
Except as specified later (for the function-call (), &&, ||, ?:, and
comma operators), the order of evaluation of subexpressions and the
order in which side effects take place are both unspecified.
It also says except as specified later and specifically cites function-call (), so we see that later on the draft standard in section 6.5.2.2 Function calls paragraph 10 says:
The order of evaluation of the function designator, the actual
arguments, and subexpressions within the actual arguments is
unspecified, but there is a sequence point before the actual call.
So we do not know whether the read of C or the evaluation of C++ will happen first at this line of code:
printf ("%d %d", c++, c);
furthermore, in section 6.5.2.4 Postfix increment and decrement operators paragraph 2 says:
[...] After the result is obtained, the value of the operand is incremented. [...] The side effect of updating the stored value of the operand shall occur between the previous and the next sequence point.
So all we know is that when performing the post increment c will be updated after its value is read but before the next sequence point which is right before printf is called but nothing else. As for the undefined behavior, if we look at section 6.5 paragraph 2 from the draft standard, is says:
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 read only to determine the value to be stored.
In the printf expression cs prior value is being read in order to evaluate both C++ and C and so we now are in undefined territory.
This question already has answers here:
Closed 11 years ago.
Possible Duplicates:
Parameter evaluation order before a function calling in C
order of evaluation of function parameters
What will be the output of the following code:
n=5;
printf("%d %d\n", ++n, power(2, n));
output=32
shoulld not be the output be 2^6 =64?
will different compilers give different result?
Order of evaluation of function arguments is unspecified. The compiler can compute the arguments in any order it pleases, but it must do it in some particular order (so there's no undefined behaviour here). The output can be either 32 or 64.
UPD: this is wrong, there's UB here, see here.
Contrary to what other answers say, the code may indeed exhibit undefined behavior.
As has been stated, the order of evaluation of function arguments is unspecified in C. In your program, you have an expression composed of several subexpressions:
ex0(ex1, ex2, ex3(ex4, ex5));
There is only a partial ordering between these subexpressions: ex4 and ex5 must obviously be evaluated before ex3 can be evaluated, and ex1, ex2, and ex3 must be evaluated before ex0 can be evaluated. Other than that, the order of evaluation of the subexpressions is unspecified, and it is up to your compiler to decide the order in which to evaluate the subexpressions.
There are certain, valid orders of evaluations that yield undefined behavior. For example, if ++n is evaluated before power(2, n), the results are undefined: there is a sequence point after the evaluation of all of the arguments to a function but not before or between, and the C Language Standard very clearly states (C99 §6.5/2; emphasis mine):
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 read only to determine the value to be stored.
If ++n is evaluated first, this rule is violated because n is modified by ++n and n is read for the call to power(2, n) without a sequence point between those two steps.
A program that exhibits undefined behavior may produce any result: it may crash, it may print an unexpected answer, or it may appear to work as you expect it to work. Since your program potentially exhibits undefined behavior, it's difficult to discuss with certainty the actual behavior that you see. It is best to avoid writing programs that potentially (or worse, actually) exhibit undefined behavior.
And what does your power function contain? I just checked C math libraries pow function. I had to cast it to <int> like this;
#include <cstdio>
#include <cmath>
using namespace std;
int main () {
int n=5;
printf("%d %d\n", ++n, (int)pow(2.0, n));
return 0;
}
Output: 6 64
I used Microsoft Compiler(used by Visual Studio). Hope it helps.