目录
表达式求值
隐式类型转换
整型 提升
C的整型算术运算总是至少以标准(int)整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型 提升
char a,b,c; ... a = b + c;
b和c的值被提升为普通整形,然后再执行加法运算
加法运算完成后,结果被截断,然后再储存于a中
意义
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度
一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长 度。
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令 中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转 换为int或unsigned int,然后才能送入CPU去执行运算。
如何提升
//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0
#include<stdio.h> int main() { char a = 3; //000000000000000000000000000000000011 这是整型3的数据 //00000011 这是变量a的数据形式 // char b = 127; // 00000000000000000000000000001111111 //01111111 z\这是变量b的数据形式 char c = a+b; //发现a和b都是char类型的,都没有达到一个int类型的大小 //这里就发生了整形提升 整型提升 根据符号位来补1或者0 //0000000000000000000000000000000000011 a //0000000000000000000000000000001111111 b //0000000000000000000000000000010000010 // c要截断变成char 类型 10000010 printf("%d\n", c);//输出为-126 // 因为%d是表示打印整型,所以又会发生整形提升 //111111111111111111111111111111 10000010 //100000000000000000000000000000 01111110 // 因为这个数是补码,所以表示的是-126 return 0; }
char a=0xb6,它是直接认为这串十六进制是补码,直接存储到内存
不会打印a和b,因为a和b在比较运算符时都会发生整形提升
0xb6是182 0xb600是46592
a 10110110 经过整形提升 11111111 11111111 11111111 10110110 补码形式,
10000000 00000000 00000000 01001010 原码 输出 所以a以%d输出是-74
b 11111111 11111111 10110110 00000000补码
1000000 0000000 01001001 00000001 原码 b的-18689
int main() { char c = 1; printf("%u\n", sizeof(c));//1 printf("%u\n", sizeof(+c));//4 printf("%u\n", sizeof(-c));//4 return 0; }
表达式有两个属性 int a=4 int b=5 a+b 值属性9和类型属性 int
算数转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则就无法进行,下面的层次体系称为寻常算数转换
如果某个操作数的类型再上面列表的排名较低,那么首先转换为另一个操作数的类型后执行运算
#include<stdio.h> int main() {/* char a = 3; char b = 127; char c = a+b; char a1 = 0xb6; printf("%d\n", a1); printf("%d\n", 0xb6);*/ int a = 4; float f = 3.14f; printf("%f\n", a + f);//会发生算数转换 a会变成float类型 return 0; }
常见关键字
关键字 typedef
typedef 顾名思义是类型定义,这里应该理解为类型重命名。
#include<stdio.h>
typedef unsigned int u_int;
int main()
{
unsigned int num = 100;
u_int num2 = 100;//u_int 是unsigned int 的别名
return 0;
}
关键字 static
1 修饰局部变量
2修饰全局变量
3修饰函数
修饰局部变量
改变了局部变量的生命周期(本质上改变了变量的存储类型),将数据从栈区放到了静态区
#include<stdio.h> void test() { int a = 1;//因为这是个局部变量。每次进函数都会重新赋值为1 a++; printf("%d\n", a); } void test1() { static int a = 1;// a++; printf("%d\n", a); } int main() { int i = 0; while (i < 10) { test();//输出了10个2 i++; } i = 0; while (i < 10) { test1();//输出 2 3 4 5 6 7 8 9 10 11 i++; } return 0; }
修饰全局变量
修饰全局变量使这个全局变量只能在自己所在源文件(.c)内部可使用
其他源文件不能用
全局变量,在其他源文件内存可以被使用,是因为全局变量具有外部链接属性
但是被static修饰后,就变成了内部链接属性,其他源文件就不能链接到这个静态的全局变量了
#include<stdio.h> int g_val = 2022; int main() { printf("%d\n", g_val);//不能使用,因为是静态全局变量 return 0; } #include<stdio.h> static int g_val = 2022;
修饰函数
使函数只能在自己所在的源文件内部使用,不能再其他源文件使用
本质上static是将函数的外部链接属性变成内部链接属性,跟全局一样
#include<stdio.h> //extern int g_val; extern int Add(int x, int y); int main() { int a1= 10; int b1 = 20; int sum = Add(a1, b1); printf("%d\n", sum); return 0; } #include<stdio.h> static int g_val = 2022; static int Add(int x, int y) { return x + y; }
无法使用这个static 修饰的函数
define定义宏
define不是关键字
#include<stdio.h> #define ADD(X,Y) X+Y #define ADD1(X,Y) ((X)+(Y)) int main() { printf("%d\n", 4*ADD(2, 3));//11 4*2+3 定义宏只是把ADD(2,3)变成了2+3; printf("%d\n", 4 * ADD1(2, 3));//20 return 0; }