整型在内存中的存储
在前面的学习中我们知道2进制的表示方法有3种,分别是原码,反码,补码 。
而三种表示方法均为有符号位or无符号位,**0表示正,1表示负号。**且正数的原码,反码,补码相同。**负数的反码等于除符号位其余位取反,而补码等于反码加一。**包括整型提升等等......
大小端字节序的判断
c
int main()
{
int a = 2;
int b = *(&a);
printf("%d", b);
}
上述代码是我们开始的一个前提,当我们上述方法打印没有问题,如果转为(char *)类型时,它仅会保留低字节的的地址。这样打印就会改变原来的值。
概念
c
1.整数在内存中的存储
所有数字都是以补码的形式来进行存储的
当超过一个字节的数字进行存储时,就会生成大小端的问题
0x 11 22 44
高位 -> 低位
内存 低位 -> 高位
44 22 11 小端存储,低位字节内容存到内存的低地址处
11 22 44 大端存储,低字节的内容存到内存的高地址处
c
例如:⼀个 16bit 的 short 型 x ,在内存中的地址为 0x0010 , x 的值为 0x1122 ,那么
0x11 为⾼字节, 0x22 为低字节。对于⼤端模式,就将 0x11 放在低地址中,即 0x0010 中,
0x22 放在⾼地址中,即 0x0011 中。⼩端模式,刚好相反。我们常⽤的 X86 结构是⼩端模式,⽽
KEIL C51 则为⼤端模式。很多的ARM,DSP都为⼩端模式。有些ARM处理器还可以由硬件来选择是
⼤端模式还是⼩端模式。
练习1
如何设计一个程序来判断当前机器的字节序
思路:这就要用到我们开始的那个问题上,我们可以考虑将其转换为char *类型,取低字节的数并打印。
c
0x 00 00 00 01怎样存储的
int check_sys()
{
int a = 1;
return *(char*)&a;//(char)a显然错误,直接截断为0x 01 对值进行操作
}
int main()
{
int ret = check_sys();
if (ret == 1)
{
printf("小端排序\n");
}
else
{
printf("大端排序\n");
}
return 0;
}
还有方法吗?当然,我们也可以用联合体的概念,因为联合体中数据时在一起存储的,利用这个概念,我们即可实现
c
int check_sys()
{
union
{
int i;
char c;
}un;
un.i=1;
return un.c;
}
关于char类型的一些笔试题
我们都知道char类型占1个字节。那他是有符号还是无符号的?这是取决与编译器的,在vs中是有符号的,那么取值范围呢?首先要理解第8位为符号位,其余7位是数值位。
懂这张图,理解下面题目便会事半功倍。
c
char 的取值范围,-128~127 (1个字节)
在vs中,默认char 是 signed char ,而 int 是 unsigned char
%d 以十进制打印有符号整型
%u 以十进制打印无符号整型
- 1
c
举例
int main()
{
char a = -1;
//a(int) 1000 0000 0000 0000 0000 0000 0000 0001
//反码 1111 1111 1111 1111 1111 1111 1111 1110
//补码 1111 1111 1111 1111 1111 1111 1111 1111
//char a 1111 1111
//整型提升 1111 1111 1111 1111 1111 1111 1111 1111
//取反加一打印 1000 0000 0000 0000 0000 0000 0000 0001
//-1
signed char b = -1;
//一样分析
unsigned char c = -1;
//c 1000 0000 0000 0000 0000 0000 0000 0001
//反码 1111 1111 1111 1111 1111 1111 1111 1110
//补码 1111 1111 1111 1111 1111 1111 1111 1111
//char c 1111 1111
//整型提升(无符号) 0000 0000 0000 0000 0000 0000 1111 1111
//打印 255
printf("%d %d %d", a, b, c);
return 0;
}
2
c
int main()
{
char a = -128;
//a 1000 0000 0000 0000 0000 0000 1000 0000
//反码 1111 1111 1111 1111 1111 1111 0111 1111
//补码 1111 1111 1111 1111 1111 1111 1000 0000
// char a 1000 0000
//整型提升 1111 1111 1111 1111 1111 1111 1000 0000
// 因为无符号打印,所以直接打印
printf("%u\n", a);
return 0;
}
c
int main()
{
char a = 128;
//a 0000 0000 0000 0000 0000 0000 1000 0000
// char a 1000 0000
//整型提升 1111 1111 1111 1111 1111 1111 1000 0000
// 因为无符号打印,所以直接打印
printf("%u\n", a);
return 0;
}
128的二进制形式是1000 0000。由于有符号char类型的取值范围是 - 128 到 127,128超出了这个范围。所以,
当你把128赋值给有符号char类型的变量a时,在内存中存储的二进制数是1000 0000 ,其最高位为1,在有符号数的规则下,这就代表负数。
1000 0000取反加一 10000 0000 -128
c
#include<string.h>
int main()
{
char a[1000];//-128~127
for (int i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d\n", strlen(a));
return 0;
}
找到'\0',也就是找到它的ASC码值0
a[0]=-1
a[1]=-2
a[2]=-3
a[3]=-4
a[127]=-128
127
0
128+128-1=255
5
c
unsigned char i = 0;//0~255 死循环
int main()
{
for (i = 0; i <= 255; i++)
{
printf("hello world\n");
}
return 0;
}
由于无符号类型不会出现负数,会从 0 重新开始循环
255加一会变为0
6
c
int main()
{
int a[4] = { 1, 2, 3, 4 };//0x00 00 00 01 0x00 00 00 02
//01 00 00 00 02 00 00 00
int* p1 = (int*)(&a + 1);
int* p2 = (int*)((int)a + 1);
printf("开始打印");
printf("%x,%#x", p1[-1], *p2);//小端存储,打印反着打印,p1不是数组只是整型指针-1就是减去四个字节
return 0;
}
//x64无法运行--64位--指针环境是8位--地址会从8字节变成4个字节
//地址变了,非法访问。
//或者int 改为 long long 不会截断