在C语言中,一元加号(+)操作符的目的是什么?

90

在C语言中,以下语句是合法的:

int foo = +4;

然而,就我所知,+4 中的一元加号 (+) 是一个无操作(no-op)。是这样吗?


5
在括号内紧随一元加操作符可以强制对其中运算进行分组。该操作符用于涉及超过一个结合律或交换律的二元运算符的表达式。操作数必须具备算术类型,其结果为操作数的值。整型操作数会经历整形提升,结果类型是提升后操作数的类型。 - Tim S.
6
K&R 表示该项仅为标准中的对称性而添加。 - Aaron Yodaiken
2
@Jeremy:有。例如,它表明+short(1)的类型为int而不是short - MSalters
1
@TimS.: 在括号内前置一元加操作符会强制执行括号内的操作。--哦?是括号而不是加号强制分组。 - Keith Thompson
@MSalters:+short(1)是语法错误。你是不是想要+(short)1 - Keith Thompson
显示剩余3条评论
8个回答

84

你可以将其用作表达式拥有算术类型的一种断言:

#define CHECK_ARITHMETIC(x) (+(x))

如果x的值为指针(例如),则这将生成编译时错误。

这是我能想到的唯一实际用途。


7
你也可以通过这种方式将枚举值转换为整数值。 - GManNickG
2
为什么你不能用例如(-(-(x)))做同样的事情? - Aaron Yodaiken
8
@luxun @zneak:有趣的想法... 让我看看... 好的,这样怎么样?如果"int"是32位且"x"恰好是一个等于"-2^31"的"int",那么"-x"将会导致有符号整数溢出,这在技术上是未定义行为。 :-) - Nemo
4
@zneak的意思是,如果表达式具有指针类型,则目的是在编译时触发错误,但如果它具有算术类型,则不执行任何操作。你的版本做得很好,但对于恰好计算为INT_MIN的算术表达式来说除外。(理论上是如此,在实践中它可能在任何现实机器上都能正常工作。)尽管如此,标准的严格解读是这些情况是不同的。 - Nemo
1
即使你不在赋值语句中使用它,仅在 xINT_MIN 时评估 -x 就属于调用未定义行为,我相信。 - Nemo
显示剩余11条评论

43

根据C90标准6.3.3.3的规定:

一元操作符+的结果是其操作数的值。对操作数执行整数提升,并且结果具有提升后的类型。

以及

一元操作符+或-的操作数必须具有算术类型。


2
那么,除非 x 的大小小于 int 的大小,否则 +x 是无操作的? - zneak
36
这些标准引述表明,一元加号不只是一个空操作符,它会对操作数进行整数提升。而且,可能更为重要的是,它会将左值转换为右值。 - Sander De Dycker
理论上讲,sizeof(short) == sizeof(int) 是可能的,但是 short 有填充位,在这样的系统上,填充位可能需要被清零或者符号扩展。理论上而已。 - Dietrich Epp
请注意,当它说算术类型时,它指的是整数类型和浮点类型,Nemo示例的工作原理是因为指针在此分类之外。整数类型和指针形成标量类型。 - lccarrasco
这对floatdouble没有任何影响,对吗? - S.S. Anne
为什么不直接显式地转换数值呢? - undefined

39

我知道一种非常有用的一元加运算符用法:在宏中使用。假设你想要执行以下操作:

#if FOO > 0
如果FOO未定义,C语言要求在这种情况下将其替换为0。但是,如果FOO被定义为空定义,则上述指令将导致错误。您可以使用以下方式代替:

#if defined(FOO) && FOO == 1
#if FOO+0 > 0

现在,该指令在 FOO 未定义、定义为空或定义为整数值的情况下语法上都是正确的。

当然,这是否会产生所需的语义是一个完全不同的问题,在某些有用的情况下可能会产生所需的结果。

编辑:请注意,您甚至可以使用此方法来区分 FOO 定义为零与定义为空的情况,例如:

#if 2*FOO+1 == 1
/* FOO is 0 */
#else
/* FOO is blank */
#endif

1
你确定这些预处理指令与C语言中的一元+运算符有关吗?在你的第二个例子中,如果FOO为空白,那么表达式将不是有效的C语言。 - zneak
2
确实如此。无论如何,运算符对“- -”(空格必要!)在我看来等同于“+”,使得“+”变得多余...不,这是错误的,当操作数为“INT_MIN”时它并不等价于“+”... :-) - R.. GitHub STOP HELPING ICE
2
抱歉打扰了这么老的答案,但这不是二元加法的例子吗,而不是一元加法? - Aurora Vollvik
2
@AuroraVollvik:如果宏定义为空,则为一元加号。 - R.. GitHub STOP HELPING ICE
2
@UndefinedBehavior:它是一元的还是二元的取决于FOO的定义方式。 - R.. GitHub STOP HELPING ICE
显示剩余8条评论

19

我发现一元运算符+的两个作用是:

  • 整数提升
  • 将左值转换为右值

整数提升示例:

#include <stdio.h>

int main(void) {

    char ch;
    short sh;
    int i;
    long l;

    printf("%d %d %d %d\n",sizeof(ch),sizeof(sh),sizeof(i),sizeof(l));
    printf("%d %d %d %d\n",sizeof(+ch),sizeof(+sh),sizeof(+i),sizeof(+l));
    return 0;
}

典型输出(在 64 位平台上):

1 2 4 8
4 4 4 8

将左值转换为右值的示例:

int i=0,j;

j=(+i)++; // error lvalue required

1
最终,有一个可能有用的应用程序,可以通知一些通用代码关于整数提升后参数大小可能不是 sizeof(int)。我不知道什么时候会用到它。但还是很好的。注意:编辑答案以显示并非所有内容都转换为 4。 - Persixty

18
基本上是这样的。它主要是为了完整性,并使这样的结构看起来更加清晰:
int arr[] = {
    +4,
    -1,
    +1,
    -4,
};

2
没有一种比没有一元的+更干净的替代方法。 - ruohola

15

不完全是空操作

一元运算符+只做了一件事:应用了整型提升。由于如果操作数在表达式中使用时本来就会发生这种提升,因此人们认为C语言中的一元+仅仅是为了和一元-保持对称。

由于整型提升通常被广泛应用,很难看到它的作用。

我想出了这个例子:

printf("%zd\n", sizeof( (char) 'x'));
printf("%zd\n", sizeof(+(char) 'x'));

在我的Mac上,这个命令会打印出

1
4

2
这对于使用std::cout在C++中打印char很有用。 - S.S. Anne

14
在C语言中,一元加号的目的是什么?
为了与一元减号对称,一元加号被添加到C语言中。这个决定来源于《国际标准——编程语言——C语言》的基本原理
虽然它不是一个空操作,但它会对其操作数执行整数提升。引用我对一元加号运算符是否进行类型转换?的回答:
草案C99标准第6.5.3.3节“一元算术运算符”说:
一元加运算符的结果是其(提升后的)操作数的值。 对操作数进行整数提升,并且结果具有提升后的类型。

值得指出的是带注释的C++参考手册(ARM对一元加号提供了以下评论:

一元加号是一个历史意外,通常没有用。


在C++中,当使用static_cast可以更清晰地表达意图时,它也会被"滥用",例如使用+解决函数指针和std :: function的lambda的歧义重载 - Shafik Yaghmour

1
“no-op”是指汇编指令吗?如果是的话,那肯定不是。+4就是4 - 编译器不会添加任何其他指令。

2
我所说的“noop”是指“什么也不做”,我希望编译器不会为了增加指令而添加指令。 - zneak
3
但是 +(char)4(char)4 不是相同的。 - DigitalRoss

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