C语言--结构体总结
结构体
一、概念#
-
在实际问题中,一组数据往往有很多种不同的数据类型。例如,登记学生的信息,可能需要用到
char
型的姓名,int
型或char
型的学号,int
型的年龄,char
型的性别,float
型的成绩。又例如,对于记录一本书,需要char
型的书名,char
型的作者名,float
型的价格。在这些情况下,使用简单的基本数据类型甚至是数组都是很困难的。而结构体(类似Pascal中的“记录”),则可以有效的解决这个问题。 -
结构体本质上还是一种数据类型,但它可以包括若干个“成员”,每个成员的类型可以相同也可以不同,也可以是基本数据类型或者又是一个构造类型。
-
结构体的优点:结构体不仅可以记录不同类型的数据,而且使得数据结构是“高内聚,低耦合”的,更利于程序的阅读理解和移植,而且结构体的存储方式可以提高CPU对内存的访问速度。
二、程序实现#
1.结构体的基本使用#
创建结构体#
strcut
标识符,用户自定义的名字- 结构体不一定只能放外面,
main
函数里面也可以放。(作用域不同) - 定义一个结构体类型,
struct Student
合起来才算是类型名 - 成员变量不能直接赋值
struct Student
{
char name[50];
int age;
int score;
};//有分号
命名结构体变量#
- 类型
+
变量 struct Student
合起来才是类型名s
是变量
struct Student s;
访问成员变量#
- 如果是普通变量(非指针),使用
.
运算符
stpcpy(s.name,"黑马");
s.age = 18;
s.score = 100;
- 如果是指针变量,使用
->
运算符
printf("%s %d %d\n",(&s)->name,(&s)->age,(&s)->score);//打印结构体中的值
2.结构体的变量#
- 普通结构体变量
//普通结构体变量初始化(此方式需要按照结构体定义顺序赋值)
struct Student s = {"abc",10,30};
//打印初始化的值
printf("%s, %d, %d",s.name,s.age,s.score);
- 结构体数组变量
//普通结构体变量初始化
struct Student a[] =
{
{"heibi", 18, 100},
{"testw", 25, 300},
{"eicis", 49, 200},
};
//打印结构体数组个数
int n = sizeof(a)/sizeof(a[0]);
printf("n:%d",n);
//访问结构体数组内容(4种方式)
for(int i = 0; i<n ; ++i)
{
printf("%s, %d, %d\n",a[i].name,a[i].age,a[i].score);
//printf("%s, %d, %d\n",(*(a+i)).name,(*(a+i)).age,(*(a+i)).score);
//printf("%s, %d, %d\n",(&a[i])->name,(&a[i])->age,(&a[i])->score);
//printf("%s, %d, %d\n",(a+i)->name,(a+i)->age,(a+i)->score);
}
3.结构体的嵌套#
-
引例
这里可以看到两个结构体得内容相差不大,有三个重复的变量,这种情况即可以使用结构体的嵌套
struct Info
{
int age;
char sex;
char name[50];
};
struct Student
{
int age;
char sex;
char name[50];
int score;
};
- 嵌套例子
struct Info
{
int age;
char sex;
char name[50];
};
struct Student
{
struct Info info;//嵌套另外一个结构体变量
int score;
};
- 初始化及访问
//结构体嵌套初始化(和不嵌套的结构体一致)
struct Student s = {18, 'm', "xiaoming", 18};
//打印嵌套结构体的内容
printf("%d, %c, %s, %d\n",s.info.age, s.info.sex, s.info.name, s.score);
-
注意事项
- 嵌套结构体变量,不能是本结构体类型。
- 但是可以嵌套任何类型的结构体指针变量。
struct Student { struct Info info; int score; //嵌套本结构体类型,会报错。因为`struct Student`类型不确定,内存大小无法确定 // struct Student temp; //嵌套任何类型的结构体指针变量。因为指针大小是确定的。32位系统4字节大小,64位系统8字节大小 struct Student *next; };
-
补充:
typedef
的使用:将struct Student
结构体重新命名代替为为Student
- 传统用法
struct Student { int age; char sex; char name[50]; }; typedef struct Student Student;//有分号
- 常见用法
typedef struct Student { int age; char sex; char name[50]; }Student;
4.同类型结构体相互赋值#
- 直接赋值
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//typedef改一个类型名,把复杂名字改简单点,不能创造新的类型
typedef struct Student
{
int age;
char sex;
char name[50];
}Student;
int main(void)
{
Student s1 = {18, 'm', "xiaoming"};
Student s2;
// s2 = s1; //同类型的两个变量相互赋值
strcpy(s2.name,s1.name);
// 成员变量逐一拷贝
s2.age = s1.age;
s2.sex = s1.sex;
printf("%d, %c, %s\n",s2.age, s2.sex, s2.name);
return 0;
}
-
通过函数实现
-
关于内存栈区与堆区操作(这里是实现用指针形式访问结构体成员)
- 如果结构体只定义了一个空指针,便不能操作其内容,要让其指针指向有效地址(栈区或者堆区)
- 在进行指向栈区空间时,不需要释放,该空间由系统自行维护。
- 在进行指向堆区空间时,需要自己申请空间,并在使用完成后手动释放。
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct Student { int age; char sex; char name[50]; }Student; int main(void) { //定义一个结构体指针变量 Student *p = NULL; //让指针有个合法指向(指向栈区) // Student temp;//普通结构体变量,栈区 // p = &temp; //让指针有个合法指向(指向堆区) p = (Student *) malloc(sizeof(Student));//申请一个`Student`大小的地址,并用p指向 if(p == NULL) { printf("分配内存失败\n"); return -1; } strcpy(p->name, "小明"); p->sex = 'm'; p->age = 19; printf("%d, %c, %s\n",p->age, p->sex, p->name); //手动释放堆区空间 free(p); p = NULL; return 0; }
-
函数代码实现赋值
- 这里使用了
const
,使s1传入进来的参数不可改变。可防止误改传入参数s1
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct Student { int age; char sex; char name[50]; }Student; //从左往右看,const修饰最近的字符,这修饰的是指针变量 //这里使用了const,使s1所指向的内存(成员变量)不可改变,指针变量可以改变 void fun(const Student *s1, Student *s2) { //两个指针的内容实现赋值 *s2 = *s1; } int main(void) { Student s1; Student s2 = {0,'0',"0"}; printf("%d, %c, %s\n",s2.age, s2.sex, s2.name); strcpy(s1.name, "小明"); s1.sex = 'm'; s1.age = 19; //通过一个函数将s1的值赋值给s2 fun(&s1, &s2); printf("%d, %c, %s\n",s2.age, s2.sex, s2.name); return 0; }
- 这里使用了
-
三、内存分析#
- 通过一个函数,动态分配内存
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Student
{
int age;
char sex;
char name[50];
}Student;
//需要注意的是:所有子函数运行完后的变量都会自动进行释放(不能再直接调用)
Student* fun()
{
Student *a;
a = (Student *)malloc(sizeof(Student));
return a;
}
int main(void)
{
Student *p = NULL;
p = fun();//返回并用`p`指向所开辟出来的地址
//只有p指向相应地址时,才能进行赋值
strcpy(p->name, "小明");
p->sex = 'm';
p->age = 19;
printf("%d, %c, %s\n", p->age, p->sex, p->name);
//释放子函数中`a`开辟出来的空间(`p`和`a`均会指向,只不过`a`在函数调用完后被释放了)
free(p);
p = NULL;
return 0;
}
四、出现过的问题#
-
不能将
x*类型的值
分配到x*类型的实体
(以下代码会出现该问题)#include <stdlib.h> //malloc(); #include <stdio.h> typedef struct /*Student*/ //出错原因:在定义结构体时没有定义变量 { //数据域(位置) int x, y; //指针域 struct Point *next; }Student; void main() { Student *pnew = (Student *)malloc(sizeof(Student)); Student *s = NULL; pnew->next = s; //此处会报错:`s为x*类型的值` , pnew->next为`x*类型的实体` }
解决方法:在定义结构体时加上结构体变量
Student
即可
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现