zoukankan      html  css  js  c++  java
  • 第44课 函数参数的秘密 (上)

    函数参数:

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

    第一感觉,这个函数会输出1和2,k的最终值会变成3。

    示例程序:

     1 #include <stdio.h>
     2 
     3 int func(int i, int j)
     4 {
     5     printf("%d, %d
    ", 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
    ", 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
    ", k);
    11     
    12     if( a-- && a )
    13     {
    14         printf("a = %d
    ", 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
    ", 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
    ", k);
    17     
    18     return 0;
    19 }

    第14行存在顺序点,这两个++操作,在进入func函数体之前必须完成,这里说的完成是修改k所在的内存,而不是传进func函数中的值,有可能将k先取出,两个++先悬挂着,这时k为1,然后将k传入到i和j(这就是所谓的实参求值完成后),在进入func前是顺序点,因此,调用func前,执行悬挂着的++,然后改变k所在的内存。

     上述程序在vc编译器下的运行结果如下:

    可以看到和我们分析的一样。

    小结:

  • 相关阅读:
    使用私有api实现自己的iphone桌面,并根据app的使用次数对app排序
    坐标系的属性
    带坐标轴的几何画板
    空间几何体的直观图matlab
    设置npm的registry
    (原创)机器学习之numpy库中常用的函数介绍(一)
    (原创)交叉编译 tesseract
    (原创)计算机视觉之数学原理-基础篇
    (原创)nRF51 DFU 初始化包介绍及生成工具
    (原创)使用nRF51822/nRF51422创建一个简单的BLE应用 ---入门实例手册(中文)之五
  • 原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9557758.html
Copyright © 2011-2022 走看看