编译器在这里做了啥:int a = b * (c * d * + e)? [复制]

Posted

技术标签:

【中文标题】编译器在这里做了啥:int a = b * (c * d * + e)? [复制]【英文标题】:What does the compiler do here: int a = b * (c * d * + e)? [duplicate]编译器在这里做了什么:int a = b * (c * d * + e)? [复制] 【发布时间】:2015-08-02 09:01:19 【问题描述】:

我的程序有一个奇怪的bug,经过几个小时的调试,我发现了以下非常愚蠢的行:

int a = b * (c * d *  + e)

如果您没有看到它:在 de 之间,我写了 * +,这里只打算写一个 +

为什么会这样编译,它的实际含义是什么?

【问题讨论】:

使用:***.com/a/3182557/962089 此外,还有打印(或以其他方式使用)字符类型的整数值:std::cout << +c; 如果这种情况经常发生,static_cast 会变得非常混乱。 提示:如果你打算写一个减号怎么办? 如果e的类型呢? 2 × (3 × 4 × +5) 在普通算术中做了什么? @Boann 我认为这个问题并没有那么简单。并非所有“基本数学符号”都适用于编程。我们都知道,在编程时从数学角度思考会导致灾难。 【参考方案1】:

+ 被解释为一元加号运算符。它只是返回其操作数的promoted 值。

【讨论】:

关于“升级”的更多细节,see here 这个效果和int a = b * (c * d * (+e))一样【参考方案2】:

一元 + 返回提升值。 一元 - 返回否定:

int a = 5;
int b = 6;
unsigned int c = 3;

std::cout << (a * +b); // = 30
std::cout << (a * -b); // = -30
std::cout << (1 * -c); // = 4294967293 (2^32 - 3)

【讨论】:

“正值”具有误导性。这听起来像是返回操作数的绝对值,但事实并非如此。 - 也不一定返回“负值”:int b = -5; std::cout &lt;&lt; -b; @ChrisHayes 回答已更正,谢谢 @MSalters 谢谢,更正了措辞【参考方案3】:

这是因为 + 被解释为一元加法,它将对整数或枚举类型执行整数提升,结果将具有提升的操作数的类型。

假设e 是一个整数或无作用域的枚举类型,无论如何都会应用整数提升,因为*通常的算术转换 应用到它的操作数上,该操作数最终在整数类型的积分促销

来自 C++ 标准草案5.3.1[expr.unary.op]

一元 + 运算符的操作数应具有算术、无范围枚举或指针类型,并且 结果是参数的值。对整数或枚举操作数执行整数提升。 结果的类型是提升操作数的类型。

积分促销在4.5部分中介绍[conv.prom]如果变量ebool, char16_t, char32_t, or wchar_t以外的类型并且转换等级小于int 那么它将被1 段所覆盖:

除 bool、char16_t、char32_t 或 wchar_t 之外的整数类型的纯右值,其整数转换 rank (4.13) 小于 int 的 rank 可以转换为 int 类型的纯右值,如果 int 可以表示所有 源类型的值;否则,源纯右值可以转换为无符号类型的纯右值 诠释。

完整的案例可以看cppreference。

一元加号在某些情况下也可以用来解决歧义,一个有趣的案例来自Resolving ambiguous overload on function pointer and std::function for a lambda using +。

请注意,对于那些引用一元 - 和负值的答案,这是具有误导性的,如本例所示:

#include <iostream>

int main()

    unsigned  x1 = 1 ;

    std::cout <<  -x1 << std::endl ;

导致:

4294967295

现场观看using gcc on wandbox。

有趣的是,从 Rationale for International Standard—Programming Languages—C:

C89 委员会从多个实现中采用了一元加号,以与一元减号对称。

而且我想不出一个好的案例来说明投射不足以实现相同的预期促销/转化。我上面引用的 lambda 示例,使用一元加号强制将 lambda 表达式转换为函数指针:

foo( +[]() ); // not ambiguous (calls the function pointer overload)

可以使用显式强制转换来完成:

foo( static_cast<void (*)()>( []() ) );

可以说这个代码更好,因为意图是明确的。

值得注意的是Annotated C++ Reference Manual(ARM)它有以下评论:

一元加号是历史性的意外,一般来说是无用的。

【讨论】:

【参考方案4】:

正如他们所解释的,(+) 和 (-) 只是用作一元运算符:

Unary operators 只作用于表达式中的一个操作数

int value = 6;
int negativeInt = -5;
int positiveInt = +5;

cout << (value * negativeInt); // 6 * -5 = -30
cout << (value * positiveInt); // 6 * +5 = 30

cout << (value * - negativeInt); // 6 * -(-5) = 30
cout << (value * + negativeInt); // 6 * +(-5) = -30

cout << (value * - positiveInt); // 6 * -(+5) = -30
cout << (value * + positiveInt); // 6 * +(+5) = 30

所以从你的代码:

int b = 2;
int c = 3;
int d = 4;
int e = 5;

int a = b * (c * d *  + e)

//result: 2 * (3 * 4 * (+5) ) = 120

【讨论】:

【参考方案5】:

为什么会编译?它编译是因为 + 被解析为一元加号运算符,而不是加法运算符。编译器尝试尽可能多地解析而不产生语法错误。所以这个:

d * + e

被解析为:

d(操作数) *(乘法运算符) +(一元加运算符) e(操作数)

然而,这个:

d*++e;

被解析为:

d(操作数) *(乘法运算符) ++(预递增运算符) e(操作数)

此外,这个:

d*+++e;

被解析为:

d(操作数) *(乘法运算符) ++(预增运算符) +(一元加运算符) e(操作数)

请注意,它不会产生语法错误,而是产生“LValue requrired”编译器错误。

【讨论】:

【参考方案6】:

为了进一步修正这里已经给出的正确答案,如果使用 -s 标志进行编译,C 编译器将输出一个汇编文件,其中可以检查实际生成的指令。使用以下 C 代码:

int b=1, c=2, d=3, e=4;
int a = b * (c * d *  + e);

生成的程序集(使用 gcc,为 amd64 编译)开头为:

    movl    $1, -20(%ebp)
    movl    $2, -16(%ebp)
    movl    $3, -12(%ebp)
    movl    $4, -8(%ebp)

因此我们可以将单个内存位置 -20(%ebp) 识别为变量 b,直至 -8(%ebp) 识别为变量 e。 -4(%epp) 是变量 a。现在,计算呈现为:

    movl    -16(%ebp), %eax
    imull   -12(%ebp), %eax
    imull   -8(%ebp), %eax
    imull   -20(%ebp), %eax
    movl    %eax, -4(%ebp)

因此,正如其他人回复所评论的那样,编译器只是将“+e”视为一元正运算。第一条 movl 指令将变量 e 的内容放入 EAX 累加器寄存器,然后迅速乘以变量 d 的内容或 -12(%ebp) 等。

【讨论】:

【参考方案7】:

这只是基本的数学。例如:

5 * -4 = -20

5 * +4 = 5 * 4 = 20 

-5 * -4 = 20

负 * 负 = 正

正 * 负 = 负

正面 * 正面 = 正面

这是最简单的解释。

减号(-)和加号(+)只是判断数字是正数还是负数。

【讨论】:

不是这样的,这个怎么样:int a = -5; int val = -a; //result val: 5 --5 变为 4 :p -(-5) = 5..开玩笑,我知道这只是一个错字.. 是的,你是对的 :) +1 —5 格式错误,因为5 是prvalue。【参考方案8】:

d 和 e 之间的 + 运算符将被视为一元 + 运算符,它将仅确定 e 的符号。 所以编译器会看到这条语句如下:

int a = b*(c*d*e) ;

【讨论】:

"这将确定只有 e 的符号" 怎么样? 我的意思是说 + 符号将作为 +5 工作,如果 e=5.. 那样。

以上是关于编译器在这里做了啥:int a = b * (c * d * + e)? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

GCC 对 int *a = 1,2,3,4,5 的语句做了啥?

二元加运算符对 C 中的 int Array 做了啥

gfortran for dummy:mcmodel=medium 到底做了啥?

(a ^ (1 << b)) 在像 C++ 这样的语言中实际上做了啥?

print(S[::2],[1::2]) 这行在下面的程序中做了啥[重复]

PHP8.0.0 中发生了啥来破坏 usort(...(int)(strlen($a)<strlen($b)));?