函数参数:
函数参数的求值顺序依赖于编译器的实现:
第一感觉,这个函数会输出1和2,k的最终值会变成3。
示例程序:
1 #include <stdio.h> 2 3 int func(int i, int j) 4 { 5 printf("%d, %d\n", i, j); 6 7 return 0; 8 } 9 10 int main() 11 { 12 int k = 1; 13 14 func(k++, k++); 15 16 printf("%d\n", k); 17 18 return 0; 19 }
运行结果如下:
这与我们的预期是不符的,这是为什么呢?
因为函数参数的求值顺序是和编译器相关的,并没有规定第一个k++先求职,第二个k++后求职。只是规定了参数的值必须求出来之后才进行确切的函数的调用。
在这里我们可以得出,求值顺序是先求了第二个k++,又求了第一个k++。
这个现象可以扩展到操作符上,例如乘法操作的左右两个操作数并没有规定哪一个先把值求出来。这也是依赖于编译器的实现。
上述程序在bcc32下的输出如下;
程序中的顺序点:
程序中有如下顺序点,例:
上面例子中,我们第一直觉会认为k的最终值为5。
示例如下:
1 #include <stdio.h> 2 3 int main() 4 { 5 int k = 2; 6 int a = 1; 7 8 k = k++ + k++; 9 10 printf("k = %d\n", k); 11 12 if( a-- && a ) 13 { 14 printf("a = %d\n", a); 15 } 16 17 return 0; 18 }
运行结果如下:
第8行的语句,存在3个改变内存的操作,两个++操作和一个赋值操作。编译器回先执行加法操作,编译器先取出第一个k的值,然后是一个++操作,这个操作先挂起不执行,然后取出第二个k的值,这个k的++操作也是先挂起,这时候有两个++操作悬挂着。这时候到了分号(;)了,改变内存的操作必须完成了,于是执行加法操作,将结果写入k代表的内存中,然后执行悬挂着的两个++操作,对k所代表的内存连加两次1。这时k就变成了6。
上述程序在bcc中的运行结果如下:
在vc编译器中的反汇编如下:
可以看到先取出k的值相加,然后使用ecx、edx对这个内存连加两次1。
得到的最终结果也是6。
上述程序第12行也存在顺序点,第14行的语句并没有执行到,这是为什么?
因为执行完a--操作之后就遇到了&&,这是一个顺序点,对内存的操作必须立刻完成,于是a变为了0,再读取a时就是0了,因此1&&0就变为了假。
函数调用时所有实参求值完成后(进入函数体之前)也是一个顺序点,示例如下:
1 #include <stdio.h> 2 3 int func(int i, int j) 4 { 5 printf("%d, %d\n", i, j); 6 7 return 0; 8 } 9 10 int main() 11 { 12 int k = 1; 13 14 func(k++, k++); 15 16 printf("%d\n", k); 17 18 return 0; 19 }
第14行存在顺序点,这两个++操作,在进入func函数体之前必须完成,这里说的完成是修改k所在的内存,而不是传进func函数中的值,有可能将k先取出,两个++先悬挂着,这时k为1,然后将k传入到i和j(这就是所谓的实参求值完成后),在进入func前是顺序点,因此,调用func前,执行悬挂着的++,然后改变k所在的内存。
上述程序在vc编译器下的运行结果如下:
可以看到和我们分析的一样。
小结:
原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9557758.html