C语言中结构体详解
1.1 结构体的相关概念
结构体: 将多个相同或不同类型的数据存在一块连续的内存空间
说明:
- 结构体和数组一样,同属于符合类型的数据;
- 结构体和数组的区别:数组用于保存相同类型的数据,而结构体通常用于描述一个具体事务的属性,保存若干个不同/相同类型的数据;
- C语言的结构体和C++/Java/Python中的class类似;
1.2 结构体变量的定义和初始化
结构体定义方法:
struct tag {
member-list
member-list
member-list
...
} variable-list ;
- tag 是结构体标签。
- member-list 是标准的变量定义,比如 int i; 或者 float f,或者其他有效的变量定义。
- variable-list 结构变量,定义在结构的末尾,最后一个分号之前,您可以指定一个或多个结构变量。
定义结构体变量的方式:
- 先声明结构体类型再定义变量名;
- 在声明类型的同时定义变量
- 直接定义结构体类型变量(无类型名)
示例:
#include <stdio.h>
#include <stdlib.h>
struct Student {
int id;
char stuName[10];
char major[10];
char sex;
};
void defStructs() {
struct Student s1 = {
1,
"jack",
"science",
'm'
};
printf("id=%d name=%s,major=%s sex=%c", s1.id, s1.stuName, s1.major, s1.sex);
//id=1 name=jack,major=science sex=m
}
//部分元素初始化:
//.property = value
结构体类型和结构体变量关系:
- 结构体类型:指定了一个结构体类型,它相当于一个模型,但其中并无具体数据,系统对之也不分配实际内存单元;
- 结构体变量:系统根据结构体类型(内部成员状况)为之分配空间;
注意:
- 定义结构体时,结构体的成员变量不能赋值;
结构体的初始化
-
如果通过结构体变量操作结构体成员,使用"点域"操作, 如:
s1.name s1.age s1.sex ...
-
如果通过结构体地址操作结构体的成员变量,使用
->
;如:struct *Student s1; s1->name; s1->age; s1->major; ...
-
结构体初始化案例:
#include <stdio.h> #include <string.h> struct Student { int id; char stuName[10]; char major[10]; char sex; }; void getStructProperty() { struct Student s1; (&s1)->id = 2; strcpy((&s1)->stuName, "Tom"); strcpy((&s1)->major, "English"); (&s1)->sex = 'f'; printf("id=%d name=%s,major=%s sex=%c", s1.id, s1.stuName, s1.major, s1.sex); //id=2 name=Tom,major=English sex=f }
1.3 结构体数组
是一个数组,数组的每一个元素都是结构体
例如:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Person {
int id;
char name[16];
char sex[8];
};
void defArray() {
struct Person pArr[3] = {
{1, "Asus", "male"},
{2, "Tom", "male"},
{3, "suse", "female"}
};
//获取属性
printf("%s\n", pArr[0].sex); //male
printf("%s\n", pArr[1].name); //Tom
printf("%d\n", pArr[2].id); //3
//遍历结构体数组
for (int i = 0; i < sizeof(pArr) / sizeof(pArr[0]); i++) {
printf("id=%d name=%s sex=%s\n",
pArr[i].id, pArr[i].name, pArr[i].sex);
}
}
int main() {
defArray();
return 0;
}
1.4 结构体嵌套
// STATEMENT : 结构体的嵌套
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Person {
char name[16];
char sex[8];
};
struct Student {
int No;
char major[8];
// 嵌套结构体Person
struct Person person;
};
void defProperty() {
struct Student student;
(&student)->No = 1;
strcpy((&student)->major, "c++");
strcpy((&student)->person.sex, "male");
strcpy((&student)->person.name, "Tom");
printf("No=%d major=%s name=%s sex=%s",
student.No, student.major, student.person.name, student.person.sex);
//No=1 major=c++ name=Tom sex=male
}
int main() {
defProperty();
return 0;
}
1.5 结构体和指针
1.5.1 指向普通结构体变量的指针
如:
struct Student s1 = {1,"Tom","male"}
//定义结体指针变量
struct Student *pS1 = &s1;
//获取属性值:
s1->id;
s1->name;
s1->gender;
案例:封装函数,实现对结构体内容的copy
//封装函数,实现对结构体内容的copy
#include <stdio.h>
#include <string.h>
struct Employee {
int No;
char name[16];
double salary;
};
void copy_struct_value(struct Employee *pEmployee, struct Employee *pEmployee1, int copy_size);
int main() {
struct Employee e1 = {
1,
"Lucy",
8888.76
};
struct Employee e2;
copy_struct_value(&e1, &e2, sizeof(e1));
printf("No=%d name=%s salary=%.3f",
e2.No, e2.name, e2.salary);
}
void copy_struct_value(struct Employee *pEmployee, struct Employee *pEmployee1, int copy_size) {
memcpy(pEmployee1, pEmployee, copy_size);
}
1.5.2 堆区结构体变量
示例:
// STATEMENT : 堆区结构体变量
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Car {
double price;
double length;
double width;
char comment[16];
};
struct Car *get_cars(double width, double length, const char *comment, double price) {
//在堆区定义结构体变量
struct Car *car = malloc(sizeof(struct Car));
car->length = length;
strcpy(car->comment, comment);
car->price = price;
car->width = width;
return car;
}
int main() {
//传参接受堆区返回的内存地址
struct Car *car = get_cars(1.8, 4.7, "good", 45.88);
printf("length=%.2f width=%.2f comment=%s price=%.2f",
car->length, car->width, car->comment, car->price);
//length=4.70 width=1.80 comment=good price=45.88
free(car);
return 0;
}
实现思路:
- 定义一个结构体
- 定义一个返回堆区内存地址的函数
- 函数内向堆内存申请一块空间,存放结构体;
- 给结构体的成员变量做初始化操作
- 初始化完成后,return这个存放结构体地址的指针;
- main函数中调用上一步定义的函数,传输适当的参数,用一个指针变量接受返回的堆内存地址;
- 输入打印
- 释放堆区开辟的内存空间
1.5.3 结构体套一级指针
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct testA {
double a;
char *b;
};
int main() {
struct testA *t = malloc(sizeof(struct testA));
t->a = 3;
/*strcpy(t->b, "afd");*/ //err,操作了野指针
//需要给b单独开辟内存空间
t->b = malloc(64);
strcpy(t->b, "aaa");
printf("a=%f b=%s", t->a, t->b);
//a=3.000000 b=aaa
free(t->b);
free(t);
return 0;
}
注意:
- 释放内存的顺序一定是从里往外释放,否则会造成内存泄漏;
- 给结构体testA开辟空间时,同时也给b开辟了空间,但是b没有指向,因此需要给b(指针变量)单独开辟一个空间,否则b为野指针;
- 如果变量名是一个结构体类型,使用
.
访问成员变量;如果变量类型为结构体指针,则使用->
访问成员变量.
1.5.5 结构体普通变量做函数参数
// STATEMENT : 结构体普通变量做函数参数
#include<stdio.h>
#include <string.h>
//结构体类型的定义
struct stu {
char name[50];
int age;
};
//函数参数为结构体普通变量
void set_stu(struct stu tmp) {
strcpy(tmp.name, "mike");
tmp.age = 18;
printf("tmp.name = %s, tmp.age = %d\n", tmp.name, tmp.age);
//tmp.name = mike, tmp.age = 18
}
int main() {
struct stu s = {0};
set_stu(s); //值传递
printf("s.name = %s, s.age = %d\n", s.name, s.age);//s.name = , s.age = 0
return 0;
}
1.5.6 结构体数组名做函数参数
#include <string.h>
#include <stdio.h>
struct stu {
char name[32];
int age;
};
void set_stu_pro(struct stu pStu[5], int len);
void printInfo(struct stu pStu[5], int len);
int main() {
struct stu stuList[5];
int len = sizeof(stuList) / sizeof(stuList[0]);
set_stu_pro(stuList, len);
printInfo(stuList, len);
//name = hahah age = 0
//name = hahah age = 3
//name = hahah age = 6
//name = hahah age = 9
//name = hahah age = 12
}
void printInfo(struct stu pStu[5], int len) {
for (int i = 0; i < len; i++) {
printf("name = %s age = %d\n", pStu[i].name, pStu[i].age);
}
}
void set_stu_pro(struct stu pStu[5], int len) {
for (int i = 0; i < len; i++) {
pStu[i].age = i * 3;
strcpy((char *) (pStu + i), "hahah");
}
}