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

news/2023/12/9 12:56:25

函数参数

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

clipboard.png

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

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

int k = 1;
printf("%d, %d\n", k++, k++);

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

#include <stdio.h>

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

int main()
{
    int k = 1;
    
    func(k++, k++);
    
    printf("%d\n", 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 = %d\n", k);
    
    if( a-- && a )           // 注意这里!
    {
        printf("a = %d\n", 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 = %d\n", i, j);
}

int main()
{
    int k = 1;
    
    func(k++, k++);
    
    printf("%d\n", 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 语言中变量修改的最晚时机

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


http://www.niftyadmin.cn/n/3777090.html

相关文章

禁止浏览器放大字体

if (typeof (WeixinJSBridge) "undefined") {document.addEventListener("WeixinJSBridgeReady", function (e) {setTimeout(function () {// 直接修改用户设置的/默认字体WeixinJSBridge.invoke(setFontSizeCallback, {"fontSize": 0}, functi…

mysql之路的反思

进公司一来&#xff0c;一直在老大的指导下学到很多东西...... linux 下&#xff0c;mysql5.10 、mysql5.5.20、 mysql5.5.28、mysql5.6.10的环境搭建&#xff0c;调配&#xff0c;优化。。。 mysql 的my.cnf 参数的深入了解。。。。。 mysqldump工具的熟练使用。。。 Mysq…

IO重定向和管道用法

STDOUT和STDERR可以被重定向到文件命令 操作符号 文件名支持的操作符号包括&#xff1a; 把STDOUT重定向到文件2> 把STDERR重定向到文件&> 把所有输出重定向到文件文件内容会被覆盖set –C 禁止将内容覆盖已有文件,但可追加| file 强制覆盖set C 允许覆盖 原有内容基…

Linux文件特殊权限

原文地址已无法找到 特殊权限 SUID、SGID、SBIT 先用ls -l命令看一下下面几个文件或目录的信息&#xff1a; -rwsr-xr-x. 1 root root 25980 2月 22 2012 /usr/bin/passwd -rwx--s--x. 1 root slocate 35612 8月 24 2010 /usr/bin/locate drwxrwxrwt. 24 root root …

WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! 解决办法

今天scp 远程拷贝文件时遇到如下报错&#xff1a; [rootbak1 bak]# scp gwsyj.sql.gz root192.168.21.65:/data/dbdata/ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdroppi…

火眼金睛:教你怎么识别AI生成的假脸

大家先看两张图&#xff0c;猜猜下面这两个人是不是真的&#xff0c;答案后面揭晓。2014年&#xff0c;机器学习研究者Ian Goodfellow提出了后来非常有名的“生成式对抗网络”&#xff08;GAN&#xff09;。之所以称之为“生成式”&#xff0c;是因为网络的输出并非对输入进行预…

innobackupex使用注意事项

整理自网络&#xff0c;长期更新。 1.XtraBackup 有两个工具&#xff1a;xtrabackup 和 innobackupex&#xff1a; xtrabackup 本身只能备份 InnoDB 和 XtraDB &#xff0c;不能备份 MyISAM&#xff1b;innobackupex 本身是 Hot Backup 脚本修改而来&#xff0c;同时可以备份…

mysql 导出存储过程

mysql 导出存储过程 今天测试组的一个好朋友&#xff0c;让我帮她拷贝几个mysql的存储过程&#xff0c;从10.200.2.6上&#xff0c;拷贝到10.200.2.2上&#xff0c;原来的数据库&#xff0c;数据都已经存在&#xff0c;只需把存储过程拷贝过去&#xff0c;我先 查看了一下原来…

“价值2个亿”的AI代码

2019独角兽企业重金招聘Python工程师标准>>> 前两天朋友圈里看到一段号称“价值一亿”的代码&#xff1a; 可以智能应答&#xff0c;很厉害是不是&#xff1f; 源码&#xff1a; Python 极简版&#xff1a; while True:print(input().replace(吗,).replace(&#x…

linux 查看统计文件夹下文件的个数

ls -l |grep "^-"|wc -l或find ./company -type f | wc -l查看某文件夹下文件的个数&#xff0c;包括子文件夹里的。ls -lR|grep "^-"|wc -l查看某文件夹下文件夹的个数&#xff0c;包括子文件夹里的。ls -lR|grep "^d"|wc -l说明&#xff1a;ls…

关于java包装类型的缓存

2019独角兽企业重金招聘Python工程师标准>>> 除了Float 和 Double 外&#xff0c;其他包装数据类型都会缓存 -128 &#xff5e;127 的值&#xff0c;对于 Integer var ? 在 -127~128 之间的赋值&#xff0c;Integer 对像由IntegerCache.cache 产生&#xff0c;会复…

mysql的sql_mode 模式修改 my.cnf

1. sql_mode模式 mysql数据库的中有一个环境变量sql_mode,定义了mysql应该支持的sql语法&#xff0c;数据校验等&#xff01;我们可以通过以下方式查看当前数据库使用的sql_mode&#xff1a; mysql> select sql_mode;-----------------------------------------------------…

从javascript到python(二):virtualenv

前序文章《从javascript到python(一):基本环境搭建》中提到了pip和pip3安装Python的库&#xff0c;但是问题来了&#xff0c;安装之后的库都会放在如&#xff1a;python2.7/site-packages,这也意味着可能出现同一个库多个版本同时存在的问题。npm中的包管理工具是怎么解决的&am…

MySQL内存使用说明(全局缓存+线程缓存)

MySQL内存使用说明&#xff08;全局缓存线程缓存&#xff09; 首先我们来看一个公式&#xff0c;MySQL中内存分为全局内存和线程内存两大部分&#xff08;其实并不全部&#xff0c;只是影响比较大的 部分&#xff09;&#xff1a; per_thread_buffers(read_buffer_sizeread_r…

Oracle 数据库 11g新特性:自适应游标与 SQL 计划管理

原文地址&#xff1a;http://blog.chinaunix.net/uid-42518-id-2404640.html 到目前为止&#xff0c;很多人都已经了解大量使用绑定变量来提高性能的方法&#xff1b;对于尚不清楚这种方法的用户&#xff0c;我将尽力以最简单的方式介绍该方法的核心概念。&#xff08;同时&am…

zip 函数

zip 函数&#xff0c;看上去是打包的意思&#xff0c;其实功能是将多个可迭代对象&#xff0c;组合成一个个元组。 zip(iter1,iter2) a,b zip(*zip(iter1,iter2)) a [1,2,3] b [4,5,6] c [a,b,c,d]# 将可迭代对象打包成一个个元组 print(zip(a,b)) print(list(zip(a,b))) p…
最新文章