目录
1. 数据类型
char //字符数据类型 1byte
short //短整型 2byte
int //整形 4byte
long //长整型 4or8byte C语言只规定了:sizeof(long)>= sizeof(int)
32位下:4byte 64位下:8byte
long long //更长的整形 8byte C99中引入的long long
float //单精度浮点数 4byte
double //双精度浮点数 8byte
//C语言有没有字符串类型?
答:C语言没有字符串类型,字符串一般都是通过char类型的数组来存储的
类型的意义:
1. 类型决定了类型开辟内存空间的大小。
2. 内存空间的视角代表了变量的类型。
1.1 类型的归类:
整型家族:
字符的本质在内存中是ASCII码值,是整型,所以划分到整型家族)
VS下: char与signed char等价
char
unsigned char
signed char
char是signed char还是unsigned char,C语言标准是未定义的,这取决于编译器的实现
short <--> signed short(等价)
unsigned short [int]
signed short [int]
int <--> signed int(等价)
unsigned int
signed int
long <--> signed long(等价)
unsigned long [int]
signed long [int]
long long <--> signed long long(等价)
unsigned long long [int]
signed long long [int]
那为什么会分为unsigned和signed类型呢?
答:在现实生活中有些数据是没有负数的,比如:身高、体重、长度
int a=0;a是一个整型,有符号整型,一个整型是四个字节==32bit
0 0000000 00000000 00000000 00000000
第一位是符号位,后面的31位才是有效位
符号位是0,表示正数
符号位是1,表示负数
有正有负的数据,比如:温度这个时候就需要signed类型的
而身高、体重、长度这些没有负数的数据
使用unsigned类型
unsigned int b=0;
00000000 00000000 00000000 00000000
32位全部是有效位
浮点数家族:
(只要是表示小数,就可以使用浮点型)
float //float的精度低,存储的数值范围较小
double//double精度高,存储的数值范围较大
构造类型:(自定义类型 - 我们可以自己创建出新的类型)
数组类型 int arr[5]; 类型是int[5]
结构体类型 struct
枚举类型 enum
联合类型 union
指针类型:
int *pi; //类型是int*
char *pc;
float* pf;
void* pv;//类型void*
空类型(void):
void 表示空类型(无类型)
通常应用于函数的返回类型(表示没有返回值)、函数的参数(表示没有不需要传参)、
指针类型(void*)
//第一个void,表示函数没有返回值
//第二个void,表示函数不需要传任何参数
void test(void)
{
printf("hehe\n");
}
int main()
{
test(1);//如果传参也不会报错,会出现警告,只不过test函数不会使用该参数,
//如果函数参数是void,那么最好不要传参数,防止写出bug
return 0;
}
2. 整形在内存中的存储
变量创建需要开辟内存空间,而内存空间的大小是根据类型决定的。
2.1 原码、反码、补码
如果我们想要知道整型是怎样在内存中存储,那我们首先需要了解到计算机中整数的三种表示形式,即:原码、反码、补码。
三种表示方法均有符号位和数值位两部分,
符号位:用0表示“正”,用1表示“负”,
数值位:
正整数的原、反、补码都相同。
负整数的三种表示方法各不相同:
原码
直接将数值转换成二进制
反码
原码的符号位不变,其他位按位取反
补码
反码+1
例:
int main()
{
int a = 20;//内存中展示:0x0113FA38 14 00 00 00
//00000000 00000000 00000000 00010100 -
// 00 00 00 14
// 0x00 00 00 14 -- 16进制
//00000000 00000000 00000000 00010100
//00000000 00000000 00000000 00010100
int b = -10;//内存中展示:0x0113FA2C f6 ff ff ff
//10000000 00000000 00000000 00001010--原码
// 0x80 00 00 0a
//11111111 11111111 11111111 11110101--反码
// 0xff ff ff f5
//11111111 11111111 11111111 11110110--补码
// 0xff ff ff f6
//所以说内存中存的是补码
return 0;
}
对于整型来说:数据存放内存中其实存放的是补码。
为什么会存放补码呢?
因为1.使用补码,可以将符号位和数值域统一处理;2.加法和减法也可以统一处理(CPU只有加法器)3.补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
对2进行说明:
怎样实现1-1呢?
1+(-1)
00000000 00000000 00000000 00000001
10000000 00000000 00000000 00000001
如果是原码相加后:
10000000 00000000 00000000 00000010 -- -2
由此可见原码相加得到的结果不是我们想要的
00000000 00000000 00000000 00000001
11111111 11111111 11111111 11111111
而补码相加后:
1 00000000 00000000 00000000 00000000 -- 32位就是0
所以说补码相加后才能得到我们想要的结果
对3进行说明:(也就是补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。)
这句话是什么意思呢?
首先我们可以从上面知道
//原码:直接通过正负的形式写出的二进制序列就是原码
//反码:原码的符号位不变,其它位按位取反得到反码
//补码:反码+1就是补码
同理我们倒推回去:
原码就等于补码-1变反码,反码符号位不变再取反得到原码。
而补码与原码相互转换,其运算过程是相同的,这句话意思就是:
补码取反+1也可以得到原码
例:
int b = -10;//内存中展示:0x0113FA2C f6 ff ff ff
//10000000 00000000 00000000 00001010--原码
//11111111 11111111 11111111 11110101--反码
//11111111 11111111 11111111 11110110--补码
//10000000 00000000 00000000 00001001 - 补码取反
//10000000 00000000 00000000 00001010 - 补码取反后+1-> 原码 -10
//或者
//11111111 11111111 11111111 11110101 - 补码-1,变反码
//10000000 00000000 00000000 00001010 - 取反变原码
2.2 大小端字节序
int main()
{
int a=20;
//
int b=-10;
return 0;
}
//我们可以在内存窗口中看到
//a的存储方式
//14 00 00 00
//b的存储方式
//f6 ff ff ff
但其实我们在上面会知道
a的十六进制表示形式是:0x00 00 00 14
b的十六进制表示形式是:0xff ff ff f6
我们会发现顺序有点不对劲,这就引出了大小端的问题
什么是大端小端呢?(取决于硬件)
大端(存储)模式,是指数据的低位保存在内存