编译器在这里做什么:int a = b * (c * d * + e)?

74

我的程序出了一个奇怪的错误,在调试了几个小时后,我发现以下这一行非常愚蠢:

int a = b * (c * d *  + e)
如果您没有看到它:在de之间,我写了* +,实际上只想写一个+
为什么这可以编译通过,并且它实际上表示什么?

1
使用:https://dev59.com/b3VD5IYBdhLWcg3wI3-L#3182557 此外,还可以打印(或以其他方式使用)字符类型的整数值:std::cout << +c; 如果这种情况经常发生,那么 static_cast 会变得非常混乱。 - chris
4
如果您本意是要写一个负数,那该怎么办?请注意,这句话需要翻译成中文。 - geometrian
1
在正常算术中,2 × (3 × 4 × +5) 是什么意思? - Jon Hanna
2
@Boann 我认为这个问题并不像你想的那么简单。并非所有的“基本数学符号”都适用于编程。我们都知道,在编程时以数学思维方式思考是灾难的开始。 - Atsby
@vaxquis那个实际上更好,但它被标记为C,而且由于这个问题已经有了C++特定的答案,我们不应该改变这个标签。 - Shafik Yaghmour
显示剩余3条评论
8个回答

119

+ 被解释为一元加法操作符。它只是简单地返回其操作数的提升值。


13
有关“promoted”的更多详细信息,请参见此处。 - M.M
6
这将产生与 int a = b * (c * d * (+e)) 完全相同的效果。 - Cort Ammon

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)

32
“正值”这个词有误导性。这让人们误以为它返回操作数的绝对值,但事实并非如此。 - Chris Hayes
9
- 符号也不一定返回“负数值”: int b = -5; std::cout << -b; - MSalters
@ChrisHayes的回答已经被纠正,谢谢。 - Andreas DM
@MSalters 谢谢,我已经更正了措辞。 - Andreas DM

18
这段代码能编译通过是因为+被解释为一元加号,对于整数或枚举类型会执行整型提升操作,结果将具有提升操作数的类型。假设e是整数或未作用域的枚举类型,则由于*应用其操作数的通常算术转换,最终会进行整型提升。从C++标准草案中可以看到,在[expr.unary.op]第5.3.1节中:

一元加运算符的操作数必须具有算术、未作用域的枚举或指针类型,结果是参数的值。对于整数或枚举操作数进行整型提升。结果的类型是提升操作数的类型。

整型提升在[conv.prom]第4.5节中有所涵盖,如果变量e的类型不是bool、char16_t、char32_twchar_t,并且其转换级别小于int,则它将被第1段所涵盖。
一个除了bool、char16_t、char32_t或wchar_t之外的整数类型的prvalue,如果它的整数转换等级(4.13)小于int的等级,则可以将其转换为int类型的prvalue,如果int能够表示源类型的所有值;否则,源prvalue可以转换为unsigned int类型的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

请查看实时演示 使用wandbox上的gcc

有趣的是,C99添加了一元加号以与一元减号对称,来自于国际标准编程语言C的缘由

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

我无法想出一个好的情况,其中强制转换不足以实现相同的所需升级/转换。我引用上面的lambda示例,使用一元加号将lambda表达式强制转换为函数指针:

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

可以使用显式转换来完成:
foo( static_cast<void (*)()>( [](){} ) );

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

值得注意的是,带注释的C++参考手册(ARM)中有以下评论:

一元加号是历史遗留问题,通常没有用。


9

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

一元运算符仅对表达式中的一个操作数起作用。

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”的编译器错误。


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)等的内容。

3
这只是基本的数学运算。例如:
5 * -4 = -20

5 * +4 = 5 * 4 = 20 

-5 * -4 = 20

负数 * 负数 = 正数

正数 * 负数 = 负数

正数 * 正数 = 正数

这是最简单的解释。

减号(-)和加号(+)只是表示数值是正数还是负数。


1
不是这样的,那么这样怎么样:int a = -5; int val = -a; //结果 val: 5 - Dyrandz Famador
1
--5 变成了 4 :p -(-5) = 5..开玩笑的,我知道这只是一个打字错误..是的,你说得对 :) +1 - Dyrandz Famador
“—5” 是不正确的,因为 “5” 是一个 prvalue。 - L. F.

-1
在编程中,d和e之间的+运算符将被视为一元+运算符,仅确定e的符号。因此,编译器将按以下方式查看此语句:
int a = b*(c*d*e) ;

1
怎么做到的?这将仅确定e的符号。 - glglgl
我的意思是,如果e=5,加号将起到+5的作用。就像这样。 - nitish-d

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接