C入门系列:第一章 基本类型
C入门系列:第二章 字符串
C入门系列:第三章 函数
C入门系列:第四章 数组和指针
C入门系列:第五章 数据存储类别和内存管理
C入门系列:第六章 文件输入/输出
C入门系列:第七章 结构和其他数据形式
C入门系列:第八章 C预处理器和C库
结构可以把一个结构赋值给另一个结构,每个成员的值都赋值,即使成员是数组。
struct names o_data; struct names n_data; // 假设这个结构以初始化 o_data = n_data; // 把一个结构赋值给另一个结构 struct names right_field = {"Ruthie", "George"}; struct names captain = right_field; // 把一个结构初始化给另一个结构 #include<stdio.h> #include<string.h> #define NLEN 30 struct namect { char fname[NLEN]; char lname[NLEN]; int letters; }; // 返回结构 struct namect getinfo(void); struct namect makeinfo(struct namect); void showinfo(struct namect); char * s_gets(char * st, int n); int main(void) { struct namect person; person = getinfo(); person = makeinfo(person); showinfo(person); return 0; } struct namect getinfo() { struct namect temp; s_gets(temp.fname. NLEN); s_gets(temp.lname, NLEN); return temp; } struct namect makeinfo(struct namect info) { info.letters = strlen(info.fname) + strlen(info.lname); return info; } void showinfo(struct namect info) { printf("%s %s, your name contains %s letters", info.fname, info.lname, info.letters); }函数中传递结构形参和传递结构指针的优缺点:
传递指针访问速度快,但无法保护数据,该问题可以在形参使用const关键字防止修改数据
传递结构的优点是结构清晰,但浪费时间和存储空间,老版本的代码可能无法处理这样的代码
通常,程序员为了追求效率会使用结构指针作为函数参数
在结构中存储字符串,应该使用字符数组;如果要使用字符指针,应该要给字符指针先初始化 malloc() 或 calloc() 分配内存空间。
#include<stdio.h> #include<string.h> #include<stdlib.h> #define SLEN 81 struct namect { char * fname; // 用指针代替数组 char * lname; int letters; }; void getinfo(struct namect *); void makeinfo(struct namect *); void showinfo(const struct namect *); void cleanup(struct namect *); int main(void) { struct namect person; getinfo(&person); makeinfo(&person); showinfo(&person); cleanup(&person); return 0; } void getinfo(struct namect * pst) { char temp[SLEN]; s_gets(temp, SLEN); // 给结构的字符指针分配内存空间 pst->fname = (char *)malloc(strlen(temp) + 1); // 拷贝到已分配的内存 strcpy(pst->fname, temp); s_gets(temp, SLEN); pst->lname = (char *)malloc(stelen(temp) + 1); strcpy(pst->lname, temp); } void makeinfo(struct namect * pst) { pst->letters = strlen(pst->fname) + strlen(pst->lname); } void cleanup(struct namect * pst) { free(pst->fname); free(pst->lname); } char * s_gets(char *st, int n) { char * ret_val; char * find; ret_val = fgets(st, n, stdin); if (ret_val) { find = strchr(st, '\n'); // 查找换行符 if (find) // 如果地址不是NULL *find = '\0'; // 在此放置一个空字符 else while (getchar() != '\n') continue; } return ret_val; }C99的复合字面量特性可用于结构和数组。如果只需要一个临时结构值,复合字面量很好用。例如,可以使用复合字面量创建一个数组作为函数的参数或赋给另一个结构。
语法是把类型名放在括号中,后面紧跟一个用花括号括起来的初始化列表:
(struct book) {"The Idiot", "Fyodor Dostoyevsky", 6.99} #include<stdio.h> #define MAXTITL 41 #define MAXAUTL 31 struct book { char title[MAXTITL]; char author[MAXAUTL]; float value; }; int main(void) { struct book readfirst; int score; printf("Enter test score"); scanf("%d", &score); if (score >= 84) { readfirst = (struct book) { "Crime and Punishment", "Fyodor Dostoyevsky", 11.25 }; } else { readfirst = (struct book) { "Mr.Bouncy's Nic Hat", "Fred Winsome", 5.99 }; } return 0; } // 复合字面量作为函数参数传递 struct rect { double x; double y; }; double area; area = rect_area((struct rect){10.5, 20.0i}); double rect_area(struct rect r) { return r.x * r.y; } struct rect { double x; double y; }; double area; area = rect_area(&(struct rect){10.5, 20.0}); double rect_area(struct rect * rp) { return rp->x * rp->y; }C99新增了一个特性:伸缩型数组成员。伸缩型数组成员有两个特性:
该数组不会立即存在
使用这个伸缩型数组成员可以编写合适的代码,好像它确实存在并具有所需数目的元素一样
声明伸缩型数组成员有如下规则:
伸缩型数组成员必须是结构的最后一个成员
结构中必须至少有一个成员
伸缩数组的声明类似于普通数组,只是它的方括号中是空的
声明了伸缩型数组成员的结构在使用上有一定要求:
相同的两个结构不能进行赋值: //只能拷贝除伸缩型数组成员外的其他成员 // 确实要进行拷贝,应使用memcpy()函数 struct flex *pf1, *pf2; *pf2 = *pf1;不要以按值方式把这种结构传递给结构
不要使用带伸缩型数组成员的结构作为数组成员或另一个结构的成员
#include<stdio.h> #include<stdlib.h> struct flex { size_t count; double average; double scores[]; // 伸缩型数组成员 }; void showFlex(const struct flex * p); int main(void) { struct flex *pf1, *pf2; int n = 5; int i; int tot = 0; pf1 = malloc(sizeof(struct flex) + n * sizeof(double)); pf1->count = n; for (i = 0; i < n; i++) { pf1->scores[i] = 20.0 - i; // 伸缩型数组成员可以像普通数组一样使用 tot += pf1->socres[i]; } pf1->average = tot / n; showFlex(pf1); n = 9; tot = 0; pf2 = malloc(sizeof(struct flex) + n * sizeof(double)); pf2->count = n; for (i = 0; i < n; i++) { pf2->scores[i] = 20.0 - i / 2.0; tot += pf2->socres[i]; } pf2->average = tot / n; showFlex(pf2); free(pf1); free(pf2); return 0; } void showFlex(const struct flex * p) { int i; for (i = 0; i < p->count; i++) printf("%g", p->scores[i]); printf("\nAverage:%g\n", p->average); }C11才可以使用匿名结构
struct names { char first[20]; char last[20]; }; struct person { int id; struct names name; }; struct person ted = { 8483, {"Ted", "Grass"} }; 访问:ted.names.first // 匿名结构 struct person { int id; struct { char first[20]; char last[20]; }; }; 访问:ted.first联合是一种数据类型,它能在同一个内存空间中存储不同的数据类型(不是同时存储)。其典型用法是,设计一种表以存储既无规律、事先也不知道顺序的混合类型。使用联合类型的数组,其中的联合都大小相等,每个联合可以存储各种数据类型。
联合的使用与结构相同,但要注意联合当前使用的类型
// 声明了三个成员,但最终只能存储其中一个 // 编译器分配空间以最大的为主,例如double最大有8个字节 union hold { int digit; double bigf1; char letter; } union hold fit; // 联合变量,分配8个字节内存空间 union hold save[10]; // 10个元素,每个元素8个字节 union hold * pu; // 联合初始化 union hold valA; valA.letter = 'R'; union hold valB = valA; // 用另一个联合初始化 union hold valC = {88}; // 初始化联合digit成员 union hold valD = {.bigf1 = 118.2}; // 指定初始化成员 fit.letter = 'A'; num = 3.02 * fit.digit; // 错误,因为当前存储的是char类型联合的另一种用法是,在结构中存储与成员有从属关系的信息。
例如,假设用一个结构表示一辆汽车,如果汽车属于驾驶者,就要用一个结构成员来描述这个所有者;如果汽车被租赁,那么需要一个成员来描述其租赁公司。
struct ower { char socsecurity[12]; }; struct leasecompay { char name[40]; char headquarters[40]; }; union data { struct ower owncar; struct leasecompany leasecar; }; struct car_data { char make[15]; int status; // 私有为0,租赁为1 union data ownerinfo; };假设 flits 是 car_data 类型,如果 flits.status=0,程序将使用 flits.ownerinfo.owncar.socsecurity,如果 flits.status=1,使用flits.ownerinfo.leasecar.name
C11提供了匿名联合
struct ower { char socsecurity[12]; }; struct leasecompay { char name[40]; char headquarters[40]; }; struct car_data { char make[15]; int status; union { struct owner owncar; struct leasecompany leasecar; }; }; 访问: struct car_data flits; flits.owncar.socsecurity; flits.leasecar.name;可以用枚举类型声明符号名称来表示整型常量。使用 enum 关键字(实际上,enum常量是int类型)。
// 枚举spectrum // 默认情况下,枚举列表中的常量都被赋予0、1、2等值 // red=0,orange=1,yellow=3,.... enum spectrum { red, orange, yellow, green, blue, violet }; enum spectrum color; // 声明枚举变量color,color的值是spectrum定义的值 // 可以直接使用枚举值判断和赋值 int c; color = blue; if (color == yellow) ...; for (color = red; color <= violet; color++) ...; // 为枚举定义具体值 // 如果枚举的值有部分被赋值,那么后面的常量了会被赋予后续的值 enum levels { low = 100, medium = 500, high = 2000 }; #include<stdio.h> #include<string.h> #include<stdbool.h> #define LEN 30 char * s_gets(char * st, int n); enum spectrum { red, orange, yellow, green, blue, violet }; const char * colors[] = {"red", "orange", "yellow", "green", "blue", "violet"}; int main(void) { char choice[LEN]; enum spectrum color; bool color_is_found = false; puts("Enter a color(empty line to quit)"); while (s_gets(choice, LEN) != NULL && choice[0] != '\0') { for (color = red; color <= violet; color++) { if (strcmp(choice, colors[color] == 0) { color_is_found = true; break; } } if (color_is_found) { switch(color) { case red: puts("Roase are red"); break; case orange: puts("Poppies are orange"); break; .... } } } return 0; }利用 typedef 可以为某一类型自定义名称,与 #define 有3处不同:
与#define不同,typedef创建的符号名只受限于类型,不能用于值
typedef由编译器解释,不是预处理器
在其受限范围内,typedef比#define更灵活
// BYTE定义为unsigned char类型 typedef unsinged char BYTE; // 变量都是unsigned char类型 BYTE x, y[10], *z; // 字符指针使用STRING表示 typedef char * STRING; STRING name, sign; // 结构struct complex使用COMPLEX表示 typedef struct complex { float real; float imag; } COMPLEX;