前言
C语言的结构体学习笔记
定义结构体并使用结构体变量
结构体使用struct
关键字定义,结构体末尾有;
定义结构体,类似于OOP编程的创建类(当然,C语言是没有对象这个概念的,只不过是这里方便理解)
结构体内部可以定义多个变量,类似于OOP编程中类的属性
结构体变量的地址,实际上就是首元素的地址
在C语言中一个结构体至少要有一个成员变量,在C++定义结构体时可以没有成员变量
先调用后初始化
初始化全部成员变量
A
:结构体名 a
:结构体变量名
1 2 3 4 5 6 7 8 9 10 11 12
| struct A { char name[8]; int age; }; int main() { struct A a; scanf("%s", a.name); scanf("%d", &a.age); return 0; }
|
只初始化部分变量
1 2 3 4 5 6 7 8 9 10 11
| struct A { char name[8]; int age; }; int main() { struct A a; scanf("%s", a.name); return 0; }
|
调用时直接初始化
初始化全部成员变量
1 2 3 4 5 6 7 8 9 10
| struct A { char name[8]; int age; }; int main() { struct A a = { "张三", 18 }; return 0; }
|
只初始化部分成员变量
不指定成员变量名
- 不指定成员变量名时,会按照定义顺序进行初始化,且其他没有被初始化的成员变量值为0
1 2 3 4 5
| int main() { struct A a = { "张三" }; return 0; }
|
指定成员变量名
1 2 3 4 5
| int main() { struct A a = { .age = 18 }; return 0; }
|
结构体在内存中存放的方式
结构体在内存中存储数据时,如果与最大成员变量不对齐,则会自动对齐,对齐时空出来的空间保持为空,且如果有其他小于空的空间的数据存放时,会自动放到空的空间(但是这个数据会右对齐)
由于结构体的成员变量在内存中存储时是按顺序的,所以我们应该手动优化代码实现最省空间的存储
如果结构体出现数组,则按照数组内的具体成员的类型作为对齐标准
以下是优化前的代码: 1. 这一组成员变量中,最长的是4个字节的int类型a4,所以,应按照4字节为一个单位的存储 2. 先存a1,占用1字节,再存a2,占用2字节,但是是右对齐,所以一共消耗了4字节空间 3. 然后存a3,占用1字节,接下来想要存a4,但是不够4字节了,所以a3占用4字节 4. 最后存a4,占用4字节 5. 最终占用12字节
1 2 3 4 5 6 7
| struct A { char a1; short a2; char a3; int a4 };
|
以下是优化后的代码: 1. 这一组成员变量中,最长的是4个字节的int类型a4,所以,应按照4字节为一个单位的存储 2. 现存b1,占用1字节,再存b2,占用1字节,向右对齐 3. 然后存b3,占用2字节,与b1、b2总共占用4字节 4. 最后存b4,占用4字节 5. 最终占用8字节
1 2 3 4 5 6 7
| struct B { char b1; char b2; short b3; int b4; };
|
以比特为单位存储成员变量
- 指定的bit数不能超过当前定义的数据类型的最大的内存占用空间
<num>
:以几个bit存储数据
1 2 3 4
| struct A { unsigned char a : <num>; };
|
结构体数组
先调用后初始化
初始化全部成员变量
指定数组数量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| struct A { char name[8]; int age; }; int main() { struct A a[2]; int i; for (i = 0; i < sizeof(a) / sizeof(a[0]); i++) { scanf("%s", a[i].name); scanf("%d", &a[i].age); } return 0; }
|
不指定数组数量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| struct A { char name[8]; int age; }; int main() { struct A a[]; int i; for (i = 0; i < sizeof(a) / sizeof(a[0]); i++) { scanf("%s", a[i].name); scanf("%d", &a[i].age); } return 0; }
|
初始化部分成员变量
1 2 3 4 5 6 7 8 9 10 11 12
| struct A { char name[8]; int age; }; int main() { struct A a[2]; scanf("%s", a[0].name); scanf("%d", &a[0].age); return 0; }
|
调用时直接初始化
初始化全部成员变量
指定数组数量
1 2 3 4 5 6 7 8 9 10
| struct A { char name[8]; int age; }; int main() { struct A a[2] = { { "张三", 18 }, { "李四", 20 } }; return 0; }
|
不指定数组数量
1 2 3 4 5 6 7 8 9 10
| struct A { char name[8]; int age; }; int main() { struct A a[] = { { "张三", 18 }, { "李四", 20 } }; return 0; }
|
只初始化部分成员变量
不指定成员变量赋值
1 2 3 4 5 6 7 8 9 10
| struct A { char name[8]; int age; }; int main() { struct A a[2] = { { "张三", 18 } }; return 0; }
|
指定成员变量赋值
1 2 3 4 5 6 7 8 9 10
| struct A { char name[8]; int age; }; int main() { struct A a[2] = { { .age=18 }, { .name="李四" } }; return 0; }
|
结构体嵌套
- 在嵌套结构体时,结构体是作为一个整体存在的,所以对齐时只遵循子级结构体,而不是父级结构体
- 例如以下的结构体占用了12字节内存
1 2 3 4 5 6 7 8 9 10
| struct A { char a1; }; struct B { struct A a; char b1; int b2; };
|
结构体变量的复制
1 2
| struct A a = {}; struct B b = a;
|
操作符
定义一个结构体
1 2 3 4 5
| struct P { char name[8]; int age; };
|
没有使用操作符之前的结构体赋值
1 2 3 4
| struct P p;
strcpy((*p).name, "张三"); (*p).age = 18;
|
使用操作符之后的结构体赋值
1 2
| strcpy(p->name, "张三"); p->age = 18;
|
结构体作为函数参数
传递结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| struct A { char name[8]; int age; };
void print_A(struct A a) { printf("name=%s, age=%d\n", a.name, a.age); }
int main() { struct A a = {"张三", 18}; print_A(a); return 0; }
|
传递结构体脂针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| struct A { char name[8]; int age; };
void set_A(struct A *a, char name[8], int age) { strcpy(a->name, name); a->age = age; }
int main() { struct A a = {"张三", 18}; set_A(&a, "李四", 20); }
|
完成