将值传递给函数时,GCC前缀增量运算符行为不端[重复]

Posted

技术标签:

【中文标题】将值传递给函数时,GCC前缀增量运算符行为不端[重复]【英文标题】:GCC prefix increment operator misbehaving when passing value to the function [duplicate] 【发布时间】:2016-04-21 10:17:03 【问题描述】:

使用 GCC 编译时,前缀增量运算符不会在调用 printf() 时发送变量 (x) 的增量值。

这是我的代码;

#include <stdio.h>

int bitcount(unsigned);

int main()

    unsigned int x = 127;

    printf("bitcount[%d] : %d\n", x, bitcount(x));
    printf("bitcount[%d] : %d\n", ++x, bitcount(x));

    return 0;


int bitcount(unsigned int x)

    int bitcount = 0;
    for(; x != 0; x >>= 1)
        if( x & 1 )
            bitcount++;

    return bitcount;

在main()方法中,问题出在下面一行;

printf("bitcount[%d] : %d\n", ++x, bitcount(x));

X 应该增加并发送到bitcount(),x 的增加值。但是 x 的值会增加,而不是增加的值,而是将旧值发送到 bitcount() 函数。

我在 MS VS 上尝试过同样的事情,但没有发生这种行为不端。输出如下;

在 Windows 10 上使用 GCC 编译的程序输出

D:\C\chapter2>gcc bitcount.c -o bitcount.exe

D:\C\chapter2>bitcount
bitcount[127] : 7
bitcount[128] : 7

在 Windows 10 上使用 MS VS 编译的程序输出

bitcount[127] : 7
bitcount[128] : 1

为了解决这个问题,我更新了代码以查看发送到函数的值;

Bitcount V2

#include <stdio.h>

int bitcount(unsigned);

int main()

    unsigned int x = 127;

    printf("bitcount[%d] : %d\n", x, bitcount(x));
    printf("bitcount[%d] : %d\n", ++x, bitcount(x));

    return 0;


int bitcount(unsigned int x)

    printf("\nDebug::\tbitcount()::x=%d\n", x);
    int bitcount = 0;
    for(; x != 0; x >>= 1)
        if( x & 1 )
            bitcount++;

    return bitcount;

在 Windows 10 系统上使用 GCC 编译的 Bitcount v2 输出

调试::bitcount()::x=127 位计数[127]:7

调试::bitcount()::x=127 位计数[128]:7

很明显,GCC 将 X 的旧值发送到 bitcount() 函数。

为了概括问题,我写了下面的程序;

InsideFunc.c

#include <stdio.h>

int func(unsigned);

int main()

    unsigned int x = 127;

    printf("x: %d, func(): %d\n",   x, func(x));
    printf("x: %d, func(): %d\n", ++x, func(x));

    return 0;


int func(unsigned x)

    printf("\n\tDebug::func()::x=%d\n", x);

    return x;

在 Windows 10 系统上使用 GCC 编译的输出

D:\C\chapter2>gcc InsideFunc.c -o InsideFunc.exe

D:\C\chapter2>InsideFunc

    Debug::func()::x=127

x:127,函数():127

    Debug::func()::x=127

x:128,函数():127

同样,变量的值增加,但旧值发送到函数。

这是我的 gcc 版本;

D:\C\chapter2>gcc --version
gcc (GCC) 5.2.0
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

有什么想法吗?

后期说明:我刚刚在 Kernighan & RitchieThe C Programming Language 一书中看到,第 49 页解释了顺序函数参数中的求值是不确定的,并且依赖于编译器;

同样,函数参数的计算顺序不是 指定,所以声明

printf("%d %d\n", ++n, power(2, n)); /*WRONG */

使用不同的编译器可能会产生不同的结果,具体取决于 n 在调用 power 之前是否递增。的解决方案, 当然是写

++n;

printf("%d %d\n", n, power(2, n));

【问题讨论】:

UB,因为函数参数评估和传递约定没有排序。 Possible dupe 我不认为这是一个重复的问题。将递增的值传递给函数时会出现此问题。 bitcount[%d] --> bitcount[%u] @LeventDivilioglu 它与 Sourav 链接的问题重复。你在同一个表达式中使用了两次x,中间没有序列点,并且有一个未排序的副作用x。一旦修复了该错误,可能还会出现函数参数评估顺序的问题。 【参考方案1】:

函数调用参数的求值顺序在 C++ 中未指定,因此如下行:

printf("bitcount[%d] : %d\n", ++x, bitcount(x));

不能保证++x 会在bitcount(x) 之前被评估,所以bitcount 可以通过x 预递增。您所有的编译器都有不同但有效(即符合标准)的行为。

【讨论】:

行为未定义。 x 在没有序列点的情况下被修改和读取。 确实未指定参数的评估顺序。但这个表达式的意义远不止于此:x 在序列点之间被多次使用,除了确定在x 中存储哪个值之外,还有其他目的。那是未定义的行为。所以在这个例子中,你有未定义和未指定的行为。前者总是一个严重的错误,后者如果程序依赖于顺序可能会导致错误。 这不是重复的,问题也不是未定义的行为。更相关的是如何调用printf函数以及调用printf之前如何创建激活记录。创建激活记录时,编译器开始从右向左推送 printf 变量;因此,首先在 x 递增并入栈之前调用 bitcount 函数,然后 x 递增并入栈。 @merkez3110 不,这根本不正确。请参阅 C99 6.5“在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的评估修改一次。此外,应只读先前的值以确定要存储的值。”然后另请参阅 6.5.2.2/10“未指定函数指示符、实际参数和实际参数中的子表达式的求值顺序”。 我会坚持:考虑像int foo(int x) return (x * x); 这样的 foo 函数,它只返回 x 的平方。当main() 代码行是int x = 2; printf("x = %d foo(x) = %d\n", ++x, foo(x)); 并且您只需通过gcc a.c -o a.exe 行编译它,代码编译并运行正常,吐出x = 3 foo(x) = 4。但是,当通过gcc a.c -Wall -Werror -o a.exe 行编译相同的代码时,您会收到类似“对'x' 的操作可能未定义”的警告。因此,很明显,未定义的行为与 printf 变量压入堆栈的顺序有关。【参考方案2】:

不要在表达式中使用自增:

printf("bitcount[%d] : %d\n", x, bitcount(x));
x++;
printf("bitcount[%d] : %d\n", x, bitcount(x));

【讨论】:

以上是关于将值传递给函数时,GCC前缀增量运算符行为不端[重复]的主要内容,如果未能解决你的问题,请参考以下文章

前缀/后缀增量运算符

增量算子在字符类型边界上的行为

cmake给gcc添加编译前缀

将值传递给 Worklight SQL 适配器中的 IN 运算符

将值传递给构造函数时使构造函数工作的问题

vc9 和 gcc 之间的不同析构函数行为