操作符的属性
1).优先级:在一个表达式中,相邻操作符,优先级高的先算。
2).结合性:如果两个相邻运算符优先级相同,这个时候看结合性了,根据运算符是左结合,还是右结合来决定运算顺序.
整型提升问题
1)整型提升
C语言中整型算数运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使⽤之前被转换为普通整型,这种转换称为整型提升。
2)整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执⾏,CPU内整型运算器(ALU)的操作数的字节⻓度⼀般就是int的字节⻓度,同时也是CPU的通⽤寄存器的⻓度。因此,即使两个char类型的相加,在CPU执⾏时实际上也要先转换为CPU内整型操作数的标准⻓度。
通⽤CPU(general-purpose CPU)是难以直接实现两个8⽐特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种⻓度可能⼩于int⻓度的整型值,都必须先转换为int或unsigned int,然后才能送⼊CPU去执⾏运算
这个整型提升是非常重要的,也就是说CPU中的ALU在进行整形运算的时候,必须至少是4个字节,如果少于4个字节,那么要整型提升,补充空位。这个在单片机,Dsp中非常常见!
下来了查资料来补充这个知识点!!!(这是关于硬件资源的!)
整型提升的规则:很简单,少于4字节空间的,需要整型提升,无符号数用0补充,有符号数用符号位补充!!!
int main()
{
//char 1个字节
char c1 = 125;
//01111101
//要运算发生整型提升
//00000000 00000000 00000000 01111101
char c2 = 10;
//00001010
//00000000 00000000 00000000 00001010
//00000000 00000000 00000000 01111101
//00000000 00000000 00000000 00001010
//00000000 00000000 00000000 10000111
printf("%d\n", c1 + c2);//135
char c3 = c1 + c2;
//c3是1个字节
//10000111
// 以%d的形式打印,发生整型提升
//1111111 11111111 11111111 10000111 ->补
//1000000 00000000 00000000 01111000 ->反
//1000000 00000000 00000000 01111001 ->原码 (-121)
printf("%d", c3); //-121
return 0;
}
算数转换问题
算数转换问题也就是说,当两个数的存储空间>=4个字节且两个数的空间大小不一样时,如果两个数相运算,则要发生算数转换,也就是空间小的要向空间大的转换!
这个也很重要,虽然是小知识点,但也是一块肉!!!
问题表达式解析
a*b + c*d + e*f;
//这个表达式怎么算?
//执行过程不能确定
c + --c;
//这个怎么算?
//这个代码有问题,这儿只能确定前置--的优先级高于+的优先级,只能说明先算的是--c,那么左边c的值是什么,是--c之后的值还是之前的值?不能确定,存在歧义!
int main()
{
int i = 10;
i = i-- - --i * ( i = -3 ) * i++ + ++i;
printf("i = %d\n", i);
return 0;
}
//上面的表达式一定存在歧,这个代码在不同的编译器上运算结果不相同!看下面测试结果!
int fun()
{
static int count = 1;
return ++count;
}
int main()
{
int answer;
answer = fun() - fun() * fun();
printf( "%d\n", answer);//输出多少?
return 0;
}
//这个代码歧义是很大的!
//首先说一下,关键字static修饰局部变量,改变的是count的存储位置,到静态区了,这个静态局部变量
//就不会销毁了,还有一点就是,所谓的生命周期变长了,作用区域没变,这都是废话!!!
//此代码存在的歧义就是answer = fun() - fun() * fun();此语句调用的时候,是先调用那个fun函数
//是不确定的!只能知道是先算乘法,再算减法!!!
#include <stdio.h>
int main()
{
int i = 1;
int ret = (++i) + (++i) + (++i);
printf("%d\n", ret);
printf("%d\n", i);
return 0;
}
//这个代码也是有歧义的,
//尝试在linux 环境gcc编译器,VS2013环境下都执⾏,看结果。
VS2022测试结果
gcc测试结果!!!
*可以通过反汇编来看一下具体在vs下是怎么执行的!!!
*这个反汇编也非常简单,首先i的地址就是ebp-8,把1放到了ebp-8所指向的单元(也就是把i存在了内存中了),接下来把i的值放到eax寄存器中,在通过add加1,放到eax中,在把eax的值放到i中,完成了一次(i++),在通过ecx和edx寄存器完成两次(i++),之后把i的值赋给eax,通过两次add计算出结果赋给ret!!!
通过上面的汇编代码分析,在VS2022下,先是完成三次i++,在求和赋给ret!!!
*这儿强调一下,读汇编语言是一个很重要的能力,如果那个语句的执行有歧义,查看汇编是一种解决问题很好的方式!!!
完结!!!