对i++,i--,++i,--i深刻认识以及printf()函数打印的过程

本文通过一个具体的C语言示例,详细分析了i++、--i等运算符的行为及printf函数的工作原理,揭示了这些运算符在实际编程中的执行顺序与效果。

对i++,i–,++i,–i深刻认识以及printf()函数打印的过程

转载自链接: link.

先来看一个例子:

#include <stdio.h>

int main()
{
    int i = 0;
    printf("%d,%d,%d\n", i++, --i, i++);
}

有一部分人认为打印的结果应该是0,0,0.
下面是DEVC++运行出来的结果:
在这里插入图片描述
大家心里肯定会有疑惑,为什么会是这样的结果?我们不妨先来分析一下。

大家都很清楚,printf()这个函数在打印参数内容时, 是从右往左的,当有多个参数时(依次入栈的原因),printf()函数执行时是遍历一个参数打印一个参数呢?还是先遍历一遍参数然后在依次打印呢?大家有没有考虑过呢?

我们不妨假设printf()函数执行时遍历一个参数打印一个参数

  1. 首先处理的是最右边的表达式i++,打印出其结果为0,而后i的值变为1;
  2. 接下来处理的是表达式- -i,由于i的值已变为1,所以打印的结果为0,而后i的值变为0;
  3. 最后处理的是最左边的表达式i++,此时i值为0,所以打印的结果为0,而后i的值变为1。

通过上面的分析,可以看出打印的结果是0,0,0,可真的是这样吗?

我们先来看一下上述代码的汇编代码:
在这里插入图片描述

1、在红色框里,处理表达式i++时,先通过寄存器eax先将内存中i的值(为0)放入一个系统临时开辟的整型大小的地址空间(ptr
[ebp-0D0h]开始的地址空间)中保存起来,之后通过寄存器ecx将内存中i的值加1再将加1后的结果放到i原来的内存中去,此时内存中i的值变为1;

在这里插入图片描述

2、红色框里的指令是用来处理表达式–i的,可以看出在处理表达式–i时,是直接通过寄存器edx将内存中i的值减1之后再将减1后的结果放到i原来的内存中去,此时i的值变为0.;

在这里插入图片描述

3、接下来处理最左边的表达式i++,可以看出红色框内的代码和处理最最右边的表达式i++的代码几乎一样,只是存放i值的地址不一样,这次是通过寄存器eax将i的值0存放到内存dword
ptr [ebp-0D4h]开始的内存单元中。

接下来便是入栈了:
在这里插入图片描述

第一步是通过寄存器edx将内存ptr [ebp-0D0h]开始的连续四个地址空间的值入栈,该值为0;

第二步是将内存中i的值通过寄存器eax入栈,此时i的值为1;

第三步通过通过寄存器edx将内存ptr [ebp-0D4h]开始的连续四个地址空间的值入栈,该值为0。

之后系统将当前指令的下一条指令的地址入栈(现场保护),然后调用printf()函数依次打印上述表达式的结果,得到的结果是0,1,0

总结:

对于前置++、–处理的时候系统会直接操作不产生临时量,只需三条代码;对于后置++、–处理的时候会先产生一个临时量,而后通过临时量返回结果,需要五条代码,所以在同样的环境下前置++或–要比后置++或–效率要高。
对于printf()函数的处理过程,我们可以看到,是先将参数从右到左遍历一遍,最后才执行打印操作,而不是遍历一个打印一个。

<think>在C语言中,`i++`和`++i`都是自增运算符,用于将变量`i`的值增加1,但它们在表达式中的行为有重要区别: 1. **i++(后缀自增)**:先返回`i`的当前值,然后再将`i`的值增加1。 2. **++i(前缀自增)**:先将`i`的值增加1,然后返回增加后的值。 在for循环中,当它们单独作为循环步进表达式(即`for`的第三个表达式)时,由于不涉及表达式的值,因此效果相同。但是,如果在循环体内或条件表达式中使用,则会产生不同的效果。 ### 示例1:单独使用(效果相同) ```c // 使用i++ for (int i = 0; i < 5; i++) { printf("%d ", i); // 输出: 0 1 2 3 4 } // 使用++i for (int i = 0; i < 5; ++i) { printf("%d ", i); // 输出: 0 1 2 3 4 } ``` - 原因:在`for`循环的第三个表达式中,自增操作是独立语句,不参与表达式求值,因此两种写法效果完全一致。 ### 示例2:在表达式中使用(效果不同) ```c int i = 0; while (i < 5) { printf("i++: %d ", i++); // 输出: 0 1 2 3 4 } int j = 0; while (j < 5) { printf("++j: %d ", ++j); // 输出: 1 2 3 4 5 } ``` - 解释: - `i++`:先打印`i`的当前值,再自增(打印0,然后i变为1) - `++j`:先自增,再打印(j先变为1,然后打印1) ### 性能考虑(现代编译器优化后无区别) - 在非内置类型的重载运算符中(如C++),`++i`可能更高效,因为`i++`需要保存临时值。 - 在C语言基本类型(如`int`)中,现代编译器优化后性能相同。 ### 关键点总结 | 特性 | `i++` (后缀) | `++i` (前缀) | |--------------|------------------------|------------------------| | 返回值 | 自增前的值 | 自增后的值 | | 副作用发生点 | 表达式求值后立即自增 | 表达式求值前自增 | | 单独语句性能 | 相同(编译器优化) | 相同(编译器优化) | | 复合表达式 | 可能产生非预期结果 | 通常更符合直觉 | ### 错误使用案例 ```c int arr[] = {10, 20, 30}; int i = 0; // 错误:访问越界和未定义行为 printf("%d", arr[i++]); // 输出arr[0]=10,然后i=1 printf("%d", arr[++i]); // 输出arr[2]=30,因为i先变成2 // 正确:明确拆分步骤 int j = 0; printf("%d", arr[j]); // 输出10 j++; // j=1 j++; // j=2 printf("%d", arr[j]); // 输出30 ``` §§相关问题§§ 1. 在while循环条件中使用`i++`和`++i`会有什么不同效果? 2. 对于自定义数据类型(如C++类),为什么推荐使用`++i`而不是`i++`? 3. 表达式`a = i++ + ++i`为什么是未定义行为? 4. 在函数调用参数中同时使用多个自增运算符可能导致什么问题? 5. C语言中自减运算符`--`的前缀和后缀形式与自增运算符有何异同?</think>在C/C++中,`i++`(后缀自增)和`++i`(前缀自增)都用于将变量`i`的值增加1,但它们在表达式中的行为有重要区别: ### 核心区别 | 特性 | `i++` (后缀自增) | `++i` (前缀自增) | |--------------|--------------------------|--------------------------| | **返回值** | 返回自增前的原始值 | 返回自增后的新值 | | **执行顺序** | 先返回值,后自增 | 先自增,后返回值 | | **性能影响** | 可能产生临时对象(复杂类型) | 通常更高效 | ### 在for循环中的使用 当单独作为循环步进表达式时(无嵌套使用),两者**效果相同**: ```c // 两种写法完全等效 for (int i = 0; i < 5; i++) { ... } // 输出: 0 1 2 3 4 for (int i = 0; i < 5; ++i) { ... } // 输出: 0 1 2 3 4 ``` 原因:for循环的第三个表达式不关心返回值,只需完成自增操作 ### 关键区别场景(当需要返回值时) ```c int main() { int a = 5, b = 5; // 示例1:在赋值语句中 int x = a++; // x = 5, 然后a=6 int y = ++b; // b=6, 然后y=6 // 示例2:在函数参数中 printf("i++: %d\n", a++); // 输出6,然后a=7 printf("++i: %d\n", ++b); // b=7,输出7 // 示例3:复合表达式 int c = 10; int d = c++ + c++; // 未定义行为!不同编译器结果不同 int e = ++c + ++c; // 同样未定义行为! return 0; } ``` ### 性能差异(重要) 1. **基本类型(int/char等)**:现代编译器优化后无差异 2. **复杂类型(C++对象)**: ```cpp class Counter { public: Counter& operator++() { // ++i ++value; // 直接修改 return *this; // 返回自身引用 } Counter operator++(int) { // i++ Counter temp = *this; // 创建临时副本 ++value; // 修改自身 return temp; // 返回副本(额外开销!) } private: int value = 0; }; ``` - `++i` 无临时对象,效率更高 - `i++` 需构造/析构临时对象,效率较低 ### 最佳实践 1. **for循环中**:优先使用`++i`(性能习惯) 2. **独立语句**:使用`i++;`或`++i;`效果相同 3. **避免**: ```c // 禁止在复杂表达式中混用 arr[i++] = arr[++i]; // 未定义行为! // 禁止在同一变量多次自增 func(i++, i++); // 参数求值顺序未定义 ``` ### 特殊案例:指针运算 ```c int arr[] = {10, 20, 30}; int *p = arr; printf("%d ", *p++); // 输出10,p指向arr[1] printf("%d ", *++p); // p先指向arr[2],输出30 ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值