C语言基础之结构体进阶

来源:互联网 发布:java webservice 编辑:程序博客网 时间:2024/05/16 17:00

1,结构体元素的对齐规则

基本对齐方式

为了提高访问效率,结构体里变量的存储遵循一定的规则
例如:

#include <stdio.h>struct data{    char a; // 填充3个字节    int b;    short c;}s;    // 结构体变量名sint main(void){    printf("sizeof(s) = %d.\n", sizeof(s));} 

输出的结果是12,因为char填充了3个字节,short填充了2个字节
但如果加上double型变量,结果却不是12+8=20:

#include <stdio.h>struct data{    char a;     int b;    short c;    double d;}s;int main(void){    printf("sizeof(s) = %d.\n", sizeof(s));} 

而是24,原因在于:1,在64位系统下结构体元素默认是8字节对齐,而在32位系统默认4字节对齐。
2,默认的对齐方式和结构体本身元素对齐方式相比,两者取其小。
所以,第一个例子,结构体里元素最大是int,4字节,比默认的8字节小,所以4字节对齐;
第二个例子,结构体里最大元素是double,8字节,所以8字节对齐。

自定义对齐方式

#pragma pack(n) //设置结构体对齐方式,限制一个区间 n = 2的次方(0、1、2…)
如上例一,将其定义为2字节对齐:

#include <stdio.h>#pragma pack(2)   struct data{    char a;     int b;    short c;}s; int main(void){    printf("sizeof(s) = %d.\n", sizeof(s));} 

则打印结果为8,因为2比默认的8位小,所以就按2字节对齐

注意:pragma pack()只对接下来相邻的结构体有效

总结:你指定的对齐方式和结构体自身默认对齐方式,俩者取最小的;指定的对齐方式充分利用内存空间,牺牲了速度,降低了访问效率。

2,位字段

在结构体里的变量,如果用其定义一个很小的数,则会造成内存的浪费,所以用位字段将变量分成需要的大小

注意:1,位字段只可用在结构体里的unsigned,int两种类型;int使用时还要考虑到首位为符号位的因素

2,而且访问的时候只能通过变量访问,不能沟通过地址访问

#include <stdio.h>struct data{    unsigned int a : 1; //1表示你占的bit宽 a的取值范围[0, 1]     unsigned int b : 3; //3, 范围0~7}s;int main(void){    printf("sizeof(struct data) = %d.\n", sizeof(struct data));    s.a = 1;    s.b = 5;    printf("s.a = %d s.b = %d.\n", s.a, s.b);       return 0;}

如上例,输出的大小是4代表在一个int之内,并正常访问

无名字段与零字段

#include <stdio.h>struct data{    unsigned int a : 1;     unsigned int b : 3;     int : 4;   // 无名字段,不可访问    int : 0;   // 0字段,就把一个字剩下的所有位都占用并且不可访问    unsigned int c : 1;}s;int main(void){    printf("sizeof(struct data) = %d.\n", sizeof(struct data));    return 0;}

如上例,输出结果是8,因为零字段会占满32位4字节的内存,所以下一个c变量到来时,只能重新开辟一个4字节存放

3,动态数组(柔性数组)

由于在结构体里定义数组时,我们常常不知道要填入数组具体大小,所以,就要用到结构体里的动态数组

#include <stdio.h>#include <stdlib.h>#include <string.h>struct string{    int len;}s;int main(void){    char buf[] = "apple";    struct string *p = (struct string *)malloc(sizeof(s)+strlen(buf)+1);        char *p_str = strcpy((char *)(p+1), buf);    printf("p_str = %s.\n", p_str);    return 0;}

打印出apple
p+1是为了让p越过结构体s的大小,指向strlen(buf)的区域,+1是因为字符串最后有”\0”.

利用动态数组打印feibo数列

#include <stdio.h>#include <stdlib.h>#include <string.h>typedef struct soft_array{    int len;    int arr[];}S;//打印柔性数组最后一项void show_infor(S *p, int len){    printf("%d项feibo数值%d.\n", len, p->arr[len-1]);}//创建数组,并且数组是feibo数列void func(int len){    S *p = (S *)malloc(sizeof(S)+sizeof(int)*len);    int i = 0;    for (i=0; i<len; i++)    {        if (i<=1)        {            p->arr[i] = 1;        }        else if (i>=2)        {            p->arr[i] = p->arr[i-1] + p->arr[i-2];        }    }    // 打印len项feibo数列数值    show_infor(p, len);    free(p);}int main(void){    printf("sizeof(S) = %d.\n", sizeof(S)); // 4字节    int len = 10;    func(len);    return 0;}

结构体类型的指针p指向malloc开辟的动态空间,再往结构体中赋值

4,结构体里用到的两个宏(操作系统内核里的)

元素偏移量的宏

通过这个宏,可以计算出元素在结构体里的偏移量

#include <stdio.h>#define OFF_SET_OF(type,member) ((long long)(&(((type *)0)->member)))struct data{    char a;    int b;    short c;    char d;};int main(void){    struct data s;    printf("&s = %p.\n", &s);    long long off_set_of;    off_set_of = OFF_SET_OF(struct data, c);    printf("off = %d.\n", off_set_of);    return 0;}

#define OFF_SET_OF(type,member) ((long long)(&(((type *)0)->member)))

(type *)0是告诉编译器有一个type类型的指针,其地址值为0;
(type *)0->member是基址为0的指针指向tpye中的某个元素,也就是偏移量;
long long是为了能放下地址值

通过元素推算结构体首地址

#include <stdio.h>#define OFF_SET_OF(type,member) ((long long)(&(((type *)0)->member)))//由结构体里的元素地址,得到整个结构体的地址#define CONTAINER_OF(ptr, type, member)  \({typeof(((type *)0)->member) * _mptr = ptr; \((type *)((long long)_mptr-OFF_SET_OF(type, member)));})struct data{    char a;    int b;    short c;    char d;};int main(void){    struct data s;    printf("&s = %p.\n", &s);    long long  off_set_of = OFF_SET_OF(struct data, c);    s.c = 110;    short *p_c = (short *)((long long)(&s)+off_set_of);     printf("*p_c = %d.\n", *p_c);    struct data *p = CONTAINER_OF(p_c, struct data, c);    printf("p = %p.\n", p);    return 0;}

p_c是指向结构体中c元素的指针,用其减去c的偏移地址,就是结构体的首地址
宏定义的第一句是判断str指针类型是否正确

原创粉丝点击