17题
struct node{
char a;
short b;
int c;
};
int main(void)
{
struct node s;
memset(&s, 0, sizeof(struct node));
s.a = 3;
s.b = 5;
s.c = 7;
struct node *pt = &s;
printf("%d\n",*(int *)pt);
printf("%lld\n",*(long long *)pt);
}
关键词—大小端、内存对齐、强制类型转换
内存对齐
首先我们来看这个结构体,先是1字节char,然后2字节short,然后4字节int,那么在内存中的布局应该是
大小端
什么是大小端呢,这里不具体展开,只做最简单的介绍。
我们知道 一个int
类型的数据在计算机中占4个字节(现在的大部分情况),那么如果我写下 int a = 1;
在内存中是怎么存储的呢
? 我们知道应该是000…1, 也就是31个0和一个1,那么这个1是在低地址,还是在高地址呢,我们可以写一个程序验证一下。
#include <stdio.h>
int main(void)
{
int a = 1;
printf("%p\n",&a);
char *p = (char *)&a;
for(int i = 1; i <= 4; i++,p++)
printf("%p %d\n",p,*p);
}
结果如下
可以看到我们用一个字符指针p来从a的首地址依次往下读取了4个字节,每次一个。可以看到1是在低地址。
至于我们的计算机究竟是大端还是小端,可以用lscpu
查看
可以看到是小端 。
对于小端(也就是上图的结果),我们可以理解为低字节(低位)存放在低地址 对于4字节的int a = 1,1自然是最低位,应该存放在最低地址,也就是首地址。
而大端(如网络传输中的字节序),就是高字节(高位)存放在低地址。
回到本题,经过3,5,7 三次的赋值,在内存里究竟是怎样呢?可以先自己画一下。
我们这里直接给出结果,用gdb来查看内存。
可以看到按照小端模式在内存中保存的样子。(由于memset()
可以看到a和b中间的一个字节也被清0了 )
强制类型转换
struct node *pt = &s;
printf("%d\n",*(int *)pt);
printf("%lld\n",*(long long *)pt);
第一句定义pt是一个结构体指针,指向了结构体首地址。
先强制类型转换为一个int型的指针,然后解引用。
这里就牵扯出一个问题 char *
和 int *
有什么区别呢?
可以想到的有类型不同 运算操作不同
那么在解引用时有什么区别呢?
char *解引用自然是一个char也就是一个字节
int * 解引用是一个int,4个字节并且和 int 变量相同,也有大小端之说,或者说在这里,就是按照小端进行计算的。
所以第一个printf我们是从结构体首地址取出4字节按照一个int类型打印。再根据小端模式,这个int值是这样的
(二进制)00000000 00000101 00000000 00000011
(十六进制–由高地址到低地址)00 05 00 03
我们可以算一下十进制,也就是
pow(2,0)+pow(2,1)+pow(2,16)+pow(2,18)
下面的long long *同理 只不过4个字节变成了8个字节。相信聪明的你一定能想明白吧。