原文:个人我的博客 https://mengkang.net/1046.html
初中级 phper 有多久没给本身充电了呢,安利一波个人直播 PHP 进阶之路
须要字节对齐的根本缘由在于CPU访问数据的效率问题。由于CPU每次都是从以4字节(32位CPU)或是8字节(64位CPU)的整数倍的内存地址中读进数据的。(更深刻的缘由,谁告知下),若是不对齐的话,颇有可能一个4字节int
须要分两次读取。具体演示看下面的实验。php
按各数据类型自身大小进行对齐。变量的内存地址正好位于它长度的整数倍html
#include <stdio.h> int main(int argc, char const *argv[]) { char a = 1; // 0x7fff5fbff77f,sizeof(a):1 int b = 1; // 0x7fff5fbff778,sizeof(b):4 int c = 1; // 0x7fff5fbff774,sizeof(c):4 char d = 1; // 0x7fff5fbff773,sizeof(e):1 int e = 1; // 0x7fff5fbff76c,sizeof(f):4 printf("%p,sizeof(a):%lu\n",&a,sizeof(a)); printf("%p,sizeof(b):%lu\n",&b,sizeof(b)); printf("%p,sizeof(c):%lu\n",&c,sizeof(c)); printf("%p,sizeof(d):%lu\n",&d,sizeof(d)); printf("%p,sizeof(e):%lu\n",&e,sizeof(e)); return 0; }
辅助以图片说明,该图左侧是上面代码的内存图,灰色部分表示该程序未使用的内存。右侧是在上面代码的基础上在char a
后面声明了一个short f
。
从上面的实验和图上咱们能够找出如下规律:segmentfault
int
类型的变量的内存地址都是偶数(这也就是为何鸟哥微博中说的不可能存在奇数的 int 变量的地址了);short f
地址也并无紧挨着a
,而是跟自身数据大小对齐,也就是偶数地址开始申请。反过来想,若是不对齐,好比上例中的 a,b,c 三个变量的内存地址紧挨着,而CPU每次只读取8个字节,也就是说变量 c 还有最后一个字节没有读取进来。访问数据效率就下降了。网络
栈上各个变量申请的内存,返回的地址是这段连续内存的最小的地址。这是怎么回事呢?框架
咱们仍是经过实验来验证下我上面画的内存图,假如我有一个int
变量,它的值占了满了4个字节,那么它的四个字节里是怎么存放数据的,咱们用十六进制来演示0x12345678
。spa
- 为何用一个8位的十六进制来呢?由于int 4个字节,一个字节有8位,每位有0/1两个状态,那么就是2^8=256,也就是16^2。因此用了一个8位的16进制数正好能够填满一个 int 的内存。
- 为何用12345678,纯属演示方便。
我先存了变量 b,而后以 char 指针 p 来依次访问 b 的四个字节的使用状况。.net
#include <stdio.h> int main(int argc, char const *argv[]) { char a = 1; // 0x7fff5fbff777 int b = 0x12345678; // 0x7fff5fbff770 char c = 1; // 0x7fff5fbff76f printf("%p\n",&a); printf("%p\n",&b); printf("%p\n",&c); char *p = (char *)&b; printf("%x %x %x %x\n", p[0],p[1],p[2],p[3]); // 78 56 34 12 printf("%p %p %p %p\n", &p[0],&p[1],&p[2],&p[3]); // 0x7fff5fbff770 0x7fff5fbff771 0x7fff5fbff772 0x7fff5fbff773 return 0; }
变量 b 0x12345678
的最高位是0x12
,最低位是0x78
针对实验结果我又画了内存图,咱们能够看到0x12
存放在的内存地址要比0x78
的大。指针
这里呢就必须说明下 大小端模式code
因此,我当前的环境是小端序的形式。htm
为何会有大端小端之分?
这个就得问硬件厂商了,都比较任性,因此历史就这样了。
以成员中自身对齐值最大的那个值为标准。
int main(int argc, char const *argv[]) { struct str1{ char a; short b; int c; }; printf("sizeof(f):%lu\n",sizeof(struct str1)); struct str2{ char a; int c; short b; }; printf("sizeof(g):%lu\n",sizeof(struct str2)); struct str1 a; printf("a.a %p\n",&a.a); printf("a.b %p\n",&a.b); printf("a.c %p\n",&a.c); struct str2 b; printf("b.a %p\n",&b.a); printf("b.c %p\n",&b.c); printf("b.b %p\n",&b.b); return 0; }
结果
sizeof(f):8 sizeof(g):12 a.a 0x7fff5fbff778 a.b 0x7fff5fbff77a a.c 0x7fff5fbff77c b.a 0x7fff5fbff768 b.c 0x7fff5fbff76c b.b 0x7fff5fbff770
灰色表填充用来对齐,保证最后结构体大小是最长的成员的大小的整数倍。
实际工做中是否不按字节对齐的状况呢?有的,好比咱们的 rpc 框架里面进行数据传输的时候,会选择设置为紧凑型,这样就能够轻松作到跨平台,跨语言了。
在网络程序中采用#pragma pack(1),即变量紧缩,不但能够减小网络流量,还能够兼容各类系统,不会由于系统对齐方式不一样而致使解包错误。
实战举例 yar_header 中使用 #pragma pack(1) 和 attribute ((packed)) 的意义