C语言中的结构和其他数据类型

设计程序最重要的一个步骤就是选择一个表示数据的好方法。使用简单的变量和数组时远远不够的,C使用结构变量(structure variable)进一步增强了表示数据的能力。接下来将介绍c语言中的数据类型结构体,联合以及枚举。

结构

结构体的声明

先看下面一个例子:

1
2
3
4
5
6
7
8
#define LEN 20
struct book {
char title[LEN];
char author[LEN];
float value;
}; // 一个结构模板

struct book library; // library 是一个以book结构设计的变量

  以上声明描述了一个有两个字符数组和float变量组成的结构。struct是关键词, 后面跟的是一个可选标记(单词book),最后以分号结尾。描述完结构,就可以以此结构模板声明变量,如:”struct book library;” 声明了一个以book结构设计的变量–libraty。
  简略的声明方法:

1
2
3
4
5
6
#define LEN 20
struct {
char title[LEN];
char author[LEN];
float value;
} library;

结构体的初始化

结构体的一般初始化的方法

1
2
3
4
5
struct book library {
"gali",
"yy",
9.15
};

(C99)运用点(.)运算符初始化,点运算符可以用来访问结构体成员
只初始化成员value

1
struct book test{ .vlaue = 10.99};

任意顺序指定初始化

1
2
3
4
5
struct book temp{
.value = 9.15,
.author = "galiyy",
.title = "test"
}

结构数组

声明一个结构数组与其他类型数组类似

1
struct book library[100];

访问数组元素的成员

1
2
3
library[0].value    /* 第1个数组元素的value成员*/
library[10].author /* 第11个数组元素的author成员*/
library[5].title[3] /* title成员的第4个字符*/

嵌套结构

在一个结构体嵌套另一个结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct names {          //第一个结构
char first[LEN];
char last[LEN];
};
struct guy { // 嵌套结构
struct names handle; // 在内部声明一个结构变量
char favfood[LEN];
char job[LEN];
float income;
};

int main(void)
{
struct guy fellow = { // 嵌套结构的初始化
{"Ewen", "Villard"},
"grilled salmon",
"personall coach",
58112.00
};
return 0;
}

访问嵌套结构中的成员

1
printf("Hello! %s\n", fellow.handle.first);

指向结构的指针

声明和初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct guy fellow[2] = {  // 声明包含2个元素的结构数组
{
{"Ewen", "Villard"},
"grilled salmon",
"personality coach",
58112.00
},
{
{"Rodney", "Swillbelly"},
"tripe",
"tabloid editor",
232400.00
}
};
struct guy * him; // 声明一个指向guy结构的指针
him = &fellow[0]; // fellow[0]是一个结构体

这里值得注意一下,与数组不同,一个结构体的名字不是该结构体的地址,需要用&取址运算符。

用指针访问成员

1
2
3
4
5
him = &fellow[0];    // him指向fellow[0]
him -> income;
him -> name.first;
him++; // him指向下一个结构fellow[1]
fellow[1].income == (*him).income;

函数与结构体

直接通过代码理解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/* book.c -- 仅包含一本书的图书目录 */
#include <stdio.h>
#define MAXTITL 41 /* 书名的最大长度+1 */
#define MAXAUTL 31 /* 作者名的最大长度+1 */
struct book{ /* 结构模板:标记为book */
char title[MAXTITL];
char author[MAXAUTL];
float value;
};
struct book getbook(void); /* 返回一个结构体*/
void showbook(const struct book *); /* 传入结构体的指针 */
int main(void)
{
struct book library; /* 把library声明为book类型的变量*/
struct book * pb; /* 指向book结构的指针*/
pb = &library; /* pb指向了library */
library = getbook();
showbook(pb);

return 0;
}
struct book getbook(void)
{
struct book temp;
printf("Please enter the book title.\n");
gets(temp.title);
printf("Now enter the author.\n");
gets(temp.author);
printf("Now enter the value.\n");
scanf("%f", &temp.value);
return temp; /* 返回结构体 */
}

void showbook(const struct book * pb) /* 传入结构体的指针 */
{
printf("%s by %s, $%.2f.\n", pb->title,
pb->author, pb->value);
}

结构体的特性

数组与数组之间不能相互赋值,但是结构体可以
C允许把一个结构体赋值给另一个结构体
以上代码为例

1
2
3
4
5
6
struct book library;
struct book library2;
library2 = library;

printf("library address :%p\n", &library);
printf("library2 address :%p \n", &library2);

得出的结果

1
2
library address :0061FED0
library2 address :0061FE84

二者存储的位置不同,属于深拷贝。

联合

  联合(union)是一个能在同一个存储空间里(但不同时)存储不同类型的数据的数据类型。在结构中,各成员都占有自己的内存空间,它们是同时存在的。一个结构变量的总长度等于所有成员长度之和。在联合中,所有成员不能同时占用它的内存空间,它们不能同时存在。联合变量的长度等于最长的成员的长度。
  结构变量可以作为函数参数,函数也可返回指向结构的指针变量。而联合变量不能作为函数参数,函数也不能返回指向联合的指针变量。但可以使用指向联合变量的指针,也可使用联合数组。
带标记的联合模板

1
2
3
4
5
union hold {
int digit;
double bigfl;
char letter;
}

声明变量3个hold变量

1
2
3
union hold fit;    /* hold类型的联合变量*/
union hold save[10]; /* 10个联合变量的数组 */
union hold * pu; /* 指向hold类型变量的指针 */

初始化变量

1
2
3
4
5
union hold valA;
valA.letter = 'A';
union hold valB = valA; /* 把一个联合初始化为另一个联合*/
union hold valC = {88}; /* 初始化联合的digit成员*/
union hold valD = {.bigfl = 118.2}; /* 指定初始化项目 */

下面是如何让使用联合(.)或(->):

1
2
3
4
5
fit.digit = 23;      /* 把23存储在fit, 使用2个字节 */
fit.bigfl = 2.0; /* 清除23, 存储2.0, 使用8个字节 */
fit.letter = 'h'; /* 清除2.0, 存储'h', 使用1个字节 */
pu = &fit;
x = fit->digit; /* 指针访问 */

枚举

  枚举类型(enumerated type)可以声明代表整数常量的符号名称。通过使用enum,可以创建一个新“类型”并指定它可以具有的值(实际上,enum常量是int类型)。枚举的目的是为了提高程序的可读性,它的语法与结构类似。

声明

以下是枚举变量的声明:

1
enum 枚举名 {枚举元素1,枚举元素2,……};

如:

1
2
enum spectrum {red, orange, yellow, green, violet, blue};
enum spectrum color;

spectrum为标记名,color是一个变量,花括号里的内容red,orange… 实际都是int类型的常量{0, 1, 2,…}, 默认值时,它会被指定为0, 1, 2等常量
指定值时,

1
enum levels {low = 10, medium = 500, high = 1000};

如果只对一个常量赋值,而后面为赋值时,

1
enum feline {cat, lynx = 10, puma, tiger}

那么cat为0, 而lynx, puma, tiger 为10,11,12。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h> 
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};

int main()
{
enum DAY day;
day = WED;
printf("%d",day);
return 0;
}

typedef

  typedef是在计算机编程语言中用来为复杂的声明定义简单的别名,它与宏定义有些差异。它本身是一种存储类的关键字,与auto、extern、mutable、static、register等关键字不能出现在同一个表达式中。它与define相似,但它们有3个不同:

  • 与define不同,typedef给出的符号名称仅限于对类型,而不是对值。
  • typedef的解释由编译器,而不是预处理器执行。
  • 虽然它的范围有限,但在其受限范围内,typedef比#define更灵活。

以下示例将展示它的用法:
定义变量类型:

1
2
typedef unsigned char BYTE;   /* BYTE是unsigned char 类型 */
BYTE x, y[10], * z;

这里BYTE大小写均可,大写可以方便区分。
指针的应用

1
2
typedef char * STRING;
STRING name, sign;

STRING name相当于 char * name;
结构体中的应用

1
2
3
4
5
6
typedef struct node{
int x;
int y;
}NODE, *pNODE;
NODE node; // NODE 代替了struct node
pNODE pn; // PNODE 代替了struct node *

这里注意node不是类型,必须配合struct一起使用;它只是一个临时标签可有可无。