“++”和“+= 1”运算符有啥区别?

Posted

技术标签:

【中文标题】“++”和“+= 1”运算符有啥区别?【英文标题】:What is the difference between "++" and "+= 1 " operators?“++”和“+= 1”运算符有什么区别? 【发布时间】:2012-10-10 21:18:42 【问题描述】:

在 C++ 的循环中,我通常会遇到使用+++=1 的情况,但我无法区分它们。例如,如果我有一个整数

int num = 0;

然后循环执行:

num ++;

num += 1;

它们都增加了num 的值,但它们有什么区别?我怀疑num++ 可以比num+=1 工作得更快,但是如何?这种差异是否足够微妙以至于可以忽略不计?

【问题讨论】:

你不应该只关心表达式的实现。编译器如何实现底层代码无关紧要,编译器将选择最快安全的方式来实现操作的含义(在本例中将 num 加 1),这对于 POD 可能完全相同。 @DeadMG 定义的行为已恢复:D 我今年 63 岁。 “UB”是什么意思? @TomWetmore Undefined behavior. 关于效率,正如我在讲座中学到的 ++num 比 num++ 更快(如果目的只是增加 num 值) 【参考方案1】:

++ 前缀或后缀运算符更改变量值。

int a = 0;
int b = a++; // b is equal to 0, a is equal to 1

或前缀:

int a = 0;
int b = ++a; // b = 1, a = 1

如果这样使用,它们是一样的:

int a = 0;
++a; // 1
a++; // 2
a += 1; // 3

【讨论】:

要明确一点,a += 1也有返回值,不过是a递增后的值。 Re a += 1 也有返回值= 也是如此。 = 返回值是使 a = b = c = 0; 等语句有效的原因。 它们没有返回值,它们是表达式并且计算结果。 @SethCarnegie:如果你想学究气,那就完全正确。 =,或者更广为人知的,operator= 本身并不是一个表达式。 a=b 是一个包含= 和两个子表达式ab 的表达式。如果分配给的表达式是用户定义的类型,operator= 是一个函数并且有一个返回类型。 @MSalters 你当然是对的,但我说的是这个具体案例,涉及ints,所以我和你都完全正确。【参考方案2】:

num += 1 相当等价于++num

所有这些表达式(num += 1num++++num)将 num 的值加一,但 num++ 的值是 num 的值 之前 它增加了。

插图:

int a = 0;
int b = a++; // now b == 0 and a == 1
int c = ++a; // now c == 2 and a == 2
int d = (a += 1); // now d == 3 and a == 3

使用任何你喜欢的东西。我更喜欢++num 而不是num += 1,因为它更短。

【讨论】:

更短当然是一个重点,但 IMO 并不像 ++a 更一致地泛化并保证不仅适用于int而且适用于任何类型的迭代器这一事实更重要。 @leftaroundabout:整数和迭代器彼此无关。我们在这里不是在谈论指针(i += 1 的正确概括是std::advance (i, 1)),并且 OP 似乎还没有足够的知识以这种方式使事情复杂化。我坚持我的观点:i += 1++i 对于整数(这是被问到的)之间的唯一区别是外观上的区别。 @AlexandreC。对于迭代器,没有std::advance(i, 1) 可以工作但++i 不行的情况。我不认为advance++i 对于迭代器的正确概括。不过,对此答案 +1。 @SethCarnegie: std::advance (i, n)i += n 的正确概括。 旁注:在我的测试中,++i 可以在某些编译器(内存中的 GCC)上生成比 i++ 更高效的程序集,因为它可以避免创建额外的临时文件。【参考方案3】:

++ 用于将 value 增加 1,而使用 += 可以增加另一个数量。

【讨论】:

问题不是关于+=,而是关于+= 1【参考方案4】:

这两个运算符都将 n 的值增加 1。当您将运算符与赋值运算符一起使用时,它们之间存在差异。

例如:

第一种情况 --后自增运算符

int n=5;
int new_var;

new_var=n++;

print("%d",new_var);

输出=5

第二种情况

int n=5;
n+=1;
new_var=n;
print("%d",new_var);

输出 =6

这与预增量运算符的结果非常相似。

第二种情况使用预增量运算符

int n=5;

new_var=++n;
print("%d",new_var);

输出 =6

【讨论】:

有两个 ++ 运算符,前置增量 (++n) 和后置增量 (n++)。您只查看了后增量。尝试与预增量进行比较。顺便说一句,在 C++ 中推荐的做法是,当两者中的任何一个都可以时,更喜欢前增量而不是后增量。【参考方案5】:

它们通常是相同的,澄清它们之间的区别没有意义。但是这两个语句的执行方式其实是不同的。 例如, a+=1 编译成汇编是 添加一个,1 并且 a++ 或 ++a 是 inc a 由于它们是两种不同的 CPU 操作,因此效率可能会略有不同。

【讨论】:

编译器也知道这一点,并在优化期间消除任何差异。 我确信编译器在优化方面遇到了很多麻烦。说清楚我是在讽刺。使用的基础指令没有区别。过去 1 亿年编写的任何编译器都可以进行这种优化,即使是人脑。 只有在单独使用后增量运算符时才适用。如果在更大的表达式中使用它,则生成的汇编代码将不同,因为语义不同。请参阅我提供的答案以获取更多信息。抱歉,我不得不对这个答案投反对票,但这是不正确的。【参考方案6】:

你们中的一些人正在接近差异,但应该非常清楚地说明:

他们是非常不同的运营商。

preincrement 和 postincrement 运算符设计用于在表达式内部更改变量的值,无论是在变量的值用于包含它的任何表达式之前还是之后。使用后增量运算符时,变量的 OLD 值用于计算封闭表达式,只有在此之后变量才会递增。

例如:

i = 10;
j = i++;  // This causes j to be 10 while i becomes 11.

这就是为什么它被称为后增量运算符。该变量在 POST (AFTER) 后递增,其值用于更大的表达式(此处为赋值表达式)。

但是,如果你这样做:

i = 10;
j = ++i; // Now both i and j will be 11 because the increment
         // of i occurs PRE (BEFORE) its value is used in the greater expression.

【讨论】:

请学习如何使用markdown。 (右侧有一个帮助按钮,说明如何使用它)。 洛基,谢谢你的提示。我通常向不需要代码示例的“人文”小组发帖!感谢您为我调整帖子。 正如您所说,在较大的表达式中或在使用结果的上下文中使用前增量和后增量运算符是不同的。但是当用作语句表达式时:++i;i++;,结果被丢弃,它们实际上是相同的。没有运算符可以在不产生结果的情况下增加对象,因此很常见的是使用 ++i;i++; 来达到此目的。【参考方案7】:

我很惊讶没有人提到至少对于旧的编译器/计算机(基本上是在 C 诞生时以及一两年之后)+= 1明显++ 慢。 ++ 是 CPU 很可能有一条指令的增量。 += 1 需要将值 1 加载到寄存器中(可能将它的值保存在某个地方)并调用加法。我不能说当前的编译器是否优化了这一点,但我怀疑他们这样做了。

【讨论】:

我会对十年的时间框架感到惊讶。也许在第一个编译器的版本 1 发布一周后,他们添加了窥视孔优化。而“显着”可能不是我会选择的形容词。 我不熟悉C编译器的历史。我知道理论上你说的可能是真的,因为在 54-57 年 Backus 领导的 FORTRAN 团队已经为该语言编写了一个优化编译器,所以 15 年后肯定有人可以编写一个优化的 C 编译器。 @chx:即使对于旧的编译器,您也不能将这种说法作为普遍真理。 C 标准中没有任何内容说明+= 1 表单要求您将值加载到寄存器中并执行加法。编译器所需要做的就是提供 C 语言指定的适当语义;这是最明显的优化之一,可以很容易地完成。您不能对性能差异做出任何笼统的陈述,只能针对特定的编译器修订做出陈述。【参考方案8】:

prefixpostfix 操作是考试题目的完美候选。

a = 0;
b = a++;  // use the value and then increment --> a: 1, b: 0

a = 0;
b = ++a;  // increment and then use the value --> a: 1, b: 1

+= 操作及其姊妹-= 是更通用的解决方案,主要用于不同的数字。甚至可以说它们在与1 一起使用时是多余的。当与1 一起使用时,它们主要充当前缀 操作。事实上,在我的机器上,它们产生相同的机器代码。您可以通过使用示例程序来尝试此操作,例如:

void foo() 
    int a, b;
    a = 0;

    // use one of these four at a time
    b = a++;          // first case (different)
    b = ++a;          // second case
    b = (a += 1);     // third case
    b = (a = a + 1);  // fourth case


int main() 
    foo();
    return 0;

并在gdb 中进行反汇编,这将给出:

第一种情况(a++)(不同)

(gdb) disassemble foo
Dump of assembler code for function foo:
   0x00000000004004b4 <+0>:     push   %rbp
   0x00000000004004b5 <+1>:     mov    %rsp,%rbp
   0x00000000004004b8 <+4>:     movl   $0x0,-0x8(%rbp)
   0x00000000004004bf <+11>:    mov    -0x8(%rbp),%eax
   0x00000000004004c2 <+14>:    mov    %eax,-0x4(%rbp)
   0x00000000004004c5 <+17>:    addl   $0x1,-0x8(%rbp)
   0x00000000004004c9 <+21>:    pop    %rbp
   0x00000000004004ca <+22>:    retq
End of assembler dump.

第二种情况(++a

(gdb) disassemble foo
Dump of assembler code for function foo:
   0x00000000004004b4 <+0>:     push   %rbp
   0x00000000004004b5 <+1>:     mov    %rsp,%rbp
   0x00000000004004b8 <+4>:     movl   $0x0,-0x8(%rbp)
   0x00000000004004bf <+11>:    addl   $0x1,-0x8(%rbp)
   0x00000000004004c3 <+15>:    mov    -0x8(%rbp),%eax
   0x00000000004004c6 <+18>:    mov    %eax,-0x4(%rbp)
   0x00000000004004c9 <+21>:    pop    %rbp
   0x00000000004004ca <+22>:    retq   
End of assembler dump.

第三种情况(a += 1

(gdb) disassemble foo
Dump of assembler code for function foo:
   0x00000000004004b4 <+0>:     push   %rbp
   0x00000000004004b5 <+1>:     mov    %rsp,%rbp
   0x00000000004004b8 <+4>:     movl   $0x0,-0x8(%rbp)
   0x00000000004004bf <+11>:    addl   $0x1,-0x8(%rbp)
   0x00000000004004c3 <+15>:    mov    -0x8(%rbp),%eax
   0x00000000004004c6 <+18>:    mov    %eax,-0x4(%rbp)
   0x00000000004004c9 <+21>:    pop    %rbp
   0x00000000004004ca <+22>:    retq   
End of assembler dump.

第四种情况(a = a + 1

(gdb) disassemble foo
Dump of assembler code for function foo:
   0x00000000004004b4 <+0>:     push   %rbp
   0x00000000004004b5 <+1>:     mov    %rsp,%rbp
   0x00000000004004b8 <+4>:     movl   $0x0,-0x8(%rbp)
   0x00000000004004bf <+11>:    addl   $0x1,-0x8(%rbp)
   0x00000000004004c3 <+15>:    mov    -0x8(%rbp),%eax
   0x00000000004004c6 <+18>:    mov    %eax,-0x4(%rbp)
   0x00000000004004c9 <+21>:    pop    %rbp
   0x00000000004004ca <+22>:    retq   
End of assembler dump.

正如您所见,即使没有打开编译器优化,它们也会产生相同的机器代码,除了第一种情况,即在movs 之后有addl。这意味着您应该使用任何您喜欢的用户,并让编译器人员完成其余的工作。

最后,请注意表亲运算符 *=/= 没有 postfixprefix 对应项。

【讨论】:

【参考方案9】:

我是 *** 的新手,但这是我的 2 便士价值。

如果问题是关于 += 而不是 +=1。发表的声明是;

我通常会遇到使用 ++ 或 +=1 的情况,但我无法区分它们。

我认为 1 很容易成为另一个数字,或者写成 += 更好?

就结果而言,没有区别(使用海报值)。两者都将递增 1,但是,++ 只会递增 1,而 += 会递增 coder 指定的值,在 ederman 的示例中,这恰好是 1。例如:

// Example 1:
num = 0;
num = ++;
// the result of num will be 1

// Example 2:
num = 0;
num = += 1;
// the result of num will be 1 the same as example 1

// Example 3:
num = 0;
num = += 2;
// the result of num will be 2.

// Example 4:
num = 0;
num = ++ 2;
// this would not compile as ++ will not except any value for the increment step it is assumed
// you will always want to increment by the value of 1

因此,如果您只想将值增加 1,我会使用 ++,但如果您需要增加 1,请使用 +=

希望有用。

【讨论】:

【参考方案10】:

这两个运算符可能看起来很相似,但它们却大不相同。

对于原始类型(指针、整数等),它们都将值加一。但是,对于 C++ 类,它们调用不同的运算符(operator+=operator++);实际上,对于某些类,例如list&lt;T&gt;::iteratori += 1 不起作用,必须使用i++

此外,它们产生不同的值。 i += 1 在递增后产生i(如预递增),而i++ 在递增前产生i。因此,

int a = 0, b = 0;
cout << (a+=1) << " " << b++ << endl;

打印1 0。因为i += 1 相当于一个预增量,所以在某些情况下,i += 1 可能会导致与i++ 不同的行为。

因此,虽然它们对于递增变量是相同的,但应该注意它们并非在所有条件下都可以完美替代。

【讨论】:

以上是关于“++”和“+= 1”运算符有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

SQL 中的 NOT 和 != 运算符有啥区别?

== 运算符和 equals() 有啥区别? (使用哈希码()???)

JavaScript 中的 != 和 !== 运算符有啥区别?

四元运算符和长运算符有啥区别

+=和=+ C赋值运算符有啥区别[重复]

赋值运算符和复制构造函数有啥区别?