C 语言内置的数据类型,除了几种原始的基本数据类型,只有数组属于复合类型,可以同时包含多个值,但是只能包含相同类型
的数据,实际使用场景受限。
举例:隔壁老王养了两只猫咪。一只名字叫小黄,今年2岁,橘色;另一只叫小黑,今年3岁,黑色。请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字,年龄,颜色。如果用户输入的小猫名错误,则显示老王没有这只猫。
传统的解决方法
尝试1
:单独定义多个变量存储,实现需求。但是,多个变量,不便于数据的管理。
尝试2
:使用数组,它是一组具有相同类型的数据的集合。但在编程中,往往还需要一组类型不同的数据,例如猫的名字使用字符串、年龄是int,颜色是字符串,因为数据类型不同,不能用一个数组来存放。
尝试3
:C语言提供了结构体。使用结构体,内部可以定义多个不同类型的变量作为其成员。
考研中见的最多的就是指针和结构体结合起来构造结点(如链表的结点、二叉树的结点等)。
C 语言提供了 struct
关键字,允许自定义复合数据类型,将不同类型的值组合在一起,这种类型称为结构体(structure)类型。
C 语言没有其他语言的对象(object)和类(class)的概念,struct 结构很大程度上提供了对象和类的功能。
比如:
构建一个结构体类型的一般格式:
struct 结构体名{
数据类型1 成员名1; //分号结尾
数据类型2 成员名2;
……
数据类型n 成员名n;
}; //注意最后有一个分号
举例:学生
struct Student{
// 定义结构体:学生
int id; //学号
char name[20]; //姓名
char gender; //性别
char address[50]; //家庭住址
};
举例:猫
struct Cat{
char name[20]; //名字
int age; //年龄
char color[20]; //颜色
};
举例:人类
struct Person{
char name[20]; //姓名
char gender; //性别
int age; //年龄
double weight; //体重
};
举例:通讯录
struct Contacts{
char name[50]; //姓名
int year; //年
int month; //月
int day; //日
char email[100]; //电子邮箱
char phone_number[15]; //手机号
};
举例:员工
struct Employee {
int id; //员工编号
char name[20]; //员工姓名
char gender; //员工性别
int age; //员工年龄
char address[30]; //员工住址
};
定义了新的数据类型以后,就可以声明该类型的变量,这与声明其他类型变量的写法是一样的。
声明结构体变量格式1:
struct 结构体类型名称 结构体变量名;
注意,声明自定义类型的变量时,类型名前面,不要忘记加上 struct 关键字。
举例:
struct Student stu1;
调用结构体变量的成员:
结构体变量名.成员名 [= 常量或变量值]
举例:
#include <stdio.h>
#include <string.h>
int main() {
struct Student stu1; //声明结构体变量
//调用结构体成员
stu1.id = 1001;
//stu1.name = "Tom"; //报错,不能直接通过赋值运算符来给字符数组赋值
strcpy(stu1.name, "Tony");
stu1.gender = 'M';
strcpy(stu1.address, "北京市海淀区五道口");
printf("id = %d,name = %s,gender = %c,address = %s\n",
stu1.id, stu1.name, stu1.gender, stu1.address);
return 0;
}
说明:
1)先声明了一个 struct Student类型的变量 stu1,这时编译器就会为 stu1 分配内存,接着就可以为 stu1 的不同属性赋值。可以看到,struct 结构的属性通过点( . )来表示,比如 id 属性要写成 stu1.id。
2)字符数组是一种特殊的数组,直接改掉字符数组名的地址会报错,因此不能直接通过赋值运算符来对它进行赋值。你可以使用字符串库函数
strcpy()
来进行字符串的复制操作。
声明结构体变量格式2:
除了逐一对属性赋值,也可以使用大括号,一次性对 struct 结构的所有属性赋值。此时,初始化的属性个数最好与结构体中成员个数相同,且成员的先后顺序一一对应。格式:
struct 结构体名 结构体变量={
初始化数据};
举例:
//声明结构体
struct Car {
char* name;
double price;
int speed;
};
//声明结构体变量
struct Car audi = {
"audi A6L", 460000.99, 175};
注意:如果大括号里面的值的数量少于属性的数量,那么缺失的属性自动初始化为 0 。
struct Student {
int id;
char name[20];
char gender;
int score; //学生成绩
};
int main() {
struct Student stu = {
1001, "songhk", 'M'};
printf("Name: %s\n", stu.name);
printf("Score: %d\n", stu.score);
return 0;
}
声明结构体变量格式3:
方式2中大括号里面的值的顺序,必须与 struct 类型声明时属性的顺序一致。此时,可以为每个值指定属性名。
格式:
struct 结构体名 结构体变量={
.成员1=xxx,.成员2=yyy,...};
举例:
struct Car audi = {
.speed=175, .name="audi A6L"};
同样,初始化的属性少于声明时的属性,剩下的那些属性都会初始化为 0 。
声明变量以后,可以修改某个属性的值。
struct Car audi = {
.speed=175, .name="audi A6L"};
audi.speed = 185; //将 speed 属性的值改成 185
声明结构体变量格式4: 声明类型的同时定义变量
struct 的数据类型声明语句与变量的声明语句,可以合并为一个语句。格式:
struct 结构体名 {
成员列表
} 变量名列表;
举例:同时声明了数据类型 Circle 和该类型的变量 c1
struct Circle {
int id;
double radius;
} c1;
举例:
struct Employee {
char name[20];
int age;
char gender;
char phone[11];
} emp1, emp2;
声明结构体变量格式5: 不指定类型名而直接定义结构体类型变量
如果类型标识符(比如Student、Circle、Employee等)只用在声明时这一个地方,后面不再用到,那就可以将类型名省略。 该结构体称为匿名结构体
。
格式:
struct {
成员列表;
} 变量名列表;
举例:
struct {
char name[20];
int age;
char gender;
char phone[11];
} emp1, emp2;
struct 声明了一个匿名数据类型,然后又声明了这个类型的两个变量emp1、emp2 。与其他变量声明语句一样,可以在声明变量的同时,对变量赋值。
举例:
struct {
char name[20];
int age;
char gender;
char phone[11];
} emp1 = {
"Lucy", 23, 'F', "13012341234"},
emp2 = {
"Tony", 25, 'M', "13367896789"};
上例在声明变量 emp1 和 emp2 的同时,为它们赋值。
**声明结构体变量格式6:**使用 typedef 命令
使用 typedef 可以为 struct 结构指定一个别名,这样使用起来更简洁。
举例:
//声明结构体
typedef struct cell_phone {
int phone_no; //电话号码
double minutes_of_charge; //每分钟费用
} Phone;
//声明结构体变量
Phone p = {
13012341234, 5};
上例中, Phone 就是 struct cell_phone 的别名。声明结构体变量时,可以省略struct关键字。
这种情况下,C 语言允许省略 struct 命令后面的类型名。进一步改为:
//声明匿名结构体
typedef struct {
int phone_no;
double minutes_of_charge;
} Phone;
//声明结构体变量
Phone p = {
13012341234, 5};
进一步,在考研中,还会出现如下的声明方式:
typedef struct {
int phone_no;
double minutes_of_charge;
} Phone,*pPhone;
这里多了个*pPhone,其实在定义一个结点指针p时,Phone *p;
等价于 pPhone p;
,前者的写法类似于int *a、char *b等更方便记忆,不必再加个pPhone p来增加记忆负担。所以在考研中我们不采用这种方法,统一删掉 *pPhone的写法。
说明:
1、在创建一个结构体变量后,需要给成员赋值。在没有给成员赋值的情况下调用,打印的值是垃圾数据,可能导致程序异常终止。
2、不同结构体变量的成员是独立的,互不影响,一个结构体变量的成员更改,不影响另外一个。
练习:盒子案例
(1)编程创建一个Box结构体,在其中定义三个成员表示一个立方体的长、宽和高,长宽高可以通过控制台输入。
(2)定义一个函数获取立方体的体积(volume)。
(3)创建一个结构体,打印给定尺寸的立方体的体积。
#include <stdio.h>
// 1. 定义Box结构体
struct Box {
double length;
double width;
double height;
};
// 2. 获取立方体体积的函数
double getVolume(struct Box box) {
return box.length * box.width * box.height;
}
int main() {
// 3. 创建结构体实例
struct Box box;
printf("输入长度:");
scanf("%lf", &box.length);
printf("输入宽度:");
scanf("%lf", &box.width);
printf("输入高度:");
scanf("%lf", &box.height);
// 调用函数获取体积并打印
printf("体积为: %.2lf\n", getVolume(box));
return 0;
}
区分三个概念:结构体、结构体变量、结构体变量的成员。
结构体是自定义的数据类型,表示的是一种数据类型。
结构体变量代表一个具体变量。类比:
int num1 ; // int 是数据类型, 而num1是一个具体的int变量
struct Car car1; // Car 是结构体数据类型,而car1是一个Car变量
Car 就像一个“汽车图纸”,生成出来的具体的一辆辆汽车,就类似于一个个的结构体变量。这些结构体变量都含有相同的成员,将结构体变量的成员比作“零件”,同一张图纸生产出来的零件的作用都是一样的。
结构体的成员也是变量,那么成员可以是基本数据类型
,也可以是数组
、指针
、结构体
等类型 。如果结构体的成员是另一个结构体,这就构成了结构体嵌套。
举例1:
#include <stdio.h>
#include <string.h>
struct Name {
char firstName[50];
char lastName[50];
};
struct Student {
int age;
struct Name name;
char gender;
} stu1;
int main(){
strcpy(stu1.name.firstName, "美美");
strcpy(stu1.name.lastName, "韩");
//stu1.age = 18;
//stu1.gender = 'F';
//或者
struct Name myname = {
"美美","韩"};
stu1.name = myname;
//stu1.age = 18;
//stu1.gender = 'F';
return 0;
}
举例2:
struct Date {
//声明一个结构体类型 struct Date
int year; //年
int month; //月
int day; //日
};
struct Employee {
//声明一个结构体类型 struct Employee
int id;
char name[20];
int age;
struct Date birthday; //成员birthday属于struct Date类型
};
声明结构体变量并调用成员:
#include <stdio.h>
#include