1.数据类型
我们如果要解释C语言的各类数据如何在内存中进行存储,就不得不介绍一下C语言的数据类型,C语言的数据类型可分为整型家族,浮点型家族,自定义家族,指针家族和空类型。
1.1整型家族
我们日常中常用的就是char int类型,他们分为:以上的形式,其中由于char字符类型的在内存中以ASCII码的形式存储,本质还是整型数据,所以也把它归为整型一类.
1.2 浮点型家族
1.3 自定义家族
1.4 指针家族
1.5空类型
以上就是C语言中常见的类型的声明形式~
各个类型有能够开辟内存空间大小不同,能够存储的数据也不同,他们都有自己的应用场景,换句话说,他们看待数据和内存空间的视角也不同~
2.整型在内存中的存储
2.1 原码,反码,补码
首先我们先了解,为什么会有原码,补码,反码这样的概念?直接引用一篇大佬的文章~什么是原码、反码和补码_原码反码补码_莪是男神的博客-CSDN博客
这里已经写的很清楚了~我们于是知道,计算机里存放的原来都是补码,接下来请看以下代码:
我们通过调试可以看到,对于a的值,内存中竟然是"倒着"的,他先把低位上的数据存放给低地址,再把高位存放的数据存放给了高地址,并不是向我们所想象的那样,按照正序存储~这是怎么一回事呢?
2.2 大端 小端存储模式
为什么会出现以上情况呢?那是因为我们的VS编译器采用小端存储模式~
为什么会有大小端之分呢?
所以,大端小端没有优劣之分,只是由于编译器和系统环境的不同,使内存有了两种不同的存放模式,大端比较容易判断数据正负,小端比较方便强制类型转换~
那如何判断我们的编译器使用的是大端还是小端呢?请看以下代码:
1.通过字符指针只访问一个字节的内存空间来判断
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;
}
int main()
{
int ret = check_sys();
if (ret == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
通过以上两种判断方式,都可以判断出为该编译器为小端存储~
2.3 EXERCISES
no.1
int main()
{
char a = -1;
signed char b = -1;
unsigned char c = -1;
printf("a=%d,b=%d,c=%d", a, b, c);
//第一个char和第二个char 都是有符号的char -1补码为11111111
//%d打印时会将其认定为负数转化为原码打印 而c是无符号字符 %d认为补码就是原码 直接输出 255
// -1 -1 255
return 0;
}
no.2
int main()
{
char a = -128;
//%u 无符号整数打印
printf("%u\n", a);
//-128补码为 1 1000 0000
//char大小只有一个字节 发生截断
//char中存放1000 0000
//由于a是有符号的 打印时进行整型提升
//前面补符号位 111111111111111111111110000000
//%u无符号整数打印 认为a无符号 直接将补码转化为数值输出
//一个很大的数出现了~
return 0;
}
no.3
int main()
{
char a = 128;
printf("%u\n", a);
//由于128 -128的特殊性,截断后二者在char类型的内存中存储相同
//a同样有符号,进行整型提升时补1 1111111111111110000000
//%u 认为他是一个无符号的数,直接输出一个很大的数
//他们两个输出的数也应该是一样的~
return 0;
}
no.4
int main() {
int i = -20;
//补码为 1111111111111101100
unsigned int j= 10;
//原码也就是补码为 1111111111111111010
//相加为 1111111111111110110
printf("%d\n", i + j);
//i整型提升 i+j为unsigned int
//%d有符号输出 将其转化为原码 1000000000001010 结果为-10
return 0;
}
no.5
int main() {
unsigned int i;
for (i = 9; i >= 0; i--)
{
printf("%u\n", i);//i是无符号整数,不可能小于0
//当i = 0,i--时,i=-1 以补码的形式存储的话为1111111111111111111 %u会认为他是一个巨大的数字
//照样输出 这也形成了死循环
Sleep(100);
}
return 0;
}
no.6
int main()
{
char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
//strlen计算字符数组的有效长度 也就是说遇到a[i]中的'\0'字符停止
//根据大转盘 -1为起始点 逆时针走 正好走255个 第256的数字为0
printf("%d", strlen(a));
return 0;
}
no.7
unsigned char i = 0;
int main()
{
for (i = 0; i <= 255; i++)
{
printf("hello world\n");
//unsigned char”值始终介于“0”到“255”范围之间。 循环将无限执行。
}
return 0;
}
3.浮点数家族在内存中的存储
猜猜下面这段代码会输出什么?
#include <float.h>
int main()
{
int n = 9;
float* pFloat = (float*)&n;
printf("n的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
*pFloat = 9.0;
printf("num的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
return 0;
}
让我们看一下结果~
我们不免产生疑问,第一次我们将一个整型变量的地址强转成一个浮点数指针,以浮点数类型输出值是0,我们对这个让他内部以浮点数方式存储,以整型方式输出,反而是一个特别大的数,这就说明,浮点数在内存中以自己独特的方式存储,并且和整型变量的存储完全不同(否则结果也不可能相差这么大)~ 那么浮点数到底在内存中怎么存储的呢?
这里大佬写的很明白了,本人讲的不如大佬好~ 【精选】【详细解说】单精度浮点数float取值范围_float 范围-CSDN博客
不过在这里指出,可以通过float.h查看浮点数存储的范围,非常方便~
这也就能解释为什么上面那个代码输出的值那么奇怪了~