C语言 结构体
创始人
2024-03-07 04:39:27

C语言 结构体

  • 一、结构体的声明和初始化
    • 1. 结构体声明
    • 2. 结构体初始化
  • 二、typedef 重定义结构体
  • 三、结构体成员的类型
  • 四、结构体成员的访问
  • 五、结构体传参
  • 六、结构体的自引用
  • 七、结构体的内存对齐
    • 对齐规则
    • 程序清单1
    • 程序清单2
    • 程序清单3
    • 程序清单4
    • 修改默认对齐数

一、结构体的声明和初始化

1. 结构体声明

程序清单:

#include struct Student {char name[20];		// 姓名int age;			// 年龄int studentID;		// 学号
}student1, student2;int main() {struct Student student3;return 0;
}

2. 结构体初始化

程序清单:

#include struct Student {char name[20];			// 姓名int age;				// 年龄int studentID;			// 学号
}student1 = {"Rose", 19, 06}, student2={"小红", 17, 03};int main() {struct Student student3 = {"Jack", 18, 02};return 0;
}

注意事项:

上面是结构体声明和初始化的标准格式,随着 Student 类型被创建出来,那么后面就可以根据其来创建相应的结构体变量:student1,student2,student3.

student1,student2 为全局变量,student3 为局部变量。

二、typedef 重定义结构体

#include typedef struct Student {char name[20];			// 姓名int age;				// 年龄int studentID;			// 学号
}Student;int main() {Student student = { "Rose", 17, 03 };// struct Student student = ...return 0;
}

我们平时写类型的时候,都需要在自定义类型前加上 struct,才能够表示一个完整的结构体类型。而经 typedef 关键字重定义后, " struct Student " 和 " Student " 就是等价的了,这可以让我们后续更加简单地创建 Student 变量了。这里只需要注意写法即可。

三、结构体成员的类型

结构的成员可以是标量、数组、指针,甚至是其他结构体。如下所示:

程序清单:

#include struct Grade {int chinese;				// 语文成绩int math;					// 数学成绩int english;				// 英语成绩
};struct Student {char name[20];				// 姓名int age;					// 年龄int studentID;				// 学号struct Grade grade;			// 成绩
};int main() {struct Student student = { "Rose", 17, 03, {90, 95, 93} };printf("学生信息:%s %d %d\n", student.name, student.age, student.studentID);printf("学生成绩:%d %d %d\n", student.grade.chinese, student.grade.math, student.grade.english);return 0;
}

输出结果:

1-1

四、结构体成员的访问

在访问结构体成员时,可以采用 . 或者 -> ,前者对应结构体变量,后者对应结构体指针变量。

程序清单:

#include struct Student {char name[20];  // 名字int age;		// 年龄int studentID;  // 学号
};int main() {struct Student student1 = {"Jack", 18, 32};  struct Student student2 = {"Bruce", 20, 05};printf("%s %d %d\n", student1.name, student1.age, student1.studentID);struct Student* ps1 = &student1;printf("%s %d %d\n", (*ps1).name, (*ps1).age, (*ps1).studentID);printf("%s %d %d\n", ps1->name, ps1->age, ps1->studentID);return 0;
}

输出结果:

1-2

五、结构体传参

程序清单:

#include struct Student {char name[20];		// 姓名int age;			// 年龄int studentID;		// 学号
};void print1(struct Student student) {printf("%s %d %d\n", student.name, student.age, student.studentID);
}void print2(struct Student* ps) {printf("%s %d %d\n", ps->name, ps->age, ps->studentID);
}int main() {struct Student student1 = {"Jack", 18, 02};print1(student1);	// 1. 传值print2(&student1);	// 2. 传地址return 0;
}

输出结果:

1-3

注意事项:

① 函数在定义时,应该写在结构体之后。由于 main 函数作为程序的入口,之后在 main 函数中调用 print1 和 print2 函数时,程序就需要自上到下扫描,如果两个打印函数都写在了结构体之前,就会产生找不到结构体类型的现象。

② 上面在使用结构体传参时,采用了两个传参类型。
第一,传值调用 print1 函数;第二,传地址调用 print2 函数。然而,从调用函数的效率角度看,后者更加高效。

前者在传值时,传的是整个结构体过去的,如果结构体过大,形参在接收实参传来的结构体时,也要同样开辟一个与原结构体一样大的内存空间,后续才能接收结构体中各种各样的成员变量。然而,后者在传整个结构体的地址时,形参只需要拿一个结构体指针变量接收即可,因为一个指针变量占内存的大小要么是 4,要么是 8 ,所以整体系统开销较小。

此外,这里结构体传参应该与数组传参区分开来,如果将一个结构体变量作为函数的参数,那么传递的就是整个结构体;然而,如果将一个数组作为函数的参数,那么传递的就是数组首元素的地址。

综上所述,结构体在传参时,应该尽可能传它的地址,而不是结构体变量本身。

六、结构体的自引用

结构体的自引用常用于表示数据结构中的链表,结构体中包含数据域和指针域。

#include struct Node {int data;				// 数据域struct Node* next;		// 指针域
};int main() {struct Node node1;return 0;
}

七、结构体的内存对齐

对齐规则

1. 第一个结构体成员在处于偏移量为 0 的地址处。

2. 其他结构体成员需要从对齐数的整数倍的偏移量地址处开始继续偏移。
( 对齐数 = 编译器默认的对齐数与该结构体成员大小两者比较后的较小值 )
在 VS 编译器中,默认值为 8;在 Linux 环境下,没有默认对齐数,此时结构体成员的自身大小就是它的对齐数。

3. 结构体占用内存的总大小为最大对齐数的整数倍,单位字节。
( 最大对齐数 = 所有结构体成员对齐数中的最大值 )

4. 如果出现了结构体嵌套结构体的情况,那么内部的结构体对齐到自己的最大对齐数的整数倍处。最终,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

程序清单1

#include 
#include struct S1 
{char c1;		// 1int i;			// 4char c2;		// 1
};int main() {printf("%d\n", sizeof(struct S1)); // 12printf("%u ", offsetof(struct S1, c1)); // 0printf("%u ", offsetof(struct S1, i)); // 4printf("%u \n", offsetof(struct S1, c2)); // 8return 0;
}

注意事项:

offsetof 可以用来计算结构体成员的偏移量。

S1 所占内存的计算过程:

1-4

程序清单2

#include 
#include struct S2
{char c1;		// 1char c2;		// 1int i;			// 4
};int main() {printf("%d\n", sizeof(struct S2)); return 0;
}// 输出结果:8

S2 所占内存的计算过程:

1-5

程序清单3

#include 
#include struct S3
{double d;		// 8char c;			// 1int i;			// 4
};int main() {printf("%d\n", sizeof(struct S3));return 0;
}// 输出结果:16

S3 所占内存的计算过程:

1-6

程序清单4

#include 
#include struct S3			// 16
{double d;		// 8char c;			// 1int i;			// 4
};struct S4
{char c1;		// 1struct S3 s3;	// 最大对齐数 8double d;		// 8
};int main() {printf("%d\n", sizeof(struct S4)); // 32printf("%u ", offsetof(struct S4, c1)); // 0printf("%u ", offsetof(struct S4, s3)); // 8printf("%u \n", offsetof(struct S4, d)); // 24return 0;
}

S4 所占内存的计算过程:

1-7

修改默认对齐数

#include 
#include struct S1 {char c;			// 1double d;		// 8
};#pragma pack(4)
struct S2 {char c;			// 1double d;		// 8 (取对齐数为 4)
};
#pragma pack()#pragma pack(1)
struct S3 {char c;			// 1double d;		// 8 (取对齐数为 1)
};
#pragma pack()int main() {printf("%d\n", sizeof(struct S1)); // 16printf("%d\n", sizeof(struct S2)); // 12printf("%d\n", sizeof(struct S3)); // 9return 0;
}

1-8

注意事项:

在 VS 编译器中,对齐数默认值为 8;在 Linux 环境下,没有默认对齐数,此时结构体成员的自身大小就是它的对齐数。当我们为程序设计对齐数时,也应该考虑到硬件的访问情况,通常应该将对齐数设置为 2 的次方。

此外,在对齐数相同的情况下,我们应该尽可能地将占用空间小的结构体成员集中在一起。对比上面程序清单1 和 程序清单2,同样的对齐数,同样的程序,后者将 char 类型的结构体成员放在了一起,结果既满足了对齐,又节省了空间。

相关内容

热门资讯

猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...