我的程序中有一个奇怪的错误,经过几个小时的调试后,我发现了以下非常愚蠢的行:
int a = b * (c * d * + e)
如果您没有看到它:在d
和e
之间我写了* +
,其中只有+
。
为什么这会编译,它实际意味着什么?
答案 0 :(得分:116)
+
被解释为一元加运算符。它只返回其操作数的promoted值。
答案 1 :(得分:27)
一元+
会返回提升值
一元-
返回否定:
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)
答案 2 :(得分:18)
这是编译因为+
被解释为一元加号,它将对整数或枚举类型执行整数提升,结果将具有提升操作数的类型。
假设e
是一个整数或无范围的枚举类型,最终会应用积分促销,因为*
将通常的算术转换应用于其操作数,最后是整体类型的整体促销。
来自草案C ++标准5.3.1
[expr.unary.op] :
一元+运算符的操作数应具有算术,无范围枚举或指针类型和 结果是参数的值。对整数或枚举操作数执行整体提升。 结果的类型是提升的操作数的类型。
4.5
[conv.prom] 部分涵盖了整体促销活动,如果变量e
是bool, char16_t, char32_t, or wchar_t
以外的类型且具有转换排名小于 int 然后它将由段落1
涵盖:
除bool,char16_t,char32_t或wchar_t之外的整数类型的整数转换的整数转换 如果int可以表示all,则rank(4.13)小于int的rank可以转换为int类型的prvalue 源类型的值;否则,源prvalue可以转换为unsigned类型的prvalue 中间体
对于一整套案例,我们可以查看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
值得注意的是,从 Rationale for International Standard—Programming Languages—C开始,将一元加号添加到C99中以获得一元减号的对称性:
一元加上被C89委员会从几个实施中采用,用于对称与一元减去。
我无法想出一个好的案例,即施法不足以实现同样的所需促销/转换。我上面引用的lambda示例,使用一元加号强制将lambda表达式转换为函数指针:
foo( +[](){} ); // not ambiguous (calls the function pointer overload)
可以使用显式转换来完成:
foo( static_cast<void (*)()>( [](){} ) );
可以说这个代码更好,因为意图是明确的。
值得注意的是Annotated C++ Reference Manual(ARM)它有以下评论:
一元加上是一次历史性事故,通常没用。
答案 3 :(得分:9)
正如他们所解释的那样,(+)和( - )只是用作一元运算符:
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
答案 4 :(得分:4)
为什么要编译?它编译是因为+
被解析为一元加运算符,而不是加法运算符。编译器尝试尽可能地解析而不生成语法错误。所以这个:
d * + e
解析为:
d
(操作数)*
(乘法运算符)+
(一元加运算符)
e
(操作数)然而,这:
d*++e;
解析为:
d
(操作数)*
(乘法运算符)++
(预增量运算符)
e
(操作数)此外,这:
d*+++e;
解析为:
d
(操作数)*
(乘法运算符)++
(预增量运算符)
+
(一元加运算符)
e
(操作数)请注意,它不会产生语法错误,但会产生“LValue requrired”编译器错误。
答案 5 :(得分:4)
为了进一步扭曲这里已经给出的正确答案,如果使用-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)等的内容。
答案 6 :(得分:3)
这只是基本的数学。例如:
5 * -4 = -20
5 * +4 = 5 * 4 = 20
-5 * -4 = 20
否定*否定=正面
正面*负面=负面
正面*正面=正面
这是最简单的解释。
减号( - )和加号(+)只是告诉数字是正数还是负数。
答案 7 :(得分:-1)
d和e之间的+运算符将被视为一元+运算符,它将仅确定e的符号。 所以编译器会看到如下声明:
int a = b*(c*d*e) ;