~Linux C_13_变量存储布局
==== 数值变量 ====
#include<stdio.h> const int A = 10; //.rodata int a = 20; //.data static int b = 30; //.data int c; //.bss int main(void) { static int a = 40; //.data char b[] = "Hello world"; //栈 register int c = 50; //eax寄存器 printf("Hello world %d\n", c); return 0; }
程序加载运行时,.rodata段和.text段通常合并到一个Segment中,操作系统将这个Segment的页面只读保护起来,防止意外的改写。
.data和.bss在加载时合并到一个Segment中,这个Segment是可读可写的。
.bss段和.data段的不同之处在于,.bss段在文件中不占存储空间,在加载时这个段用0填充。全局变量,static变量(不管是函数里的还是函数外的)如果不初始化则初值也是0,也分配在.bss段。
==== 结构体 ====
#include <stdio.h> int main(int argc, char** argv) { struct { char a; short b; int c; char d; } s; s.a = 1; s.b = 2; s.c = 3; s.d = 4; printf("%u\n", sizeof(s)); return 0; }
结构体成员也是从低地址向高地址排列的,和数组类似。但有一点不同,结构体的各成员并不是一个紧挨一个排列的,中间有空隙,称为填充(Padding),这个结构体的末尾也有三个字节的填充,所以sizeof(s)的值是12。
填充方式:
--> a0bbccccd000 -->
合理设计结构体各成员的排列顺序可以节省存储空间:
struct { char a; char d; short b; int c; } s;
gcc提供了一种扩展语法可以消除结构体中的填充字节:
struct { char a; short b; int c; char d; } __attribute__((packed)) s;
这样就不能保证结构体成员的对齐了,在访问b和c的时候可能会有效率问题,所以除非有特别的理由,一般不要使用这种语法。
==== 联合体 ====
#include <stdio.h> typedef union { struct { unsigned int one:1; unsigned int two:3; unsigned int three:10; unsigned int four:5; unsigned int :2; unsigned int five:8; unsigned int six:8; } bitfield; unsigned char byte[8]; } demo_type; int main(void) { demo_type u = {{ 1, 5, 513, 17, 129, 0x81 }}; //联合体如果用Initializer初始化,则只初始化它的第一个成员。初始化第二个byte[8]没有效果。 printf("sizeof demo_type = %u\n", sizeof(demo_type)); printf("values: u=%u,%u,%u,%u,%u,%u\n", u.bitfield.one, u.bitfield.two, u.bitfield.three, u.bitfield.four, u.bitfield.five, u.bitfield.six); printf("hex dump of u: %x %x %x %x %x %x %x %x \n", u.byte[0], u.byte[1], u.byte[2], u.byte[3], u.byte[4], u.byte[5], u.byte[6], u.byte[7]); return 0; }
一个联合体的各个成员占用相同的内存空间,联合体的长度等于其中最长成员的长度。比如u这个联合体占8个字节,如果访问成员u.bitfield,则把这8个字节看成一个由Bit-field组成的结构体,如果访问成员u.byte,则把这8个字节看成一个数组。
联合体如果用Initializer初始化,则只初始化它的第一个成员,例如demo_type u = {{ 1, 5, 513, 17, 129, 0x81 }};初始化的是u.bitfield,但是通过u.bitfield的成员看不出这8个字节的内存布局,而通过u.byte数组就可以看出每个字节分别是多少了。
==== Bit-field ====
#include <stdio.h> typedef struct { unsigned int one:1; unsigned int two:3; unsigned int three:10; unsigned int four:5; unsigned int :2; //表示空出两位 unsigned int five:8; //之后会空出三位,但这是考虑到对齐的问题,一般只会出现在尾部。 unsigned int six:8; //到了最后,可能会因为遵循“对齐”原则而有了编译器主观的做法,导致不连续。 } demo_type; int main(void) { demo_type s = { 1, 5, 513, 17, 129, 0x81 }; //在驱动中可以大量的运用这种方式来控制各个位的值。 printf("sizeof demo_type = %u\n", sizeof(demo_type)); printf("values: s=%u,%u,%u,%u,%u,%u\n", s.one, s.two, s.three, s.four, s.five, s.six); return 0; }