一.结构体
1.结构体的概念:
结构体可以理解为自定义的数据类型,它是由一批数据组合而成的结构型数据
2.结构体格式:
struct 结构体名字 {成员1;成员2;...成员n;
};
案例:
#include<stdio.h>
#include<string.h>
struct student {char name[100];int age;char gender;
};int main() {struct student s1;strcpy(s1.name, "zhangsan");s1.age = 18;s1.gender = 'M';printf("name:%s,age:%d,gender:%c", s1.name, s1.age, s1.gender);
}
3.结构体书写的位置决定了它的作用范围
如果是写在了函数的里面表示我们把它定义在了局部位置,只能在本函数中使用
写在了函数的外面则表示我们把它定义在了全局位置,在整个程序中都可以使用
4.小练习:
定义三个学生对象,并且将他们放置于数组中,最后遍历输出
#include<stdio.h>
#include<string.h>
struct student {char name[100];int age;char gender;
};int main() {struct student s1 = {"zhangsan",18,'M'};struct student s2 = {"lisi",18,'M'};struct student s3 = {"wangwu",18,'M'};struct student ans[3] = {s1, s2, s3};for (int i = 0; i < 3; i++) {printf("name:%s,age:%d,gender:%c\n",ans[i].name, ans[i].age, ans[i].gender);}
}
5.起别名
在我们使用结构体时是否会觉得在定义时太过麻烦,这可以用起别名的方式进行简化
举例:
#include<stdio.h>
#include<string.h>
typedef struct student {char name[100];int age;char gender;
} stu;int main() {stu s = {"zhangsan", 18, 'M'};printf("name:%s, age:%d, gender:%c", s.name, s.age, s.gender);
}
注意如果要定义别名的话,在定义结构体时需要在之前加上typedef
二.结构体作为函数参数
在函数中可以传递结构体
其中有两种情况:
1.传递结构体中的数据值
#include<stdio.h>
#include<string.h>
typedef struct student {char name[100];int age;char gender;
} stu;// 因为这个函数用到了结构体,所以这个函数的声明必须写在函数的下面,否则程序会报错
void change_age(stu s) {printf("修改前的数据为:name:%s, age:%d, gender:%c\n", s.name, s.age, s.gender);printf("请输入修改后的年龄为:");scanf("%d", &s.age);printf("修改后的数据为:name:%s, age:%d, gender:%c\n", s.name, s.age, s.gender);
}int main() {stu s = {"zhangsan", 18, 'M'};change_age(s);
}
像这一段代码所改变的仅仅只是在函数中的数据,并不会对主函数中的数据产生影响,真正想要对主函数的数据产生影响的话还是得要用到第二种传递方式
2.传递结构体的地址值(指针)
#include<stdio.h>
#include<string.h>
typedef struct student {char name[100];int age;char gender;
} stu;// 因为这个函数用到了结构体,所以这个函数的声明必须写在函数的下面,否则程序会报错
void change_age(stu* p) {printf("修改前的数据为:name:%s, age:%d, gender:%c\n", (*p).name, (*p).age, (*p).gender);printf("请输入修改后的年龄为:");scanf("%d", &((*p).age));
}int main() {stu s = {"zhangsan", 18, 'M'};stu* p = &s;change_age(p);printf("修改后的数据为:name:%s, age:%d, gender:%c\n", s.name, s.age, s.gender);
}
三.结构体嵌套
如果结构体中的成员的类型是其它的结构体,那么就用到了结构体的嵌套
1.举例:
#include<stdio.h>
#include<string.h>
// 定义一个结构体表示学生,其中的成员有姓名,年龄,性别,联系方式,联系方式又是一个结构体,其中包含手机号和电子邮箱
typedef struct connection {char phone_number[20];char email[30];
} con;// 因为student中用到了connection,所以student必须定义在connection之后,否则会报错
typedef struct student {char name[100];int age;char gender;con c;
} stu;int main() {stu s = {"zhangsan", 18, 'M', "12345678912", "12345678912@qq.com"};printf("name:%s, age:%d, gender:%c, phone_number:%s, email:%s\n", s.name, s.age, s.gender, s.c.phone_number, s.c.email);
}
四.结构体内存对齐(难点)
1.在c语言的结构体中是怎么确定变量到底放在什么位置的呢?
答案是:只能放在自己类型所占的字节数(char为1,int为4等等)整数倍的内存地址上
2.最后怎么确定一个结构体的总长度呢?
答案是:结构体的总长度是最长数据类型的整数倍
3.举例:
如下方的结构体:
struct nums {//double长为8double a;//char长为1char b;//int长为4int c;//char长为1char d;
};
//那么你会不会以为这么一个结构体的长度为14?这样想你就掉入陷阱了
我们最先放下的数据是a,因为内存地址是从0开始的,0是double数据类型的长度——8的倍数,所以前8个字节(0-7)用于放a
其次放下的数据是b,因为char类型的长度仅仅只有1,所以第9个字节(8)就用来放b
再之后是c,轮到它时,我们已经走到了9(第十个字节),但是下一个4的倍数为12,所以我们要一直走到12(第十三个字节)才能放下c
最后是d,它最后放在16(第十七个字节)上
那么这个结构体的长度为17?
又错了
还记得上方的第二点吗?
17之后下一个能被8整除的数为24,这才是这个结构体真正的长度
4.在实际应用开发时我们应该怎么在定义结构体上节省空间
看了上方的解析,其实我们能得出一个结论,在定义结构体时我们应该从小到大依次定义结构体中的数据,这就是最节省结构体空间的定义方式