C结构体基础(一)
- 什么是结构体
- 建立结构体类型
- 定义结构体类型变量
- 结构体变量的初始化
- 结构体变量的引用
-
- 1. 引用成员的基本方式
-
- 补充:字符数组成员不能直接赋值
- 方法一:逐个字符复制
- 方法二:使用 strcpy 函数
- 2. 成员本身也是结构体时的引用方式
- 3. 结构体成员可以参与运算
- 4. 同类型的结构体变量可以相互赋值
- 5. 结构体变量和成员都可以取地址
- 6. 两种错误用法
什么是结构体
数组只能存储相同类型的数据,但在实际使用中,我们常常希望有一种数据类型,可以把多个不同类型的数据放在一起进行管理。而结构体正好满足这个需求,它和类和对象中的类非常像:我们可以先设定一个结构类型,在其中自定义多个不同类型的数据变量。结构体变量创建之后,就可以使用结构体变量调用其中的数据变量进行赋值等操作。
建立结构体类型
格式:
struct 结构体名{
成员表列
};
说明:
下面以 Student 与 Teacher 两个结构体的定义为例子:
struct Student{
int id;
char name[20];
int age;
float score;
};
struct Teacher{
int id;
char name[20];
char course[30];
float salary;
};
定义结构体类型变量
这里再简单说一下:结构体类型和 int、float 一样,都是一种数据类型。不同的是,int、float 这类类型是由系统预先定义好的,而结构体类型则是由用户根据实际需求自行指定的。因此,定义一个结构体类型变量,和定义一个 int 类型变量在语法上非常相似。
类比:
int a;
float b;
struct Student student1, student2;
在上面的声明中,struct Student 是结构体类型名,作用类似于 int a; 中的 int;student1 和 student2 则是结构体变量名,作用类似于 int a; 中的 a。
当然,也可以在声明结构体类型的同时定义变量:
struct Student{
int id;
char name[20];
int age;
float score;
}student1, student2;
但通常并不推荐这样写。因为这种写法把「结构体类型的定义」和「结构体变量的定义」糅合在了一起,代码变长后不容易区分哪些是类型、哪些是变量;同时,如果后续还想继续使用 Student 这个结构体类型,也不够灵活。更推荐的做法仍然是:先单独定义好结构体类型,再按需像 int a; 一样去定义变量。
结构体变量的初始化
结构体变量的初始化,通常是在定义结构体变量时进行的。
例如,先定义好 Student 结构体类型,再在定义变量时赋值:
struct Student {
int id;
char name[20];
int age;
float score;
} student = {20260101, "张三", 18, 125.5};
也可以在主函数中这样初始化:
struct Student student = {.name = "张三", .score = 125.5};
这里使用的是指定初始化,.name 表示给结构体变量 student 中的 name 成员赋值,.score 表示给 score 成员赋值。指定初始化时可以不按成员顺序书写,也可以只初始化部分成员。
需要注意的是,如果只初始化了部分成员,那么未被初始化的成员会自动被初始化为零值:数值型成员为 0,字符型成员为 '\\0',指针型成员为 NULL。
结构体变量的引用
定义结构体变量之后,就可以使用结构体变量来引用其中的成员。
1. 引用成员的基本方式
引用结构体变量中某个成员的方式为:
结构体变量名.成员名
例如,给结构体变量 student 中的 id 成员赋值:
student.id = 20260101;
这里的 . 称为成员运算符,student.id 就表示 student 这个结构体变量中的 id 成员。这句话的作用就是把 20260101 赋给 student 的 id 成员。
补充:字符数组成员不能直接赋值
试运行下面的代码,你会发现编译器报出错误:
[错误] assignment to expression with array type
这是因为我们之前讲过,字符串在 C 语言中本质上就是一个字符数组,而数组名表示数组的首地址,是一个地址常量,不能放在赋值号左边。也就是说,数组之间不能直接相互赋值。
#include <stdio.h>
struct Student{
char name[50];
int id;
};
int main(){
struct Student stu;
stu.name = "张三";
stu.id = 20260101;
printf("姓名:%s,学号:%d", stu.name, stu.id);
return 0;
}
上面代码中,stu.name 是一个字符数组,"张三" 是一个字符串,stu.name = "张三"; 相当于试图把一个字符串赋给一个数组名,因此会报错。为了解决这个问题,我们有两种办法。
方法一:逐个字符复制
既然数组不能整体赋值,但数组中的单个元素是可以赋值的,那么我们可以通过循环,把字符串中的字符一个一个复制到 stu.name 中。
#include <stdio.h>
struct Student{
char name[50];
int id;
};
int main(){
struct Student stu;
char Name[10] = "张三";
for (int i = 0; i < 10; i++){
stu.name[i] = Name[i];
}
stu.id = 20260101;
printf("姓名:%s,学号:%d", stu.name, stu.id);
return 0;
}
不过需要注意的是,这种方法也容易写出问题。比如:
for (int i = 0; i < 2; i++){
stu.name[i] = Name[i];
}
如果把循环条件中的 i < 10 改成了 i < 2,最后输出的姓名就只有“张”。这是因为前面说过,字符串中会有一个结束标志 '\\0':"张三" 在内存中实际存放的是 '张'、'三'、'\\0' 三个字符。上面的循环只复制了前两个字符,没有把 '\\0' 复制到 stu.name 中,printf 输出时找不到结束标志,就会继续向后读取,所以输出结果会出错。
如果不知道字符数组的长度,可以用下面这个方法,借助字符串的结束标志 '\\0' 来控制循环:
for (int i = 0; Name[i] != '\\0'; i++){
stu.name[i] = Name[i];
}
stu.name[i] = '\\0';
这样当遇到 '\\0' 时循环结束,就不必提前知道数组长度。不过要记得在循环结束后,再给目标字符串补上一个 '\\0',保证 stu.name 是一个完整的字符串。
方法二:使用 strcpy 函数
逐个字符复制虽然可以解决问题,但写起来比较麻烦,而且容易漏掉 '\\0'。C 语言标准库提供了专门用于字符串复制的函数 strcpy,使用时需要包含头文件 <string.h>。
#include <stdio.h>
#include <string.h>
struct Student{
char name[50];
int id;
};
int main(){
struct Student stu;
strcpy(stu.name, "张三");
stu.id = 20260101;
printf("姓名:%s,学号:%d", stu.name, stu.id);
return 0;
}
strcpy(stu.name, "张三"); 表示把字符串 "张三" 复制到字符数组 stu.name 中。strcpy 会自动把字符串末尾的 '\\0' 一起复制过去,因此比手动逐个字符复制更安全、更方便。使用 strcpy 时要注意:目标数组 stu.name 必须有足够的空间存放源字符串以及最后的 '\\0'。
2. 成员本身也是结构体时的引用方式
如果结构体的某个成员本身也是一个结构体,那么引用时就需要一层一层地使用 . 运算符。
例如,学生结构体中包含一个表示成绩的结构体成员 scores:
student.scores.PE = 85;
student.scores.Math = 120;
上面先通过 student.scores 找到 student 中的 scores 成员,再通过 .PE、.Math 继续引用 scores 内部的成员。
3. 结构体成员可以参与运算
结构体变量中的成员可以像普通变量一样,根据它自身的数据类型进行各种运算。
例如,对 score 这样的 float 类型成员进行加法运算:
student.score = student.score + 10.5;
对 age 这样的 int 类型成员进行自增运算:
student.age++;
这里要顺便说明一下:成员运算符 . 的优先级很高,它和圆括号、下标等运算符同属于最高优先级,因此计算时会优先完成成员引用,再进行其他运算。
4. 同类型的结构体变量可以相互赋值
C 语言允许同类型的结构体变量之间直接整体赋值。
例如:
student1 = student2;
这句话表示把结构体变量 student2 中所有成员的值,依次赋给结构体变量 student1 中对应的成员。
5. 结构体变量和成员都可以取地址
结构体变量本身可以取地址,它的成员也可以取地址。
例如:
&student;
&student.id;
&student 表示整个结构体变量的地址,&student.id 表示其中 id 成员的地址。
6. 两种错误用法
不能把整个结构体变量当作一个整体直接输出。下面的写法是错误的:
printf("%s\\n", student);
结构体变量中通常包含多个不同类型的成员,不能直接用 %s 这样的格式说明符输出整个结构体。
也不能对结构体变量取地址后,再用与成员不对应的格式说明符整体输出:
printf("%d,%s,%d,%f", &student);
&student 是整个结构体变量的地址,并不是某个具体成员的值。要输出结构体中的内容,应当逐个成员进行输出,例如:
printf("%d,%s,%d,%f", student.id, student.name, student.age, student.score);
网硕互联帮助中心



评论前必须登录!
注册