关于 WEXITSTATUS 与 `G++ 4.9.4` 的奇怪行为

Strange behavior about WEXITSTATUS with `G++ 4.9.4`

下面的代码片段可以编译,

#include<sys/types.h>
#include<sys/wait.h>
#include<iostream>

int main()
{
    int ret = 0xFFFF;
    std::cout << WEXITSTATUS(ret);
}

而此代码片段确实无法通过 G++ 4.9.4:

编译
#include<sys/types.h>
#include<sys/wait.h>
#include<iostream>

int main()
{
     std::cout << WEXITSTATUS(0xFFFF);
}

这是编译器的抱怨:

In file included from /usr/include/x86_64-linux-gnu/sys/wait.h:77:0,
                 from t.cpp:2:
t.cpp: In function ‘int main()’:
t.cpp:7:22: error: lvalue required as unary ‘&’ operand
         std::cout << WEXITSTATUS(0xFFFF);
                      ^

这里是关于编译器的详细信息:

g++ --version
g++ (Ubuntu 4.9.4-2ubuntu1~16.04) 4.9.4
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.

并通过以下命令在Ubuntu16.04上安装编译器

sudo apt-get install gcc-4.9
sudo apt-get install g++-4.9
sudo update-alterntives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 20
sudo update-alterntives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 20

注意: 我必须使用g++-4.9,我别无选择。

奇怪的是,我无法在 godbolt.org 上重现上述现象。它 compiles on godbolt.orggcc 4.9.3gcc 4.9.4 不可用)。

这是g++ -E the_said_code_snippet_does_not_compile.cpp

的输出
//omit
# 4 "t.cpp" 2

int main()
{
        std::cout << ((((*(const int *) &(0xFFFF))) & 0xff00) >> 8);
}

有人能解释一下这件事吗?

已更新:

我现在可以重现错误了!看这个link

已更新:

这只是一个简化的例子。我实际上面对的是 WEXITSTATUS(pclose(fp)) 不编译。

这看起来像是宏的古怪实现,导致运算符优先级出现问题。通常它应该是这样的:

#define     WEXITSTATUS(status)        (((status) & 0xff00) >> 8)

现在如果是(结果完全错误)

#define     WEXITSTATUS(status)        status & 0xff00 >> 8

您将获得以下订单:

( std::cout << status ) & (0xff00 >> 8)

虽然我无法想象会存在这种破烂的实现?

经过处理的代码片段显示,宏假设 status 是一个至少返回纯右值的表达式,人们可以获得它的地址和将其转换为 const int 的“魔法”。

#define     WEXITSTATUS(status)        ((((*(const int *) &(status))) & 0xff00) >> 8)

错误是由于地址运算符得到了错误的操作数。本质上,这个宏不能仅与表达式或变量一起使用常量,假设这些是从文档中描述的特定源返回的值。

WEXITSTATUS 宏是 C 标准库实现的问题,而不是编译器本身的问题。通常(在 GCC 的情况下)编译器不提供 C 标准库实现。是独立包。

大多数 Linux 发行版,包括 Ubuntu,使用 glibc 作为 C 标准库实现。

在 glibc 直到版本 2.23(含)中,当使用 C++ 时,宏是按以下方式定义的,并且设置了 __USE_MISC(参见下面的提交 link):

#   define __WAIT_INT(status)   (*(const int *) &(status))

// ...

# define WEXITSTATUS(status)    __WEXITSTATUS (__WAIT_INT (status))

宏的实际实现在__WEXITSTATUS里面,但是使用__WAIT_INT好像是为了支持non-POSIX》union等待" wait 界面的变体。使用此定义,纯右值不能与宏一起使用,因为它试图获取 status.

的地址

2016 年,commit b49ab5f4503f36dcbf43f821f817da66b2931fe6 支持 union wait - 根据 1990 年代初期弃用的 NEWS 条目 - 已被删除,现在定义很简单

# define WEXITSTATUS(status)    __WEXITSTATUS (status)

现在它也适用于纯右值。

似乎 Ubuntu 16.04 仍然使用该更改之前的 glibc 版本,这并不奇怪,因为它是在提交时发布的。

我不知道 POSIX 关于是否可以使用带有 int 右值而不是变量名的宏的说法。

WEXITSTATUS 不能总是直接用于对 pclose 的调用似乎是已知问题。显然,现在 glibc 中不再存在的 above-mentioned 扩展曾经(是?)也存在于(某些?)BSD 中(并且可能源自它们?)。参见例如this question, in which the answer also expresses doubts about POSIX-compliance. However, OpenBSD, mentioned in the linked question, also removed union wait in 2014. According to the changelog 自 4.3BSD(1986 年发布)以来,它已被弃用。