2.2 大小端介绍
什么是大端小端:
大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;
小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,保存在内存的高地址中。
讲简单点:
为什么有大端和小端:
这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit。但是在C语言中除了8 bit的char之外,还有16 bit的short型,32 bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。
我们可以想到,超过了一个字节序就会出现排放顺序的问题。
如果我们从内存中拿出来这个数字乱序肯定是不行的,因此就只剩下正着存和反着存。
因此就定义了大端字节序存储,小端字节序存储。
我们再进到 vs 编译器中看是如何存储的
2.3 练习
i>写出一个检测大小端的函数
//如果是大端返回0 //如果是小端返回1 #include <stdio.h> int check_sys() { int a = 1; return *(char*)&a; } int main() { if (1 == check_sys()) printf("小端\n"); else printf("大端\n"); return 0; }
分析:
int 类型是4个字节,要取出1个字节我们对 a 的地址强转为 char* 再解引用,这样取出的数字不是 1 就是 0 。
ii>看这段代码给出结果
#include <stdio.h> int main() { char a = -1; signed char b = -1; unsigned char c = -1; printf("a = %d, b = %d, c = %d\n", a, b, c); return 0; }
效果展示:
注:%d打印的是十进制的有符号数,%u打印的是十进制的无符号数。
如果(unsigned)char 类型变量赋值为负数,再用 %u 打印,是把负数整型提升后的补码当作原码解读后来打印。
Q:为什么是这样的值呢?
分析:
这里的 signed char b 和 char a 是一样的。
引申:
char 的取值范围:
char 的取值范围是一个环状的,取出的值只会在这个范围里。
iii> 看这段代码给出结果
#include <stdio.h> int main() { char a = -128; printf("%u\n", a); return 0; }
效果展示:
Q:这是为什么呢?
A:如果(unsigned)char 类型变量赋值为负数,再用 %u 打印,是把负数整型提升后的补码当作原码解读后来打印。
iv> 看这段代码给出结果
#include <stdio.h> int main() { int a = -20; unsigned int b = 10; printf("%d\n", a+b); return 0; }
效果展示:
因为 %d 打印的是有符号数。
v> 看这段代码给出结果
#include <stdio.h> #include <windows.h> int main() { unsigned int i = 1; for (i = 9; i >= 0; i--) { printf("%u\n", i); Sleep(1000); } return 0; }
效果展示:
分析:
这段代码会死循环。
vi> 看这段代码给出结果
#include <stdio.h> #include <string.h> int main() { char a[1000]; int i = 0; for (i = 0; i < 1000; i++) { a[i] = -1 - i; } printf("%d", strlen(a)); return 0; }
效果展示:
分析:
二进制不断 +1/-1 是在这个圆上不断轮回。
vii> 看这段代码给出结果
#include <stdio.h> unsigned char i = 0; int main() { for (i = 0; i <= 255; i++) { printf("hehe\n"); } return 0; }
效果展示:
分析:
光标一直在闪烁,说明代码是死循环的。unsigned char 的取值范围是 0 ~ 255的,i<=255的条件是恒成立的,因为i++不断执行都是在轮回。