【C】 44_函数参数的秘密 (上)

发布时间:2019-08-06 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了【C】 44_函数参数的秘密 (上)脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

函数参数

  • 函数参数在本质上与局部变量相同在栈上分配空间
  • 函数参数的初始值是函数调用时的实参

clipboard.png

  • 函数参数的求值顺序依赖于编译器的实现
  • 操作符的求值顺序依赖于编译器的实现(+-*/...)

下面的程序输出什么?为什么呢?

int k = 1;
PRintf("%d, %dn", k++, k++);

实例分析: 函数参数的求值顺序

#include <stdio.h>

int func(int i, int j)
{
    printf("i = %d, j = %dn", i, j);
}

int main()
{
    int k = 1;
    
    func(k++, k++);
    
    printf("%dn", k);
}
输出:【gcc】
i = 2, j = 1
3

特别说明:此处暂时没有找到有其它求值顺序的编译器来输出说明。
由于c语言明确规定函数参数的求值顺序,其交由具体的编译器厂商决定,因此为了提高程序的可移植性,不可依赖某一编译器的求值顺序行为。

程序中的顺序点

  • 程序中存在一定的顺序点
  • 顺序点是指执行过程中修改变量值(内存)的最晚时刻
  • 在程序到达顺序点的时候,之前所作的一切操作必须完成

C 语言中的顺序点

  • 每个完整表达式结束时,即分号处
  • &&, ||, ?: 及 逗号表达式 的每个参数计算之后
  • 函数调用时所有实参求值完成后(进入函数体之前)

下面的程序运行结束后 k 的值为多少呢?

int k = 2;
k = k++ + k++;

编程实验: 程序中的顺序点

实验 1

#include <stdio.h>

int main()
{
    int k = 2;
    int a = 1;
    
    k = k++ + k ++;          // 注意这里!
    
    printf("k = %dn", k);
    
    if( a-- && a )           // 注意这里!
    {
        printf("a = %dn", a);
    }
    
    return 0;
}
输出【gcc】:
k = 6

输出【vs2010】:
k = 6

VS2010 汇编

k = k++ + k ++;
003D356C  mov         eax,dword ptr [k]  
003D356F  add         eax,dword ptr [k] 
003D3572  mov         dword ptr [k],eax ; k = 2 + 2 = 4 
003D3575  mov         ecx,dword ptr [k]  
003D3578  add         ecx,1   
003D357B  mov         dword ptr [k],ecx ; k = k + 1 = 4 + 1 = 5
003D357E  mov         edx,dword ptr [k]  
003D3581  add         edx,1  
003D3584  mov         dword ptr [k],edx ; k = k + 1 = 4 + 1 = 6

gcc 汇编

int k = 2;
080483cd:   movl $0x2,0x1c(%esp)

k = k++ + k ++;    
080483dd:   mov  0x1c(%esp),%eax
080483e1:   add  %eax,%eax 
080483e3:   mov  %eax,0x1c(%esp)   ; k = 2 + 2 = 4
080483e7:   addl $0x1,0x1c(%esp)   ; k = k + 1 = 4 + 1 = 5
080483ec:   addl $0x1,0x1c(%esp)   ; k = k + 1 = 5 + 1 = 6

实验 2

#include <stdio.h>

int func(int i, int j)
{
    printf("i = %d, j = %dn", i, j);
}

int main()
{
    int k = 1;
    
    func(k++, k++);
    
    printf("%dn", k);
}
输出:【gcc】
i = 2, j = 1
3

输出:【用于 80x86 的 Microsoft (R) 32 位 C/C++ 优化编译器 16.00.30319.01 版】
i = 1, j = 1
3

分析:
C 文件会被编译成汇编文件。一条C代码可能对应多条汇编代码,汇编代码的顺序也许没有特定的规定。对于不同的编译器,可能有不同的编译方式,但都必须满足这一原则:在程序到达顺序点时,所有改变内存的操作必须完成。

注意:
在实际工程中, 需要遵循一定的规则避免非 C 语言规定而与编译器相关的写法。
对于“函数参数的求值顺序”、“程序中的顺序点“不必过度深究,遇到奇怪的问题时,思考是否是这里导致的问题即可。

小结

  • 函数的参数在栈上分配空间
  • 函数的实参并没有固定的计算次序
  • 顺序点时 C 语言中变量修改的最晚时机

以上内容参考狄泰软件学院系列课程,请大家保护原创

脚本宝典总结

以上是脚本宝典为你收集整理的【C】 44_函数参数的秘密 (上)全部内容,希望文章能够帮你解决【C】 44_函数参数的秘密 (上)所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。