《Linux C一站式学习》第七章 结构体
1. 复合类型与结构体
在编程语言中,最基本的、不可再分的数据类型称为基本类型(Primitive Type),例如整型、浮点型;根据语法规则由基本类型组合而成的类型称为复合类型(Compound Type),例如字符串是由很多字符组成的。
struct complex_struct {
double x, y;
};
struct complex_struct {
double x, y;
} z1, z2;
struct complex_struct z3, z4;
struct complext_struct z = { 3.0, 4.0 }
Initializer中的数据依次赋给结构体的各成员。如果Initializer中的数据比结构体的成员多,编译器会报错,但如果只是末尾多个逗号则不算错。如果Initializer中的数据比结构体的成员少,未指定的成员将用0来初始化,就像未初始化的全局变量一样。
这样是错误的:
struct complex_struct z1;
z1 = { 3.0, 4.0 };
Designated Initializer是C99引入的新特性,用于初始化稀疏(Sparse)结构体和稀疏数组很方便。有些时候结构体或数组中只有某一个或某几个成员需要初始化,其它成员都用0初始化即可,用Designated Initializer语法可以针对每个成员做初始化(Memberwise Initialization),很方便。
struct complex_struct z1 = { .y = 4.0 };
结构体类型用在表达式中有很多限制,不像基本类型那么自由,比如+ - * /等算术运算符和&& || !等逻辑运算符都不能作用于结构体类型,
if
语句、while
语句中的控制表达式的值也不能是结构体类型。
结构体变量之间使用赋值运算符是允许的:z1 = z2;
注:结构体作为变量声明、参数、返回值都要加上struct关键字。
2. 数据抽象
1、在本节的基础上实现一个打印复数的函数,打印的格式是x+yi,如果实部或虚部为0则省略,例如:1.0、-2.0i、-1.0+2.0i、1.0-2.0i。最后编写一个
main
函数测试本节的所有代码。想一想这个打印函数应该属于上图中的哪一层?
答:
void print_complex(struct complext_struct z)
{
if (z.x == 0 && z.y == 0) {
printf("0");
} else if (z.x == 0 && z.y != 0) {
printf("%.1f i", z.y);
} else if (z.x != 0 && z.y == 0) {
printf("%.1f", z.x);
} else {
printf("%.1f + %.1f i", z.x, z.y);
}
}
2、实现一个用分子分母的格式来表示有理数的结构体
rational
以及相关的函数,rational
结构体之间可以做加减乘除运算,运算的结果仍然是rational
。注意要约分为最简分数,例如1/8和-1/8相减的打印结果应该是1/4而不是2/8,可以利用第 3 节
“递归”练习题中的Euclid算法来约分。
答:
#include <stdio.h>
#include "gcd.c"
struct rational
{
int x;
int y;
}; //分号不能忘!
struct rational add(struct rational a, struct rational b)
{
struct rational c;
c.x = a.x + b.x;
c.y = a.y + b.y;
return simply(c);
}
// 变量和函数不能重名 int gcd会出错
struct rational simply(struct rational a)
{
int gcdnum;
if (abs(a.x) > abs(a.y)) {
gcdnum = gcd(abs(a.x), abs(a.y));
} else {
gcdnum = gcd(abs(a.y), abs(a.x));
}
a.x = a.x / gcdnum;
a.y = a.y / gcdnum;
return a;
}
void print(struct rational a)
{
printf("%d/%d \n", a.x, a.y);
}
int main(void)
{
struct rational a = { -1, 8 };
struct rational b = { -1, 8 };
struct rational c = add(a, b);
print(c);
return 0;
}
3. 数据类型标志
enum coordinate_type { RECTANGULAR, POLAR };
struct complex_struct {
enum coordinate_type t;
double a, b;
};
枚举类型的成员是常量,它们的值由编译器自动分配,例如定义了上面的枚举类型之后,
RECTANGULAR
就表示常量0,POLAR
表示常量1。如果不希望从0开始分配,可以这样定义:
enum coordinate_type { RECTANGULAR = 1, POLAR };
枚举常量也是一种整型,其值在编译时确定,因此也可以出现在常量表达式中,可以用于初始化全局变量或者作为
case
分支的判断条件。
虽然结构体的成员名和变量名不在同一命名空间中,但枚举的成员名却和变量名在同一命名空间中,所以会出现命名冲突。例如这样是不合法的:
int main(void)
{
enum coordinate_type { RECTANGULAR = 1, POLAR };
int RECTANGULAR;
}
习题
2. 编译运行下面这段程序:
#include <stdio.h>
enum coordinate_type { RECTANGULAR = 1, POLAR };
int main(void)
{
int RECTANGULAR;
printf("%d %d\n", RECTANGULAR, POLAR);
return 0;
}
结果是什么?并解释一下为什么是这样的结果。
答:3719156 2。main函数中的局部变量作用域覆盖了外部的枚举。
4. 嵌套结构体
struct segment {
struct complex_struct start;
struct complex_struct end;
};
Initializer也可以嵌套,因此嵌套结构体可以嵌套地初始化:
struct segment s = { { 1.0, 2.0 }, { 4.0, 6.0 } };
访问嵌套结构体的成员要用到多个.运算符,例如:
s.start.t = RECTANGULAR;
s.start.a = 1.0;