Structures, Unions, and Enumerations
16.1 结构变量
结构的元素:成员;具有不同类型(面向对象??)
16.1.1 结构变量的声明
struct{...}
指明了类型,而part1 和part2 则是具有这种类型的变量。
结构的成员在内存中是按照声明的顺序存储的。
part1内存形式。假设
- part1
存储在地址为2000的内存单元中
- 每个整数在内存中占4个字节
- NAME_LEN
的值为25
- 成员之间没有间隙
每个结构代表一种新的作用域,为成员设置了独立的名字空间 (name space),上下两个结构name成员不会冲突
struct {
int number;
char name[NAME_LEN+1];
int on_hand;
} part1, part2;
struct {
char name[NAME_LEN+1];
int number; char sex;
} employee1, employee2;
16.1.2 结构变量的初始化
struct {
int number;
char name[NAME_LEN+1];
int on_hand;
} part1 = {528, "Disk drive", 10},
part2 = {914, "Printer cable", 5};
//如果没有初始化则用0表示初始值
16.1.3 指定初始化
点号和成员名称的组合称为指示符
优点: - 易读易验证 - 初始化时不用按顺序
16.1.4 对结构的操作
访问结构内的成员
- 写出结构名字 + .
+ 成员名字
左值
指可以出现在赋值操作符左侧的表达式。左值表示一个内存位置,可以读取或修改该位置存储的值。简单来说,左值是指向内存位置的表达式,可以用来存储数据。
Part1.number = 258; /* changes part1's part number */
Part1.on_hand++; /* increments part1's quantity on hand */
点是C语言的运算符
赋值运算
part2 = part1;
//part1.number 复制到part2.number ,把 part1.name 复制到part2.name ...
//封装稍候将进行复制的数组
struct { int a[10]; } a1, a2;
a1 = a2; /* legal, since a1 and a2 are structures */
16.2 结构类型
命名结构类型
- 可以声明“结构标记”
- 使用typedef
来定义类型名
为什么要命名结构类型?比如说,有一个结构拥有着几个声明,而另一个结构不仅需要前者的声明还需要新的声明。如果重复编写会使程序膨胀,并且将来修改会有风险,而最大的问题是,part1
和part2
不具有兼容的类型,因此part2
≠ part1
,反之亦然,且类型没有名字,所以也不能用作函数调用的参数。
struct {
int number;
char name[NAME_LEN+1]; int on_hand;
} part1;
struct {
int number;
char name[NAME_LEN+1];
int on_hand;
} part2;
16.2.1 结构标记的声明
结构标记(structure tag)
一旦创建了标记part
,就可以用它来声明变量了
结构标记只有在前面放置了单词struct
才会有意义;假如程序拥有part
变量,这两者并不会冲突
结构标记的声明可以和结构变量的声明合并在一起
struct part {
int number;
char name[NAME_LEN+1];
int on_hand;
} part1, part2;
//所有声明为struct part 类型的结构彼此之间是兼容的**
struct part part1 = {528, "Disk drive", 10}; struct part part2;
part2 = part1; /* legal; both parts have the same type */**
16.2.2 结构类型的定义
用typedef
来定义真实的类型名
类型Part
的名字必须出现在定义的末尾
16.2.3 结构作为参数和返回值
函数可以有结构类型的实际参数和返回值
函数显示出结构的成员
void print_part(struct part p) {
printf("Part number: %d\n", p.number); printf("Part name: %s\n", p.name); printf("Quantity on hand: %d\n", p.on_hand);
}
//调用
print_part(part1);
函数返回part
结构,此结构由函数的实际参数构成
struct part build_part(int number, const char * name, int on_hand) {
struct part p;
p.number = number;
strcpy (p.name, name);
p.on_hand = on_hand;
return p;
}
//调用
part1 = build_part(528, "Disk drive", 10);
16.3 嵌套的数组和结构
成员是结构的结构和元素是结构的数组。
16.3.1 嵌套的结构
struct person_name {
char first[FIRST_NAME_LEN+1];
char middle_initial;
char last[LAST_NAME_LEN+1];
};
//用结构person_name 作为更大结构的一部分内容
struct student {
struct person_name name;
int_id, age;
char sex;
} student1, student2;
//访问student1 的名、中名或姓需要两次应用.运算符
strcpy(student1.name.first, "Fred");
为什么有嵌套的结构???
16.3.2 结构数组
数组和结构最常见的组合之一是其元素为结构的数组。这类数组可以用作简单的数据库。
//存储100种零件
struct part inventory[100];
//显示 存储在位置i 的零件
print_part(inventory[i]);
//给 inventory[i] 中的成员number 赋值883
inventory[i].number = 883;
16.3.3 结构数组的初始化
初始化结构数组是为了保存一些你后面不会改变的东西。比如国家地区信息
在结构初始化式的外围括上另一对花括号
struct dialing_code {
char *country; //指针
int code;
};
//包含一些世界上人口最多的国家的代码
const struct dialing_code country_codes[] =
{{"Argentina", 54}, {"Bangladesh", 880},
{"Brazi1", 55}, {"Burma (Myanmar)", 95},
{"China", 86}, {"Colombia", 57},
{"Congo, Dem. Rep. of", 243}, {"Egypt", 20},
{"Ethiopia", 251}, {"France", 33},
{"Germany", 49}, {"India ", 91},
{"Indonesia" 62}, {"Iran", 98},
{"Italy", 39}, {"Japan", 81},
{"Mexico", 52}, {"Nigeria", 234},
{"Pakistan", 92}, {"Philippines", 63},
{"Poland", 48}, {"Russia", 7},
{"South Africa", 27}, {"Korea" 82},
{"Spain", 34}, {"Sudan" 249},
{"Thailand", 66}, {"Turkey", 90},
{"Ukraine", 380}, {"United Kingdom", 44},
{"United States", 1}, {"Vietnam", 84}};
编译、链接
在使用 gcc
编译和链接 C 程序时,通常会经历以下几个步骤:
1. 预处理:处理 #include
、#define
等预处理指令。
2. 编译:将源代码(如 .c
文件)编译成目标文件(.o
文件)。
3. 汇编:将编译生成的中间代码转换成机器代码。
4. 链接:将多个目标文件和库文件链接在一起,生成最终的可执行文件。
这条命令会将inventory.c
和readline.c
这两个源文件编译并链接成一个名为inventory
的可执行文件。编译器会先将每个源文件编译成目标文件(通常是.o
文件/.exe
),然后将这些目标文件链接在一起,生成最终的可执行文件。
./inventory
:这是运行可执行文件的命令。./
表示当前目录,inventory
是可执行文件的名称。
h
文件(头文件)不需要单独操作或编译。头文件通常包含函数声明、宏定义和结构定义等,它们在编译源文件时被包含进来。编译器会在处理源文件时自动处理头文件。