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指针类型是否正确
- C语言基础之结构体进阶
- c语言基础之结构体
- C语言基础之结构体、枚举
- C语言基础之结构体
- C语言基础之结构体
- C语言基础之指针进阶
- c语言基础-结构体
- C语言基础:结构体
- C语言基础-结构体
- C语言基础之结构体和共用体
- 黑马程序员_iOS开发C语言基础之结构体
- 黑马程序员——C语言基础之结构体
- 黑马程序员-C语言基础之结构体数组
- 黑马程序员——c语言基础之结构体
- ios开发之c语言基础-结构体
- ios开发之c语言基础-结构体经典题目
- C语言基础进阶之 MessageBox()用法简介
- C语言基础进阶之 MessageBox()用法简介
- JVM调优
- redis开机启动与centos6开机启动配置
- Android内核开发:如何统计系统的启动时间
- 关于java接口的形象理解
- linux mint ,ubuntu 安装为知笔记
- C语言基础之结构体进阶
- k8s部署php镜像
- 【WinterCamp 2013】阿凡达
- 谈谈微服务中的 API 网关(API Gateway)
- HandlerThread 使用实例
- c++模拟post和get请求
- iSwift for Mac(Objective-C代码转换工具)破解版 v4.0激活版
- Jboss与com.sun.jersey.guice冲突
- apache报Permission denied: make_sock: could not bin