关于C/C++前置++与后置++的区别与习题


i++和++i的区别

  1. 对于普通独立的语句,i++和++i是一样的,如:
  • i++; 等效于i=i+1;
  • ++i; 等效于i=i+1;
  1. 对于赋值语句,i++先赋值后加1,++i先加1后赋值,如:
  • i=1; y=i++; 等效于:i=1; y=i; i=i+1; 则执行完后 y=1; i=2;
  • i=1; y=++i; 等效于:i=1; i=i+1; y=i; 则执行完后 y=2; i=2;

++i 是先加后赋值;i++ 是先赋值后加;++i和i++都是分两步完成的。

因为++i 是后面一步才赋值的,所以它能够当作一个变量进行级联赋值,++i = a = b,即 ++i 是一个左值;i++ 的后面一步是自增,不是左值。

形象的理解可以是i++先做别的事,再自己加1,++i 先自己加1,再做别的事情。

int main()
{
    int a = 5;
    int i = 2;
    ++i = a;

    cout << i << endl;	// 5

    return 0;
}
  1. i++ 不能作为左值,而++i 可以。(一般来说,左值是可以放到赋值符号左边的变量。)
  • 左值是对应内存中有确定存储地址的对象的表达式的值。
  • 右值是所有不是左值的表达式的值。
  • 能否被赋值不是区分左值与右值的依据。 比如,C++的const左值是不可赋值的;而作为临时对象的右值可能允许被赋值。

左值与右值的根本区别在于:是否允许取地址&运算符获得对应的内存地址。

提问:

  1. 为什么(i++)不能做左值,而(++i)可以。

看底层源码:

// 前缀形式:
int& int::operator++()	// 这里返回的是一个引用形式,就是说函数返回值也可以作为一个左值使用
{	
  *this += 1;	// 函数本身无参,意味着是在自身空间内增加1的
  return *this;	// 反回值
}

// 后缀形式:
const int int::operator++(int)	// 函数返回值是一个非左值型的,与前缀形式的差别所在。
{
  int oldValue = *this;  //函数带参,且有另外的空间开辟
  ++(*this);  // 增加
  return oldValue;  // 返回临时对象的值
}

i++和++i哪个效率高

这个问题是分两种情况的:

  1. 对于内置数据类型,以现在的编译器的优化水平,前置++和后置++没区别的,这个可以通过看汇编代码证明。
  2. 对于自定义数据类型,像STL,前置++的效率要高于后置++,所以STL中关于iterator都是前置++的。

只有在必要时才使用后置操作符。

因为前置操作需要做的工作更少,只需要加1后返回加1后的结果即可。而后置操作符则必须先保存操作数原来的值,以便返回未加1之前的值作为操作的结果。对于int对象和指针,编译器可优化掉这项额外工作。但是对于更多的复杂迭代器类型,这种额外工作可能会花费更大代价。

因此,养成使用前置操作这个好习惯,就不必担心性能差异的问题。

知识点习题

在这里插入图片描述

在 printf() 函数中,后置++,存储的是一份拷贝出来的值,前置++,存储的是变量本身(VS编辑器)

本题相信大多数人都是对D选项有疑惑:

D选项,由++或者–运算的顺序是从右向左,故先计算++i,++i的返回值为 i本身,本应该是4,但是后面的运算却影响i的值,另外printf输出流的缓存栈(下面例题会有涉及)是在所有表达式计算完后再入栈的,只需要知道首先入栈的是i的地址里面存的值,此时i的值为5。

输出时从栈顶开始,相当于: int i = 3; ++i; ++i; printf("%d,%d",i,i);所以是 5,5;

再举一个例子,int i = 1; printf("%d,%d", i += 2, i *= 3); 在输出i之前先进行了i *= 3和 i += 2;最终i = 5;所以结果是5,5;

下面是所有选项的详解:

  • A选项考察C语言的内存对齐

内存对齐是一个比较常考察的一个部分。下面的分析都是基于32位机器考虑的。这里要注意两点:

  1. 所有的空间均需要为最大类型大小的整数倍
  2. 内存需要保持对齐

对于题目中的:

struct X {
    short s;
    int i;
    char c;
};

其中需要考虑变量s和c的对齐,很容易发现,应该是12。
但是有一些编译器存在对该形式的优化,在实际分配内存的时候,会将该结构体按照如下的结构进行分配:

struct X {
    short s;
    char c;
    int i;   
};

这样分配的内存会减少到8。

  • B选项考察double类型的比较

这一点应该是课本上的基本常识,是由于双精度丢失,所以只能够采用比较两个差绝对值是否小于一个很小的数字来确定。
判断一个双精度浮点数是否为0:if( abs(f) <= 1e-15 )

  • C选项中,在标准库中,对于char数组是无法采用这种赋值方式进行赋值的。

C数组初始化两种方式:

  • 一种逐个赋值 char a[14] ={‘H’,‘e’,‘l’,‘l’,‘o’}
  • 另一种 char a[14] = “Hello, world!”

  • D选项中,主要考察下面的内容
	int i=1;
	printf("%d,%d\n",i--,i++);

步骤如下:

  1. 把i的值1存入缓存器a
  2. i值加1,i=i+1=2;
  3. 把i的值2存入缓存器
  4. i值减1,i=i-1=1
  5. 把缓存器[ebp-0E8h]=1,入栈
  6. 把缓存器[ebp-0ECh]=2,入栈

故,打印输出为 2,1

总结:

由 ++或者 --运算的顺序是从右向左,故先计算i++,i++在计算过程中会产生缓存区,返回的值就是缓存区的值

例:

1.

	int x=2, y=3;
	printf("%d,%d\n",(x++)+y,++y);	// 等价x++ + y

第一个入栈的是y值本身,而其他表达式对其无影响,故入栈y==4
第二个入栈的是表达式的缓存区的值,为x+y=2+4=6,故入栈 6

输出值为6 4

2.

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i = 1;
    printf("%d,%d\n", ++i, ++i);    //3,3
    printf("%d,%d\n", ++i, i++);    //5,3
    printf("%d,%d\n", i++, i++);    //6,5
    printf("%d,%d\n", i++, ++i);    //8,9
    system("pause");
    return 0;
}

首先是函数的入栈顺序从右向左入栈的,计算顺序也是从右往左计算的,不过都是计算完以后在进行的压栈操作:

  • 对于第5行代码,首先执行++i,返回值是i,这时i的值是2,再次执行++i,返回值是i,得到i=3,将i压入栈中,此时i为3,也就是压入3,3;
  • 对于第6行代码,首先执行i++,返回值是原来的i,也就是3,再执行++i,返回值是i,依次将3,5压入栈中得到输出结果
  • 对于第7行代码,首先执行i++,返回值是5,再执行i++返回值是6,依次将5,6压入栈中得到输出结果
  • 对于第8行代码,首先执行++i,返回i,此时i为8,再执行i++,返回值是8,此时i为9,依次将i,8压入栈中,得到输出结果。

  1. 在C/C++ 中++(a++)为什么错误

正确答案:a++是a的自增,结果是a+1,这不是一个左值,不能再进行自增操作。

答案解析:

比如:

a+3=10

这个代码是错误的,因为这个不能确定一个储存单元。
你不能说出a+3的位置,所以这个表达式只能放在右侧,不能进行赋值赋值。

可以这样解释:

  1. (a++)本身是一个表达式没有问题
  2. ++x 是单目前缀运算符,需要作用到一个变量上
  3. 但它不能作用到一个表达式上,所以++(a++)不行
  4. 通过两句a++; ++a;应该也可以达到目的。

  1. 以下代码在C与C++的编译器下编译会出现什么问题。
int main()
{
    int a = 5;
    int i = 2;
    ++i = a;

    return 0;
}

正确解答:

  • 在C++中可以正常编译,且 i = 5
  • 在C中++i = a;会报错 error: expression is not assignable
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值