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