我试图理解为什么以下代码生成“参数可能被破坏..”警告。这是一个最小的示例:

#include <unistd.h>

extern char ** environ;

int test_vfork(char **args, char **newEnviron)
{
    pid_t pid = vfork();
    if (pid == 0) {
        execve(args[0], args, (newEnviron != NULL)? newEnviron : environ);
    }

    return 0;
}

这是输出(这是gcc 4.3.3):
$ gcc -O2 -Wclobbered -c test.c -o test.o
test.c: In function ‘test_vfork’:
test.c:5: warning: argument ‘newEnviron’ might be clobbered by ‘longjmp’ or ‘vfork’

另外我发现,如果我将execve行替换为以下内容,则警告消失了:
    if (newEnviron != NULL)
        execve(commandLine[0], commandLine, newEnviron);
    else
        execve(commandLine[0], commandLine, environ);

不知道为什么gcc比原始版本更好。谁能阐明一些想法?

最佳答案

从C99的 longjmp 描述:

All accessible objects have values, and all other components of the abstract
machine218) have state, as of the time the longjmp function was called, except
that the values of objects of automatic storage duration that are local to the
function containing the invocation of the corresponding setjmp macro that do not
have volatile-qualified type and have been changed between the setjmp invocation
and longjmp call are indeterminate.

如果您将 newEnviron 设置为 Volatile 对象,则本段表示 longenmp 不会破坏 newEnviron vfork 的规范或实现可能会有类似的警告。

-编辑-

因为您已启用优化,并且 newEnviron 是非 volatile ,并且由于在三元条件运算符中使用后未对其进行访问,所以该实现可为您的条件运算符执行的优化将是实际上重新分配 newEnviron 的值为,环境为。就像是:
execve(args[0], args, (newEnviron = newEnviron ? newEnviron : environ));

但是从 vfork 手册中我们知道,在执行之前修改大多数对象会导致未定义的行为。

参数不会遇到同样的问题,因为没有这样的条件测试。

如果语句具有更多序列点,那么您的可能会不那么容易被识别为优化机会。但是,我会猜测序列点在优化中起着很重要的作用。

顺便说一下,优化是将 newEnviron 的存储重新用于条件运算符的结果,这意味着既不使用其他寄存器(如果通常使用寄存器),也不需要额外的堆栈空间(对于使用堆栈)。

我敢打赌,如果您能够说服 gcc 您需要在执行行之后访问 newEnviron 的值,那么将无法进行优化,并且警告将消失。

但是,当然,使用 volatile 也是一个简单的解决方案。

10-04 13:33