第44课 函数参数的秘密 (上)

函数参数:

函数参数的求值顺序依赖于编译器的实现:

第一感觉,这个函数会输出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

时间: 2024-10-07 16:16:16

第44课 函数参数的秘密 (上)的相关文章

第44课 函数参数的秘密(上)

1. 函数参数 (1)函数参数在本质上与局部变量相同,都在栈上分配空间 (2)函数参数的初始值是函数调用时的实参值 (3)函数参数的求值顺序依赖于编译器的实现(注意:这里指求值顺序而不是入栈顺序!) [实例分析]函数参数的求值顺序 #include <stdio.h> int func(int i, int j) { printf("i = %d, j = %d\n",i, j); return 0; } int f() { printf("f() Call...

第45课 函数参数的秘密(下)

参数入栈顺序: 调用约定: gcc语言默认使用__cdecl调用约定.调用约定不是语言的一部分,是编译器的一部分. 调用约定使用的最典型的地方就是库函数的调用.因为库的编译方式我们的自己的程序的编译方式可能不一样. 小问题: 如何编写一个计算n个数平均值的函数? 示例程序: 1 #include <stdio.h> 2 3 float average(int array[], int size) 4 { 5 int i = 0; 6 float avr = 0; 7 8 for(i=0; i&

第7课 函数参数的扩展

1.  函数参数的默认值 (1)C++中可以在函数声明时为参数提供一个默认值(注意是声明,不能在定义中提供) (2)当函数调用时没有提供参数的值,则使用默认值 [实例分析]默认参数值初探 #include <stdio.h> //默认值只能在函数声明时提供 int mul(int x = 0); //参数x的默认值为0 int main() { printf("%d\n", mul()); //传入默认值0 printf("%d\n", mul(-1))

AGG第六课 函数参数使用弧度

2.1 定义 所谓"弧度的定义"就是说,1弧度的角大小是怎样规定的? 我们知道"度"的定义是,"两条射线从圆心向圆周射出,形成一个夹角和夹角正对的一段弧.当这段弧长正好等于圆周长的360分之一时,两条射线的夹角的大小为1度. 那么,弧度又是怎样定义的呢? 弧度的定义是:两条射线从圆心向圆周射出,形成一个夹角和夹角正对的一段弧.当这段弧长正好等于圆的半径时,两条射线的夹角大小为1弧 摘自:http://sourceforge.net/p/vector-agg

C之函数参数(三十九)

我们上节博文讲了函数的意义,那么我们今天来讲下函数参数.函数参数在本质上与局部变量相同在栈上分配空间,函数参数的初始值是函数调用时的实参值.用下图来实际说明 函数参数的求值顺序依赖于编译器的实现,我们来看看下面代码的输出是什么?为什么呢? #include <stdio.h> int func(int i, int j) {     printf("i = %d, j = %d\n", i, j);          return 0; } int main() {    

第8课 函数重载分析(上)

重载(Overload): C++中的函数重载: 函数重载示例: 1 #include <stdio.h> 2 #include <string.h> 3 4 int func(int x) 5 { 6 return x; 7 } 8 9 int func(int a, int b) 10 { 11 return a + b; 12 } 13 14 int func(const char* s) 15 { 16 return strlen(s); 17 } 18 19 20 int

第20课 可变参数模板(1)_模板参数包和函数参数包

1.  参数包(parameter pack) (1)模板参数包(以tuple为例):template<typename- Elements>class tuple ①Elements标识符的左侧使用了省略号,在C++11中Elements被称为"模板参数包",表示可以接受任意多个参数作为模板参数. ②编译器将多个模板参数打包成"单个"的模板参数包,如tuple<int, char, double>实例化模板类时,Element就是包含int

一起入门python7之函数参数

上一节课简单的讲了一下函数.那么今天来给大家讲一下函数参数.用案例说话哈.上一节课只是让大家有比较简单的了解,那么这节我们来细化一下哈.>>>def hello(x):        #这里的x就是参数return x * x>>>hello(2)               #自动将x=2带入x*x当中去计算了.故得四.4>>>#大家想一下,倘若是次方的该怎么办?比如x的n次方.>>> def wa(x,n):    #首先先定义两

第9课 - 函数定义及调用

第9课 - 函数定义及调用 1. makefile中的函数 (1)make 解释器提供了一系列的函数供 makefile 调用 (2)在 makefile 中支持自定义函数实现,并调用执行 (3)通过 define 关键字实现自定义函数 2. 在makefile中自定义函数 (1)自定义函数的语法 其中,$(0) 代表被调用的函数名,$(1) , $(2) , $(3) ... 代表调用函数时后面的传参 (2)深入理解自定义函数 - 自定义函数是一个多行变量,无法直接调用 - 自定义函数是一种过