前言
(1)前几天,我刷视频无意之间看到一个视频说,sizeof(a++),这个a是会自增吗?
(2)如果有经验的人肯定会说,不会自增,这是常识。那么将这句话转化为汇编之后会发生什么呢?为什么a不会进行自增呢?
将sizeof(a++)转换为汇编
(1)既然我们要探究为什么sizeof(a++)中的a不会进行自增,那么最好的办法就是,将C语言转换成汇编,看看最终执行效果。
(2)这是为什么呢?我去查阅了C语言标准,其中有这么一段话:
When applied to an expression, sizeof does not evaluate the expression (i.e. the expression is an unevaluated operand) (since C++11), and even if the expression designates a polymorphic object, the result is the size of the static type of the expression. Lvalue-to-rvalue, array-to-pointer, or function-to-pointer conversions are not performed. Temporary materialization, however, is (formally) performed for prvalue arguments: the program is ill-formed if the argument is not destructible. (since C++17)
翻译结果:当应用于表达式时,sizeof不计算表达式 (即表达式是未计算的操作数) (C++11 起),并且即使表达式指定多态对象,结果也是表达式的静态类型的大小。不执行左值到右值、数组到指针或函数到指针的转换。然而,临时物化(正式)是对纯右值参数执行的:如果参数不可破坏,则程序是格式错误的。 (自 C++17 起)
(3)由此可见,C语言标准决定了sizeof之后的式子是不会进行任何的计算的。
为什么sizeof后面的式子不进行计算
用编译器的时间换运行态的时间
(1)这个时候可能有人就会有疑问了。为什么sizeof后面的式子不进行计算呢?
(2)通过和大佬们交流,我得知了一个名词,叫做用编译器的时间换运行态的时间。
(3)这个时候可能有人就有疑惑了。编译器时间是什么呢?运行态时间又是什么呢?我在stack overflow上找到了非常好的解释。
<1>编译时:您(开发人员)编译代码的时间段。
<2>运行时间:用户运行您的软件的时间段。
(4)当你调用sizeof 的时候,核心目的是为了知道这个变量所占几个字节。那么,一个数据,进行自增或者自减,是不会影响到他的类型的,他占据几个字节也不会有影响。所以为了提高代码执行速度,我们可以只进行判断变量a 的所占字节即可,他的运算是可有可无的。
时间交换可能会产生的不良后果
(1)现在,我们知道了可以使用编译器时间来换取运行时间,来提高用户体验,那么这样做,真的就非常完美了吗?
(2)答案肯定是不,为什么呢?因为,想单片机程序,如果我想让一个单片机进行一个1ms的延时。于是我就让变量a进行不断的自增,最终实现延时效果。
//假设这是一个延时1ms的程序
void delay(void)
{
int a;
for(a=0;a<50000;a++);
}
(3)但是,如果是玩过MSP430的同学就会发现,不对,有问题。这个并没有延时效果,我们会需要调整编译器优化等级才可能出现延时效果。
(4)这是为什么呢?原因很简单,如果你编译器优化等级过高,而编译器发现for(a=0;a<50000;a++);这个语句什么都没做,可能就会放弃掉这个语句。最终导致无法产生想要的延时效果。
(5)编译器除了放弃一些认为不用的程序以外,还做了什么吗?举个例子,假如一个程序里面有一个运算式子:a = 2+2+4 ,那么编译器会直接将2+2+4计算出来,并且将8传给a。这样就能够降低运行状态的时间,提高用户体验。
总结
(1)sizeof(a++)这个运算式子算是编译器时间换运行态时间的一种方式,这样做能够提高用户体验。
(2)但是,有可能程序员一开始的目标就是要用来延时的,所以容易产产生负面效果。
(3)因此,我们应该对这个有充分的了解。根据需求做决定。