一、结构体
定义
结构体是C语言一种派生类型,允许多种数据类型的变量组合起来,自定义成新的类型,这些不同类型的变量称为 结构成员变量,变量名称称为结构成员标识符。
注:数组是将多个相同类型的变量组合起来,多个变量没有标识符,只能通过下标访问。
结构体特点
1.结构体允许多个不同类型变量声明和定义
2.结构体可以没有成员变量,空结构
3.结构成员变量不能是函数类型、结构体本身和void等不完整类型
4.结构成员变量可以包含指向自身的指针,也可以包含其他类型结构体
5.结构类型同样需要先声明、定义,再使用
6.结构体类型既可以单独声明和定义,也可以同时声明和定义,可以使用typedef自定义结构类型名称。
结构体声明和定义
结构声明的基本语法:
struct 结构类型名 {
成员变量类型1 变量名称1;
成员变量类型2 变量名称2;
... ...
}结构类型变量名;
结构类型名和结构类型变量名可以相同,也可以不同。例如:以下四种方法结构体声明和定义方法等价。
方法一:声明同时定义结构类型、声明和定义结构变量stu1,stu2
// 声明同时定义结构类型
struct student{
char name[10];
int age;
char sex[4];
};
// 声明和定义结构变量stu1,stu2
struct student stu1,stu2;
方法二:先声明后定义结构类型、声明和定义结构变量
// 先声明
struct student;
// 后定义
struct student{
char name[10];
int age;
char sex[4];
};
// 声明和定义结构变量
struct student stu1,stu2;
方法三:typedef自定义的新的类型名称
typedef struct student
{
char name[10];
int age;
char sex[4];
}STUDENT;
STUDENT stu1,stu2;//STUDENT是typedef自定义的新的类型名称,等价于struct student结构类型名。
方法四:同步声明定义结构类型和结构变量
// 同时声明定义结构类型和结构变量
// 省略结构类型名称,此时该匿名结构类型只能使用一次了
struct
{
char name[10];
int age;
char sex[4];
} stu1,stu2;
特殊的结构体成员变量
1.指向自身的指针变量:结构类型可以包含指向自己的指针变量,不能包含自身。这种特性可以用来建立链表、树型等复杂数据结构。例如
// 以下合法,指向自身的指针
struct student {
...
struct student *s;
};
// 以下不合法,指向自身
struct student {
... struct student s;
};
2.指向其他结构体的成员变量:例如
struct studentType1 {
...
};
struct studentType2{
...
struct studentType1 ss;
}stu1,stu2;
结构体成员变量初始化和访问
成员变量通过点运算符 .
访问。
例如:初始化一条学生记录,初始化并且打印出来。
#include <stdio.h>
struct student{
char name[10];// 姓名
int age;// 年龄
char sex[4];// 性别
char *nickname;// 昵称
};
int main(void){
struct student stu1={
"张三",
20,
"男",
"风一般的男子"};// 定义变量stu1时直接初始化
printf("%s\n", stu1.name);
printf("%d\n", stu1.age);
printf("%s\n", stu1.sex);
printf("%s\n", stu1.nickname);
return 0;
}
运行结果
张三
20
男
风一般的男子
结构指针
结构指针:即指向结构体变量的指针,结构指针使用通过 ->
间接访问结构体成员变量。
例如:初始化一条信息,使用结构指针间接访问成员变量。
#include <stdio.h>
struct student{
char name[10];// 姓名
int age;// 年龄
char sex[4];// 性别
char *nickname;// 昵称
};
int main(void){
struct student stu1 = {
"张三",
20,
"男",
"风一般的男子"};// 定义变量stu1时直接初始化
struct student *sp=&stu1; // 定义结构体指针变量,并且初始化
printf("%s\n", sp->name);
printf("%d\n", sp->age);
printf("%s\n", sp->sex);
printf("%s\n", sp->nickname);
return 0;
}
结构体与函数
结构体可以直接作为函数参数和返回值;
结构体和普通变量一样,通过值传递传给函数参数,也可以作为整体返回值;
结构体指针也可以作为参数传递给函数,同样也可以作为函数返回值类型。
二、联合
1.联合,也称为联合体或共用体,语法和结构一样。
2.与结构相比,结构体是有n个成员变量,就分配n个成员的存储空间,各个成员变量独立;
3.联合是有n个成员变量,只分配1个成员变量(最大的成员变量)存储空间,各个成员变量共享相同的内存空间。
特点:可以声明多个不同类型成员变量;
多个成员变量共享相同存储空间,修改任一变量影响其它变量的值
联合的声明和定义
nion 联合类型名称 {
变量成员类型1 变量名1;
变量成员类型2 变量名2;
... ...
}联合类型变量名;
例如:语法和结构一样。
union test;// 仅声明联合类型test
// 声明和定义联合类型test
union test {
int i;
float f;
};
// 声明定义联合类型,并且声明两个联合变量
union test {
int i;
float f;
}test1,test2;
访问联合成员变量
使用点号: 联合类型变量名 . 成员变量名访问。
例如:定义一个字符和整数共用的联合体,并且访问成员变量的值。
#include <stdio.h>
union test{
char a;
int b;
};// 声明和定义联合类型test
int main(void){
union test u;//声明联合变量u
u.a = 'A';
printf("%c\n", u.a);
printf("%d\n", u.a);
printf("%d\n", u.b);
u.b = 66;
printf("%c\n", u.a);
printf("%d\n", u.a);
printf("%d\n", u.b);
return 0;
}
运行结果:
A
65
65
B
66
66
// 结果分析:
成员变量a和b共用同一块内存空间,在内存中,成员变量a和b的二进制位表示永远保持一致,
输出时根据不同类型进行解释。
三、枚举
枚举类型:用来定义一些离散的命名的整数常量。枚举类型变量只能取这些命名的整数常量值。
使用枚举变量,可以让C语言程序简单,容易阅读和理解。
enum关键字用来声明枚举类型。
枚举类型声明和定义
语法:
enum 枚举类型名称
{
枚举元素名1 [= 整数初值],
枚举元素名2 [= 整数初值],
枚举元素名3 [= 整数初值],
...
}枚举类型变量名;
特点:
a. 枚举类型名称和变量名可以相同,可以任意合法的标识符
- 省略初始化时,编译器默认给元素按顺序赋值初始化
- 枚举类型同样需要先声明定义,后使用
以下五种方式基本等价
1.定义枚举,同时声明两个枚举变量
enum mouth{
Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sept,Oct,Nov,Dec
}mouth1,mouth2;//定义和声明十二个月份的枚举。
2.定义枚举,然后声明两个枚举变量
enum mouth{
Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sept,Oct,Nov,Dec
};//定义枚举
enum mouth mouth1,mouth2;//声明两个枚举变量
3.声明枚举,定义枚举,声明枚举变量
enum mouth;//声明枚举
enum mouth{
Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sept,Oct,Nov,Dec
};//定义枚举
enum mouth mouth1,mouth2;//声明枚举变量
4.省略枚举类型名称,直接一次性声明和定义该枚举类型变量
enum{
Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sept,Oct,Nov,Dec
}mouth1,mouth2;
5.typedef自定义枚举类型名称,简化枚举类型变量声明
typedef enum mouth{
Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sept,Oct,Nov,Dec
}MOUTH; //typedef自定义枚举类型名称
// 省略mouth也可以,即简化枚举类型变量声明
typedef enum{
Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sept,Oct,Nov,Dec
}MOUTH2;
MOUTH mouth1;
MOUTH2 mouth2;
枚举变量值
枚举变量值本质上是整数,一般是int型。
1.默认初始化时,元素的值从0开始计数
enum mouth{
Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sept,Oct,Nov,Dec
};
2.赋值初始化
可以全部赋初值,此时枚举变量的取值为任意不重复的整数,不需要按照大小排序和递增。
enum mouth{
Jan=-1,Feb=2,Mar=5,Apr=6,May=10,Jun=11,Jul=-2,Aug=3,Sept=14,Oct=-5,Nov=12,Dec=15
};
3.部分赋值初始化
部分赋初值,未赋值部分在前一个枚举常量的值之上加1。
enum mouth{
Jan=-1,Feb,Mar,Apr=6,May=10,Jun=11,Jul=-2,Aug=3,Sept,Oct=-5,Nov=12,Dec=15
};
此时Feb=0 Mar=1 Sept=4
输出枚举元素的值
#include <stdio.h>
enum mouth{ //定义枚举
Jan,
Feb,
Mar,
Apr,
May,
Jun,
Jul,
Aug,
Sept,
Oct,
Nov,
Dec
};
int main(void){
printf("%d\n", Jan);
printf("%d\n", Feb);
printf("%d\n", Dec);
return 0;
}
输出结果:
0
1
11
枚举使用
枚举变量可以直接使用枚举类型成员名称赋初值或者整数强制转换赋初值。
例如:判断枚举变量是否是12月份。
#include <stdio.h>
enum mouth{
Jan,//1
Feb,
Mar,
Apr,
May,//5
Jun,
Jul,
Aug,//8
Sept,
Oct,//10
Nov,
Dec
};
int main(void){
enum mouth m1;// 声明枚举变量m1
m1=(enum mouth)11;// 初始化枚举变量m1,初值为Dec
if(m1 == Dec)
printf("是十二月份\n");
return 0;
}
输出结果:是十二月份
结果分析:枚举类型本质上是整型,因此整数可以强制转换枚举类型值,整数可以直接赋值给枚举类型变量。
四、位段
位段是结构或联合类型的一种成员变量.
位段特点是该成员按位进行内存空间分配,其它普通成员最小是按照字节或字节的整数倍进行分配的。
结构或联合的成员变量可以按照二进制位数进行声明和分配空间,可以将多个成员保存到一个字节或机器字中。这种成员变量称为位段。
注:机器字是指计算机硬件指令一次整数运算能够处理最大的字长,例如32位机器,机器字长32位,4个字节。字节是计算机内存最小的编址单元。
位段的特点:
•位段本质上仍是结构或联合类型的一个成员变量
•位段节约存储空间,实现按照二进制位进行分配空间
•位段常用于标志位集合,简化操作和节约空间
•位段仅仅支持int
,signed int
, unsigned int
类型,位段成员值为整型
•位段无法使用 &
运算符获取地址,不存在位段指针或数组
位段的声明和定义
位段使用冒号和二进制位宽度声明和定义。
编译器根据二进制位个数分配内存空间,考虑内存对齐,一般会分配变量类型长度整数倍。
联合或共用类型的位段:
struct/union 类型名称 {
int 成员名称 : 位宽度;
int 成员名称 : 位宽度;
....
};
示例:
// 声明3个位段变量
// 实际使用3个二进制位,编译器分配4个字节,sizeof(struct test) == 4
struct test {
int a : 1;
int b : 1;
int c : 1;
};
// 声明3个位段变量
// 实际使用34个二进制位,编译器分配8个字节,sizeof(struct test) == 8
// 因为1个int型空间无法保存,所以分配2个
struct test {
int a : 31;
int b : 2;
int c : 1;
};
// 未命名位段,位段成员名称可以省略
// 作用是填充作用,特殊宽度0强制在下一个机器字边界对齐
// 声明4个位段成员变量,分配8个字节空间
// 未命名字段填充占据31二进制位空间
struct test{
int a:1;
int : 0;
int b:1;
int c:1;
};
位段使用
位段的使用和结构类型其它类型成员语法一致。
注:不同之处是位段不允许使用 & 运算符获取地址,结构类型其它成员可以。
例如:旅馆有5个房间,需要记录是否有 人住。
分析:1.一般可以使用5个int
型记录,0表示未住人,1表示住人;
2.为节约存储空间,使用5个二进制位记录,实际只需要1个int
型空间。
#include <stdio.h>
struct hotel{ // 声明5个位段变量
unsigned int room1 : 1;
unsigned int room2 : 1;
unsigned int room3 : 1;
unsigned int room4 : 1;
unsigned int room5 : 1;
};
int main(void){
struct hotel myhotel = { // 初始化位段
1, 0, 0, 1, 1};
// 输出位段值
printf("%u %u %u %u %u\n", myhotel.room1, myhotel.room2, myhotel.room3, myhotel.room4, myhotel.room5);
return 0;
}
运行结果:1 0 0 1 1
注:C标准未对位段内存分配、对齐等进行规定,位段实现是机器相关的,使用位段时,注意程序的移植性影响。
五、自定义数据类型
C语言自定义数据类型名称
C语言不仅内置int,float,char,函数,指针等多种数据类型,也提供typedef
关键字用于创建新的数据类型名称, 方便自定义数据类型。
注:本质上typedef没有创建新的类型,只是增加了新的类型名称。
typedef的使用
语法:typedef 已有类型名称 新类型名称
例如:
typedef int MyInterger;
int i,j;// 整型变量声明
MyInterger i,j; // 整型变量声明,等价于上面声明
typedef char *MyString; // 自定义char *类型名Mystring
char *s;// 常规声明字符串
MyString s; // 使用新名称简化,等价上面声明
typedef简化结构类型应用
使用typedef可以简化结构体等复合类型名称,起名也可以增加代码可读性。
// 自定义结构类型名称
typedef struct node {
.....
}MyNode;
struct node node1;//声明结构体变量node1
MyNode node1; // 等价上面
// 自定义结构指针类型
typedef struct node *NodePtr;
struct node *p;//声明结构体指针变量p
NodePtr p; // 等价上面
自定义函数类型指针名称
typedef定义一个指向一类函数的指针。
例如:
typedef int (*FunPtr)(int , int );
FunPrt f1,f2;
说明:
1.FunPtr是函数指针类型,该函数类型是返回值为int,参数为两个int型的函数;
2.f1,f2是两个函数指针类型变量。
typedef的作用
1.简化代码,增强可读性;
2.将程序机器相关的内置类型参数化,提高可移植性。例如不同平台上的内置数据类型是不一样的,程序移植到不同平台时,只需要typedef定义即可,其它位置无需修改。
例如:typedef int MyInteger;
如果A平台不支持int型,只支持long类型,则可以简单修改定义 typedef long MyInterger;
即可,程序代码无需修改。
-------------------------END-------------------------