概述
C语言结构体知识点梳理
- 1 结构体基本概念
- 1.1 定义与创建
- 1.2 初始化与调用
- 2 结构体对齐
- 2.1 默认内存对齐
- 2.2 pragma强制对齐
- 2.3 变量位域设置
- 3 多文件编译情况
- 如何修改结构体
- P.S.:
- 结构体变量存储详情
1 结构体基本概念
1.1 定义与创建
一、通常定义
// 定义
struct Person{
int number;
char sex;
double money;
}p1;
// 创建 & 初始化:需要分配变量名
p1 = {}; //全局变量(高址->低址)
struct Person p2 = {}; //局部变量 (高址->低址)
static struct Person p3 = {}; //静态全局变量 (低址->高址)
p1.number = 1; // 正确的初始化方式
p2.number = 1; // 正确的初始化方式
p3.number = 1; // 正确的初始化方式
二、匿名定义
struct{
int number;
char sex;
double money;
}P;
// 创建 & 初始化:无法创建除 P 外其他的结构体变量
// P = {}; // 错误的初始化方式
P.number = 1; // 正确的初始化方式(唯一全局变量)
三、类型定义
typedef struct{
int number;
char sex;
double money;
}W;
// W 等价替代 struct Person
W w00 = {}; //此时的 w00 仅为局部变量
static W w01 = {}; //此时的 w00 为静态全局变量
w00.number = 1;
四、多重定义
typedef struct People{
int number;
char sex;
double money;
}w1, w2;
// w1 等价替代 struct People
// 定义静态变量的 2 种方法
static struct People p000 = {};
static w1 w111 = {};
// 定义局部变量的 2 种方法
struct People p = {};
w1 w112 = {};
有关变量地址的讨论详见p.s.
1.2 初始化与调用
一、顺序初始化
二、指定初始化
调用原则:
给(返回值(将作为右值),参数列表实际赋值):地址
取(左值,参数列表定义):指针
子函数内:不加&或*
演示代码
#include <stdio.h>
struct student_st
{
char c;
int score;
const char *name;
};
static void show_student(struct student_st *stu)
{
printf("c = %c, score = %d, name = %sn", stu->c, stu->score, stu->name);
}
int main(void)
{
// method 1: 按照成员声明的顺序初始化
struct student_st s1 = {'A', 91, "Alan"};
show_student(&s1);
// method 2: 指定初始化,成员顺序可以不定,Linux 内核多采用此方式
struct student_st s2 =
{
.name = "YunYun",
.c = 'B',
.score = 92,
};
show_student(&s2);
// method 3: 指定初始化,成员顺序可以不定
struct student_st s3 =
{
c: 'C',
score: 93,
name: "Wood",
};
show_student(&s3);
return 0;
}
2 结构体对齐
2.1 默认内存对齐
规则:
1、结构体内存容量是最大类型变量的整数倍
2、默认留空对齐。
示例1:【常规对齐】
struct Demo1{
char arr[10];//类型长度为1,长度共10
int num;//类型长度为4
char str[3];//类型长度为1,长度为3
double node;//类型长度为8,结构体存储基准
long n;//类型长度为4
char ch;//类型长度为1
};
// 内存图:
/*高地址↑
[lon0][lon1][lon2][lon3][ ch ][ ][ ][ ]
[dou0][dou1][dou2][dou3][dou4][dou5][dou6][dou7]
[str0][str1][str2][ ][ ][ ][ ][ ]
[arr8][arr9][ ][ ][num0][num1][num2][num3]
[arr0][arr1][arr2][arr3][arr4][arr5][arr6][arr7]
低→高
低地址↑*/
// 内存占用比:30/40Byte
// 寻址有效率:6/10
示例2:【极限对齐】
struct demo{
int num;
char ch;
char arr[3];
double dot;
}d;
// 内存图:
/*高地址↑
[dou0][dou1][dou2][dou3][dou4][dou5][dou6][dou7]
[num0][num1][num2][num3][ ch ][arr0][arr1][arr2]
低→高
低地址↑*/
2.2 pragma强制对齐
规则:
#pragma pack(N)
可将结构体以规定长度N(1、2、4、8)为存储基准。
此时将不再以变量类型最长的长度为默认存储基准。
示例:
#pragma pack(4)
struct Demo1{
char arr[10];//类型长度为1,长度共10
int num;//类型长度为4
char str[3];//类型长度为1,长度为3
double node;//类型长度为8,结构体存储基准
long n;//类型长度为4
char ch;//类型长度为1
};
// 内存图:
/*高地址↑
[ ch ][ ][ ][ ]
[lon0][lon1][lon2][lon3]
[dou4][dou5][dou6][dou7]
[dou0][dou1][dou2][dou3]
[str0][str1][str2][ ]
[num0][num1][num2][num3]
[arr8][arr9][ ][ ]
[arr4][arr5][arr6][arr7]
[arr0][arr1][arr2][arr3]
低→高
低地址↑*/
// 内存占用比:30/36Byte
// 寻址有效率:6/18
/*|* OTHERS *|*/
/** pack(2)
// 内存占用比:30/32Byte
// 寻址有效率:6/32
*/
/** pack(1)
// 内存占用比:30/30Byte
// 寻址有效率:6/30
*/
2.3 变量位域设置
规则:
将结构体中变量按bit位取内存
位域限定的3种变量类型:int,unsigned int,char
示例1:【默认】
struct Owner{
char name;
short age;
};
struct demo{
char addr[2];
char apart;
char floor;
struct Owner p;
};
// 内存图:
/*高地址↑
[age0][age1]
[name][ ]
[apar][floo]
[add0][add1]
低→高
低地址↑*/
示例2:【位域】
struct Owner{
char name:3;
short age;
};
struct demo{
char addr[2];
char apart:1;
char floor:4;
struct Owner p;
};
// 内存图:
/*高地址↑
[age0][age1]
[0:0:0:0:0:n2:n1:n0][ ]
[0:0:0:f3:f2:f1:f0:a0][ ]
[add0][add1]
低→高
低地址↑*/
p.s.:虽然apart + floor + p.name恰好可以凑成一字节,但是并未跨结构体拼接在一起(即使加上#pragma pack(1)
也是如此)。
注意:
位域是不跨平台的,可移植的程序应避免使用位域。
1、int位域是有符号还是无符号是不确定的;
2、位域最大数目不确定;
3、位域成员在内存中从左向右分配,还是从右向左分配尚未定义;
4、当一个结构包含两个位域,第二个位域成员较大,无法容纳第一个位域剩余位时,是舍弃还是利用,也是不确定的。
以上链接自:C语言-----结构体知识点总结.
总结:位域可以很好地节省空间,但是存在跨平台问题。
以下是关于位域的拓展案例:
int main()
{
char pub[4] = { 0 };
struct S
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
}*ps;
// 结构体指针数组
ps = (struct S*)pub;
ps->a= 10;
ps->b = 12;
ps->c = 3;
ps->d = 4;
// 仅看前两个字节
printf("%02x,%02x,%02x,%02x", pub[0],pub[1],pub[2],pub[3]);
system("pause");
return 0;
}
// 输出结果: 62,03,04,00
图解:
3 多文件编译情况
如何修改结构体
将结构体定义在头文件
在对应c文件中调用结构体变量
封装保护:
头文件中声明对应c文件中内容
将对应c文件封装成静态库
后果:
此时如果在头文件结构体中添加新的属性,
则在对应c文件中调用的结构体变量将不支持
p.s.:
#静态库不会改变,若外界结构变化,静态库要重新进行封装
#静态库编译封装:
// 编译student.c,并封装成静态库
gcc -c student.c
ar -r libStudent.a student.o
//编译test.c 链接libStudent.a 并运行查看打印的结果
//"student.h" 需要和test.c 位于同一目录下,保证链接成功
gcc test.c -L. -lStudent -o out
./out
兼容性强地修改结构体的2种方案:
1、空间预留
在对应c文件尚未封装成静态库时:
在头文件的结构体末尾添加预留空间:
int resv[32];
然后再将对应c文件封装成静态库
当需要扩展结构体属性时:
在结构体末尾创建新属性并对应减少预留空间
char new;
int resv[31];
一定要从末尾添加,防止偏移地址紊乱
2、指针扩展
不想对外暴露的对象,可以用指针处理
P.S.:
结构体变量存储详情
演示代码
#include <stdio.h>
// P为全局变量
struct Person{
int number;
char sex;
double money;
}P1,P2;
/** 匿名结构体,无法内部内嵌*/
struct{
int number;
char sex;
double money;
}P;
/** 定义结构体类型 */
// 此时的W为类型
// 结构体不能重名
typedef struct People{
int number;
char sex;
double money;
}w1, w2;
typedef struct{
int number;
char sex;
double money;
}W;
// 结构体定义在函数内部, 无法在外部获得其定义
// extern struct Class;
void print_message();
// 结构体定义必须在使用之前定义,预先声明或扩展声明均无效
// struct Class;
// extern struct Class;
// 只能在外部重新再次定义
int main(int argc, const char *argv[]){
/**1*/
static struct Person pxxx = {};
struct Person pxx = {};
/**2*/
sizeof(P);
P.sex = 'f';
/**3*/
W w000 = {};
static W w001;
/**4*/
static struct People p000 = {};
static w1 w111 = {};
struct People p = {};
w1 w112 = {};
printf("1n");
printf("full : %xn", &P1);
printf("static : %xn", &pxxx);
printf("origin : %xn", &pxx);
printf("2n");
printf("full : %xn", &P);
printf("3n");
printf("origin : %xn", &w000);
printf("static : %xn", &w001);
printf("4n");
printf("static1 : %xn", &p000);
printf("static2 : %xn", &w111);
printf("origin1 : %xn", &p);
printf("origin2 : %xn", &w112);
// C不再是全局变量,而是局部变量
struct Class{
int number;
char sex;
double money;
}C;
struct Class c101;
struct Class c102;
static struct Class c103;
printf("5n");
printf(""full" : %xn", &C);
printf("origin1 : %xn", &c101);
printf("origin2 : %xn", &c102);
printf("static : %xn", &c103);
print_message();
return 0;
}
void print_message(){
// 结构体定义在函数内部,作用域仅在函数内部
// ip仅为局部变量
struct Class{
int number;
char sex;
double money;
}ip;
struct Class o1;
struct Class o2;
// 若把子函数块提前,该动态变量最优先生成
static struct Class o;
printf("6n");
printf("static : %xn", &o);
printf("local : %xn", &o1);
printf("local : %xn", &o2);
printf(""full" : %xn", &ip);
return;
}
输出详情:
1
full : 4050c0
static : 405008
origin : 60fef0
2
full : 4050b0
3
origin : 60fee0
static : 405018
4
static1 : 405028
static2 : 405038
origin1 : 60fed0
origin2 : 60fec0
5
"full" : 60feb0
origin1 : 60fea0
origin2 : 60fe90
static : 405048
6
static : 405058
local : 60fe50
local : 60fe40
"full" : 60fe60
最后
以上就是端庄背包为你收集整理的C语言结构体知识点梳理1 结构体基本概念2 结构体对齐3 多文件编译情况P.S.:的全部内容,希望文章能够帮你解决C语言结构体知识点梳理1 结构体基本概念2 结构体对齐3 多文件编译情况P.S.:所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复