V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xjr1022
V2EX  ›  C

C 语言,碰到个很让人疑惑的问题

  •  
  •   xjr1022 · 2018-12-04 17:15:04 +08:00 · 3505 次点击
    这是一个创建于 2163 天前的主题,其中的信息可能已经有所发展或是发生改变。
    
    //声明函数
    void test(int a, int b);
    
    int main() {
    	int a = 2;
    	test(a, a++);
    	return 0;
    }
    
    void test(int x, int y) {
    
    	printf("x=%d,y=%d", x, y);
    
    }
    
    //运行结果 
    x=3,y=2
    

    想请教一下这个是为什么呢,明明给形参 y 传递的是自增的量,怎么就 x 变成 3,而 y 还是 2,

    第 1 条附言  ·  2018-12-04 18:05:50 +08:00
    ![][https://i.loli.net/2018/12/04/5c065015bdaba.png]
    刚得到的答案,大家看看对吗
    45 条回复    2018-12-14 10:27:06 +08:00
    shylockhg
        1
    shylockhg  
       2018-12-04 17:19:14 +08:00
    从右边传参数
    xjr1022
        2
    xjr1022  
    OP
       2018-12-04 17:23:34 +08:00
    @shylockhg 麻烦您能说的详细点吗,谢谢了
    misaka19000
        3
    misaka19000  
       2018-12-04 17:23:52 +08:00
    在我电脑上面的运行结果
    x=2,y=2
    make
        4
    make  
       2018-12-04 17:24:03 +08:00   ❤️ 1
    a++的返回值是自增之前的值,++a 的返回值才是自增之后的值
    函数调用最后的参数先压栈
    wutiantong
        5
    wutiantong  
       2018-12-04 17:26:08 +08:00   ❤️ 2
    在 C++里这属于典型的 ub 了,因为函数传参时 eval 顺序是未定义的。
    单就这个例子来说,先算了(a++) 后算了(a),结果没毛病。
    shylockhg
        6
    shylockhg  
       2018-12-04 17:26:29 +08:00
    这个很尴尬。。。
    clang 3,2
    gcc 2,2
    shylockhg
        7
    shylockhg  
       2018-12-04 17:28:12 +08:00
    @shylockhg 不过 clang 报了警告,估计对这个做了 trick 处理
    kljsandjb
        8
    kljsandjb  
       2018-12-04 17:29:06 +08:00 via iPhone   ❤️ 1
    视 calling convention 而定吧
    shylockhg
        9
    shylockhg  
       2018-12-04 17:29:09 +08:00   ❤️ 1
    @shylockhg 反了
    clang 2,2
    gcc 3,2
    lovefantasy
        10
    lovefantasy  
       2018-12-04 17:30:43 +08:00 via Android
    等一个解释
    KingHL
        11
    KingHL  
       2018-12-04 17:31:26 +08:00   ❤️ 4
    谁这么写代码,直接打死
    xjr1022
        12
    xjr1022  
    OP
       2018-12-04 17:33:07 +08:00
    @KingHL 是一道蛋疼的试题
    WuwuGin
        13
    WuwuGin  
       2018-12-04 17:35:15 +08:00   ❤️ 1
    The language clearly says that certain things lead to undefined behavior. There is no problem, there is no "should" involved. If the undefined behavior changes when one of the involved variables is declared volatile, that doesn't prove or change anything. It is undefined; you cannot reason about the behavior.

    https://stackoverflow.com/a/949443

    https://en.wikipedia.org/wiki/Sequence_point

    总结:在实际编程中,不应该出现这种二义性的行为。探讨此类问题没有实际意义。
    des
        14
    des  
       2018-12-04 17:35:27 +08:00 via Android
    不要这么写,这都什么鬼。顺手给你加一点好了
    test(++a, a++)
    KingHL
        15
    KingHL  
       2018-12-04 17:38:28 +08:00   ❤️ 2
    参数入栈顺序从右向左,具体实现还要看不同的编译器行为,实际工程应用中,应该禁止这种不确定性的代码。如果是试题的话,估计是在考察你参数入栈顺序这个知识点。
    xi2008wang
        16
    xi2008wang  
       2018-12-04 17:38:48 +08:00
    xjr1022
        17
    xjr1022  
    OP
       2018-12-04 17:39:25 +08:00
    @WuwuGin 是的,真实开发相信没人这么写,这是一道考研真题。。。。
    xjr1022
        18
    xjr1022  
    OP
       2018-12-04 17:42:25 +08:00
    @des 这就要问出题老师怎么想的了,哈哈
    xjr1022
        19
    xjr1022  
    OP
       2018-12-04 17:43:17 +08:00
    @misaka19000 试试 gcc 下编译,有惊喜
    WordTian
        20
    WordTian  
       2018-12-04 17:43:27 +08:00 via Android
    看编译器用的什么调用约定。。。
    就是参数是从左往右还是从右往左
    kokutou
        21
    kokutou  
       2018-12-04 17:46:20 +08:00 via Android
    @xjr1022
    那就只能看卷子上或考试说明上说的用啥编译器。
    然后自己试试记下来就完事了。
    xjr1022
        22
    xjr1022  
    OP
       2018-12-04 17:51:15 +08:00
    @kokutou 没答案,而且卷子连操作系统,编译器版本 c 标准都没指定,估计默认就是谭浩强的那个编译环境了
    misaka19000
        23
    misaka19000  
       2018-12-04 17:56:33 +08:00
    用 gcc 取了下汇编,lea 指令那里看不太懂什么意思,有大佬能解释下吗

    pushq %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq %rsp, %rbp
    .cfi_def_cfa_register 6
    subq $16, %rsp
    movl $2, -4(%rbp)
    movl -4(%rbp), %eax
    leal 1(%rax), %edx
    movl %edx, -4(%rbp)
    movl -4(%rbp), %edx
    movl %eax, %esi
    movl %edx, %edi
    call test
    movl $0, %eax
    freedomSky
        24
    freedomSky  
       2018-12-04 17:57:51 +08:00 via iPhone
    我想知道这是哪儿的考研题
    kljsandjb
        25
    kljsandjb  
       2018-12-04 17:58:18 +08:00 via iPhone
    @misaka19000 #23 lea 实现运算功能,自增 1
    misaka19000
        26
    misaka19000  
       2018-12-04 17:59:09 +08:00
    eax 存的应该是 2,但是不知道 edx 是咋算出来的。。。
    kljsandjb
        27
    kljsandjb  
       2018-12-04 17:59:17 +08:00 via iPhone
    @misaka19000 #23 然后 edi 相当于第一个参数为 3,esi 为第二个参数为 2,结果就是楼主的结果
    kljsandjb
        28
    kljsandjb  
       2018-12-04 18:00:12 +08:00 via iPhone
    @misaka19000 #26 这个代码没优化,讲的啰嗦、其实 edx 就是 lea1(%rax)算的
    misaka19000
        29
    misaka19000  
       2018-12-04 18:00:23 +08:00
    @kljsandjb #25 但是不清楚 rax 的值是多少额,lea 能知道是针对哪一个值进行自增吗
    kljsandjb
        30
    kljsandjb  
       2018-12-04 18:01:31 +08:00 via iPhone
    @misaka19000 #29 rax 不是赋值了吗,先从栈取出来给它的
    kljsandjb
        31
    kljsandjb  
       2018-12-04 18:02:02 +08:00 via iPhone
    @misaka19000 #29 lea 可以做最基本的运算,其实跟地址什么的没什么关系
    XIVN1987
        32
    XIVN1987  
       2018-12-04 18:02:29 +08:00
    TDMGCC:x=3,y=2
    kljsandjb
        33
    kljsandjb  
       2018-12-04 18:03:47 +08:00 via iPhone
    @misaka19000 #29 https://i.loli.net/2018/12/04/5c0650f7e4dae.png 这三行我觉得说得很清楚了呀~
    misaka19000
        34
    misaka19000  
       2018-12-04 18:10:49 +08:00
    @kljsandjb #30 大佬能解释下这行做了什么操作吗

    leal 1(%rax), %edx

    从上下文看这个时候 rax 还没有值吧?
    kljsandjb
        35
    kljsandjb  
       2018-12-04 18:11:33 +08:00 via iPhone
    @misaka19000 #34 eax 是 rax 的低 32 位
    kljsandjb
        36
    kljsandjb  
       2018-12-04 18:11:48 +08:00 via iPhone
    @misaka19000 #34 movl 操作会把高位清零
    misaka19000
        37
    misaka19000  
       2018-12-04 18:14:51 +08:00
    @kljsandjb #36 原来是这样啊,蟹蟹大佬(^ᗜ^)
    Kaiv2
        38
    Kaiv2  
       2018-12-04 18:22:09 +08:00 via Android
    看看汇编,就很清晰了
    GAMEKON
        39
    GAMEKON  
       2018-12-04 18:25:27 +08:00 via Android
    多写一行能死啊
    sbw
        40
    sbw  
       2018-12-04 18:32:48 +08:00   ❤️ 2
    ub,取决于编译器实现。

    lea 用于寄存器的时候可以实现简单的 乘 2,乘 4,乘 5,加 1 这种计算,比 add 快(不需要进入 ALU )。
    alaneuler
        41
    alaneuler  
       2018-12-04 19:06:48 +08:00
    这就是 sequence point 吧。。。
    https://en.wikipedia.org/wiki/Sequence_point
    alaneuler
        42
    alaneuler  
       2018-12-04 19:09:30 +08:00   ❤️ 1
    Before a function is entered in a function call. The order in which the arguments are evaluated is not specified, but this sequence point means that all of their side effects are complete before the function is entered.
    transzero
        43
    transzero  
       2018-12-04 22:34:18 +08:00 via Android   ❤️ 1
    默认 cdecl 调用方式,传参压栈从右往左,++是后计算的,所以第二个 2,第一个自然是 3
    hx1997
        44
    hx1997  
       2018-12-04 23:25:20 +08:00
    国内考研题有的知识那叫一个老旧啊... 这种题也是上世纪谭浩强观念的遗毒,简直让人怀疑他们的知识体系从国外引进来就没有更新过,只会闭门造车......
    iceheart
        45
    iceheart  
       2018-12-14 10:27:06 +08:00 via Android
    这是在写 bug 么?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1011 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 21:06 · PVG 05:06 · LAX 13:06 · JFK 16:06
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.