目录
1. 数据类型
2. 整形在内存中的存储
2.1进制与权重
2.2数制与码制
2.3原码,补码与反码
3.大小端字节序
4.练习
整型:
char
unsigned char
signed char
short
unsigned short [int]
signed short [int]
int
unsigned int
signed int
long
unsigned long [int]
signed long [int]
注:[int]代表int可以省略。所有的short,int,long都是默认为signed类型。char类型既可以为unsigned类型也可为signed,具体取决于编译器,vs2019里为signed类型。
浮点数:
float
double
构造类型:
> 数组类型
> 结构体类型 struct
> 枚举类型 enum
> 联合类型 union
指针类型:
int *pi;
char *pc;
float* pf;
void* pv;
注:void 表示空类型(无类型) 通常应用于函数的返回类型、函数的参数、指针类型。
一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。 那接下来我们谈谈数据在所开辟内存中到底是如何存储的?
进制也就是进位记数制,是人为定义的带进位的计数方法。 对于任何一种进制---X进制,就表示每一位上的数运算时都是逢X进一位。 十进制是逢十进一,十六进制是逢十六进一,二进制就是逢二进一,以此类推,x进制就是逢x进位。
计算机里存储数据都是采用的二进制。
数制:我们把多位数码中每一位的构成方法以及从低位到高位的进位规则称为数制。
码制:为了便于记忆和查找,在编制代码时总要遵循一定的规则,这些规则就称为码制。
常用的数制有:二进制,八进制,十进制等
由于数据需要表示不止是正数,还有负数。为了表示负数,规定最前面一个不是对应权重而是表示正负,叫做符号位,剩下的表示数值叫做数值位。符号位为1代表负数,为0代表正数。这里的符号位代表正负,为码制。
例如:-5的二进制
如果按照最前面放符号位来处理二进制,就会出现两个问题。
第一:这样二进制数第一个数值代表符号是码制,后面的数值位是数制,整体就是数制与码制的混合编码。对于计算机而言,只能对数制进行加减运算无法直接处理带符号位的二进制数。
例如:(-5)+(5)显然是不会等于-10的。
第二:出现了+0和-0
为了解决上述两个问题,出现了补码,把首位改成数制而不是码制,不过权重发生了变化。这样二进制就全都是数制编码,可以直接进行数字运算。
如果10000000是有符号数的话,转为十进制就是-128(1*-2^7+0*2^6+……)。1000就是-8。
这时就可以二进制位数直接相加。
这样子不太适合我们求出二进制的补码。因此出现了反码 。
把最前面一位记为符号位时得出的二进制数为原码。反码通常是用来由原码求补码或者由补码求原码的过渡码。反码跟原码是正数时,一样;负数时,反码就是原码符号位除外,其他位按位取反。 将反码加一就得到了补码。
对于整形来说:数据存放内存中都存放的是补码。使用补码,可以直接对二进制进行运算处理。CPU只有加法器,会将减法转化为加上对应的负数。
可以看出a的存储与我们想的不一样。为什么呢?
因为在计算机系统中,以字节为单位,每个地址单元 都对应着一个字节,一个字节为8 bit。但是在C语言中还有16 bit的short 型,32 bit的long型(要看具体的编译器)大于一个字节,另外,对于位数大于8位的处理器,例如16位或者32 位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因 此就导致了大端存储模式和小端存储模式因为计算机的存储时有两种存储方式,叫做大小端字节序。
大端序(Big-Endian)将数据的低位字节存放在内存的高位地址,高位字节存放在低位地址。 这种排列方式与数据用字节表示时的书写顺序一致,符合人类的阅读习惯。
小端序(Little-Endian),将一个多位数的低位放在较小的地址处,高位放在较大的地址处,则称 小端序 。 小端序与人类的阅读习惯相反,但更符合计算机读取内存的方式,因为CPU读取内存中的数据时,是从低地址向高地址方向进行读取的。
注:低位字节和高位字节
可以看出vs2019是小端存储。
1.设计一个小程序来判断当前机器的字节序。
思路:存入值为1的变量拿到低地址的第一个字节的内容判断是否为1。
//代码1
#include
int check_sys()
{
int i = 1;
return (*(char *)&i);
}
int main()
{
int ret = check_sys();
if(ret == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
//代码2
int check_sys()
{
union
{
int i;
char c;
}un;
un.i = 1;
return un.c;
}
2.
//输出什么?
#include
int main()
{
char a= -1;
signed char b=-1;
unsigned char c=-1;
printf("a=%d,b=%d,c=%d",a,b,c);
return 0;
}
过程解析:
char a = -1;
/*
* 将-1存入a的过程:
* -1:先拿到4个字节大小二进制位
* 10000000 00000000 00000000 00000001 原码
* 11111111 11111111 11111111 11111110 反码
* 11111111 11111111 11111111 11111111 补码
* 再截取一个字节存入
* 11111111 最后a里面放置的内容
*/
signed char b = -1;
/*
* b同理:
* 11111111 最后b里面放置的内容
*/
unsigned char c = -1;
/*
* c同理:
* 11111111 最后c里面放置的内容
*/
printf("a=%d,b=%d,c=%d", a, b, c);
//对于小于四个字节的类型再遇到 %d%u 或者进行算术运算时,就会发生整形提升:
//此时整形提升的规则是把一个字节的内容取出来如果是signed类型补符号位,unsined类型补0
/*
* %d a过程
* 11111111 -->取出一个字节
* 11111111 11111111 11111111 11111111 -->根据a类型和规则进行整型提升
* %d --->将数值看成有符号的类型进行读数并打印其十进制数值
* 要打印十进制要找到原码。此时将a转为源码
10000000 00000000 00000000 00000001
* 打印-1
*/
/*
* b同理:
* 打印-1
*/
/*
* %d c过程
* 11111111 -->取出一个字节
* 00000000 00000000 00000000 11111111 -->根据c类型和规则进行整型提升
* 要打印十进制要找到原码。此时将c转为源码
00000000 00000000 00000000 11111111
* 打印
*/
3.
//输出什么?
#include
int main()
{
char a = -128;
printf("%u\n",a);
return 0;
}
过程解析:
char a = -128;
//100000000 a内存
printf("%u\n", a);
//11111111 11111111 11111111 10000000 整形提升
//%d --->将数值看成无符号的类型进行读数并打印其十进制数值
//无符号原码
//11111111 11111111 11111111 10000000
//
4.
#include
int main()
{
char a = 128;
printf("%u\n",a);
return 0;
}
过程解析:
char a = 128;
/*
* 00000000 00000000 00000000 10000000 原反补
* 截取一个字节存入
* 10000000 最后a里面放置的内容
*/
printf("%u\n", a);
//11111111 11111111 11111111 10000000 整形提升
//无符号的原码
//11111111 11111111 11111111 10000000
//
5.
int i= -20;
unsigned int j = 10;
printf("%d\n", i+j);
//按照补码的形式进行运算,最后格式化成为有符号整数
//输出什么?
过程解析:
int i = -20;
//100000000 00000000 00000000 00010100 原
//111111111 11111111 11111111 11101011 反
//111111111 11111111 11111111 11101100 补
unsigned int j = 10;
//000000000 00000000 00000000 00001010 原,反,补
printf("%d\n", i + j);
//111111111 11111111 11111111 11101100
//000000000 00000000 00000000 00001010
//111111111 11111111 11111111 11110110 i+j 补码
//111111111 11111111 11111111 11110101 反码
//100000000 00000000 00000000 00001010 原码 -10
6.
#include
int main()
{
unsigned int i;
for (i = 9; i >= 0; i--)
{
printf("%u\n", i);
}
return 0;
}
结果:无限循环打印
过程解析:
for (i = 9; i >= 0; i--)
{
//000000000 00000000 00000000 00001001 9
//000000000 00000000 00000000 00001000 8
//000000000 00000000 00000000 00000111 7
//000000000 00000000 00000000 00000110 …
//000000000 00000000 00000000 00000101
//000000000 00000000 00000000 00000100
//000000000 00000000 00000000 00000011
//000000000 00000000 00000000 00000010
//000000000 00000000 00000000 00000001
//000000000 00000000 00000000 00000000 0
// 接下来i-1的结果不是-1,而是4294967295(无符号的0xFFFFFFFF)
/*
首先计算机内部i - 1<---> i + (-1)
000000000 00000000 00000000 00000000 i 无符号
11111111 11111111 11111111 111111111 -1有符号
有符号和无符号的数相加减,整形提升有符号的数转化为无符号
11111111 11111111 11111111 111111111 结果
无符号数0xFFFFFFFF
*/
//11111111 11111111 11111111 111111111
//11111111 11111111 11111111 111111110
//……
//10000000 00000000 00000000 000000000
//……
printf("%u\n", i);
}
7
int main()
{
char a[1000];
int i;
for(i=0; i<1000; i++)
{
a[i] = -1-i;
}
printf("%d",strlen(a));
return 0;
}
过程解析:
char a[1000] = { 0 };
int i = 0;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
//1111 1111 -1
//1111 1110 -2
//1111 1101 -3
//……
//1000 0000 -128
//10111 1111 -129 截取
//0111 1111 127
//0111 1110 126
//0111 1101 125
//……
//0000 0001 1
//0000 0000 0 \0本质为0
8
#include
unsigned char i = 0;
int main()
{
for(i = 0;i<=255;i++)
{
printf("hello world\n");
}
return 0;
}
运行结果:无限打印
过程解析: