首页 > 编程笔记 > C语言笔记 阅读:17

C语言++和--的用法(自增自减运算符)

自增运算符 ++ 和自减运算符 -- 是 C 语言中的两个重要运算符。它们可以被作为前缀或后缀运算符,仅需要运算符左边或右边有一个运算对象即可。

当运算符置于运算对象左边时,被称为前缀模式,例如 ++i 和 --i。相反,当运算符置于运算对象右边时,被称为后缀模式,例如 i++ 和 i--。

自增自减的前缀模式

下面是前缀模式的一个例子:
#include <stdio.h>
int main()
{
   int a, b;
   a = 10;
   b = 10;
   printf("%d %d\n", ++a, --b);
   printf("%d %d\n", a, b);
   return 0;
}
在前缀模式下,++ 和 -- 与右运算对象构成自增和自减表达式。前缀自增表达式的结果为运算对象值加 1,前缀自减表达式的结果为运算对象值减 1。

与赋值表达式类似,自增和自减表达式会对运算对象本身产生操作。自增表达式会将运算对象加 1,自减表达式会将运算对象减 1。

因此,printf() 函数输出的值为 11 和 9(自增和自减运算符的结果),随后输出 a 和 b 的值,可以发现它们分别被加 1 和减 1 了(自增和自减运算符产生的操作):

程序的运行结果为:

11 9
11 9

要清晰地区分“表达式结果”和运算符产生的“额外操作”的概念,以便更好地理解自增和自减运算符的使用。

自增自减的后缀模式

下面是后缀模式的一个例子:
#include <stdio.h>
int main()
{
    int a, b;
    a = 10;
    b = 10;
    printf("%d %d\n", a++, b--);
    printf("%d %d\n", a, b);
    return 0;
}
在后缀模式下,++ 和 -- 与左运算对象构成自增和自减表达式。后缀自增表达式的结果为运算对象本身的值,后缀自减表达式的结果也是如此。

和前缀模式一样,后缀模式也会对运算对象本身产生操作。自增表达式会将运算对象加 1,自减表达式会将运算对象减 1。

因此,printf() 函数输出的值为 10 和 10(自增和自减运算符的结果),随后输出 a 和 b 的值,可以发现它们分别被加 1 和减 1 了(自增和自减运算符产生的操作)。

程序的输出结果为:

10 10
11 9

自增和自减表达式的操作时机

在前面的代码示例中,我们都是在 Visual Studio 中运行程序的。接下来,我们尝试在不同的编译器中运行,观察其运行结果:
#include <stdio.h>
int main()
{
    int a, b;
    a = 1;
    b = a++ + a++ + a++;
    printf("%d %d", a, b);
    return 0;
}
我们通过下面两张图可以看到,同一段代码在不同的编译器中出现了不同的结果:

在 Visual Studio 编译器中执行编译的结果为 4 3:


图 1 Visual Studio中的编译结果

GCC 编译器中执行编译的结果为 4 6:


图 2 GCC中的编译结果

在这个代码片段中,“a++ + a++ + a++” 表示变量 a 被后缀自增了三次。当我们在下一行输出变量 a 的值时,结果是 4,这个结果在 Visual Studio 编译器和 GCC 编译器中是一致的。但是,这两种编译器对于何时产生额外操作有着不同的理解。

在 Visual Studio 编译器中,表达式的求值过程如下:
此时,“a++ + a++ + a++”变成了“1 + 1 + 1”,因此变量 b 的值为 3。执行三次额外操作,即让变量 a 自增三次,因此变量 a 的值变为 4。

在 GCC 编译器中,表达式的求值过程如下:
此时,“a++ + a++ + a++”变成了“1 + 2 + 3”,因此变量 b 的值为 6。

为什么不同的编译器在同一段代码中产生不同的结果?

这是因为某些表达式在求表达式结果的同时,还会对运算对象进行操作。例如,赋值运算符和自增、自减运算符。但是在不同的编译器中,这些操作的时机是不同的。

在 GCC 编译器中,每完成一个子表达式的求值,都会对运算对象进行操作。在 Visual Studio 编译器中,所有对运算对象的操作会累积到所有子表达式求值完成后再进行。

由于对运算对象的操作没有固定的时机,因此只有一个最晚时机:在完整表达式求值结束后,在进入下一步操作之前。完整表达式是指它不是任何一个表达式的子表达式。例如,表达式 b = a++ + a++ + a++ 是一个完整表达式,而 a++ 是它的一个子表达式。

因此,编译器只需要保证在这个表达式结束并进入下一步操作之前,对运算对象进行操作即可。

在这种情况下,前缀模式和后缀模式都会受到影响。例如,表达式 b = ++a + ++a + ++a 在不同的编译器中也可能产生不同的结果。

为了避免编写这种可能在不同编译器下产生不同结果的代码,不要在一个表达式中重复对一个变量进行自增或自减操作。

相关文章