1. 先看个例子
#include <stdio.h> #include <string.h> #define MAXTITL 41 #define MAXAUTL 31 struct book { /*结构模板,标记是 book */ char title[MAXTITL]; char author[MAXAUTL]; float value; }; char * s_gets(char *, int); int main() { struct book library; /* 把 library 声明为一个 book 类型的变量*/ printf("请输入书的标题:\n"); s_gets(library.title, MAXTITL); printf("现在输入书的作者姓名:\n"); s_gets(library.author, MAXAUTL); printf("现在输入书本的价格:\n"); scanf("%f", &library.value); printf("%s by %s: $%.2f\n", library.title, library.author, library.value); printf("%s: \"%s\"($%.2f)\n", library.author, library.title, library.value); printf("Done.\n"); return 0; } 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; } /* output: 请输入书的标题: 我与地坛 现在输入书的作者姓名: 史铁生 现在输入书本的价格: 20 我与地坛 by 史铁生: $20.00 史铁生: "我与地坛"($20.00) Done. */
结构变量:为了提高C语言表示数据的能力。
比如说描述一本书,我们会用一个char数组表示书名, 再用一个char数组表示作者,一个float表示书的描述,但是我们要描述很5本书的时候,我们就得用5个char数组分别表示5本书名,5个char数组表示五本书的作者,5个float表示五本书的价格。这样做很麻烦,而且不容易维护他们之间的关系。所以就有了结构。
结够有点像面向对象,但是只有属性,没有行为;
2. 建立结构声明,并声明一个结构变量
struct book { /*带标记定义结构,可重用*/ char title[MAXTITL]; char author[MAXAUTL]; float value; }; struct book library; struct { /*不带标记定义结构, 不可重用*/ char title[MAXTITL]; char author[MAXAUTL]; float value; } library; /*定义结构的同时,声明一个结构变量。*/
3. 结构的内存模型
4. 初始化结构变量
struct book library = { /*按顺序初始化*/ "我与地坛", "史铁生", 20.00 }; struct book library = { /*按成员名称初始化*/ .value = 20.00, .author = "史铁生", .title = "我与地坛" }; struct book library = { /*局部初始化*/ .value = 20.00 }; struct book library = { /*瞎j8初始化*/ .value = 20.00, .author = "史铁生", 5.00 }; //因为value紧跟author, 最终value = 5.00,
5. 访问结构的数据,用结构成员运算符 (. )
6. 声明结构数组
struct book library[5];
7.结构数组内存模型
8. 嵌套结构
#include <stdio.h> #define LEN 20 const char * msgs[5] = { " Thank you for the wonderful evening, ", "You certainly prove that a ", "is a special kind of guy. We must get together", "over a delicious ", " and have a few laughs" }; struct names{ char first[LEN]; char last[LEN]; }; struct guy{ struct names handle; char favfood[LEN]; char job[LEN]; float income; }; int main() { struct guy fellow = { {"Ewen", "Villard"}, "grilled salmon", "personality coach", 68112.0 }; printf("Dear %s, \n\n", fellow.handle.first); printf("%s%s.\n", msgs[0], fellow.handle.first); printf("%s%s\n", msgs[1], fellow.job); printf("%s\n", msgs[2]); printf("%s%s%s", msgs[3], fellow.favfood, msgs[4]); if (fellow.income > 150000.0) puts("!!"); else if (fellow.income > 75000.0) puts("!"); else puts("."); printf("\n%40s%s\n", " ", "See you soon,"); printf("%40s%s\n", " ", "Shalala"); return 0; } /* output: Dear Ewen, Thank you for the wonderful evening, Ewen. You certainly prove that a personality coach is a special kind of guy. We must get together over a delicious grilled salmon and have a few laughs. See you soon, Shalala */
9. 指向结构的指针
#include <stdio.h> #define LEN 20 struct names{ char first[LEN]; char last[LEN]; }; struct guy{ struct names handle; char favfood[LEN]; char job[LEN]; float income; }; int main() { struct guy fellows[2] = { { {"Ewen", "Villard"}, "grilled salmon", "personality coach", 68112.0 }, { {"Rondeny", "Swillbelly"}, "tripe", "tabloid editor", 432400.00 } }; struct guy * him; /* 声明一个指向结构的指针 */ printf("address #1: %p #2: %p\n", &fellows[0], &fellows[1]); him = &fellows[0]; /* 告诉编译器该指针指向何处 */ printf("him->income is $%.2f: (*him).income is $%.2f\n", him->income, (*him).income); him++; printf("him->favfood is %s: (*him).handle.last is %s\n", him->favfood, (*him).handle.last); return 0; } /* output: address #1: 000000000062FD90 #2: 000000000062FDE4 him->income is $68112.00: (*him).income is $68112.00 him->favfood is tripe: (*him).handle.last is Swillbelly */
10. 用指针访问结构成员
struct guy * him; //声明一个指向结构变量的指针 struct guy barney; him = &barney; //让指针指向结构变量barney him->income; //使用 -> 运算符访问结构的成员变量,即 barney.income (*him).income //将指针解引用,即barney.income
11. 向函数传递结构的信息
a. 传递结构成员
b. 传递结构地址
c. 传递结构
#include <stdio.h> #define FUNDLEN 50 struct funds { char bank[FUNDLEN]; double bankfund; char save[FUNDLEN]; double savefund; }; double sum1(double, double); double sum2(const struct funds *); double sum3(struct funds); int main() { struct funds stan = { "Garlic-Melon Bank", 4032.27, "Lucky's Savings and Loan", 8543.94 }; printf("Stan has a total if $%.3f.\n", sum1(stan.bankfund, stan.savefund)); //向函数传递结构成员 printf("Stan has a total if $%.3f.\n", sum2(&stan)); //向函数传递结构的地址 printf("Stan has a total if $%.3f.\n", sum3(stan)); //向函数传递结构 return 0; } double sum1(double x, double y) { return (x + y); } double sum2(const struct funds * money) { return (money->bankfund + money->savefund); } double sum3(struct funds moolah) { return (moolah.bankfund + moolah.savefund); } /* output: Stan has a total if $12576.210. Stan has a total if $12576.210. Stan has a total if $12576.210. -------------------------------- */
12. 结构中的字符数组和指针
假设有一个结构声明
#define LEN 20 struct names{ char first[LEN]; char last[LEN]; };
使用指向char的指针来代替字符数组
struct pnames{ char * first; char * last; };
这样做是可以的。但是会带来麻烦。考虑以下代码
struct names veep = {"Talia", "Summers}; struct pnames treas = {"Brad", "Fallingjaw"}; printf("%s and %s\n", veep.first, treas.first);
代码运行都没有问题,内存分配是怎么做的?
对于struct names 类型的结构变量 veep , 以上字符串都存储在结构内部,结构总共要分配40字节存储姓名。
然而对于 struct pnames类型结构变量 treas,以上字符串存储在编译器存储常量的地方,结构本身只存储了两个地址, 在我们的系统*占16字节(64位系统一个地址是8字节)。 struct pnames 结构不用为字符串分配任何存储空间。他使用的是存储在别处的字符串。
考虑以下代码
struct names accountant; struct pnames attorney; puts("Enter the last name of your accountant:"); scanf("%s", accountant.last); puts("Enter the last name of your attorney:"); scanf("%s", attorney.last); //潜在的危险
用户的输入存储到哪里去了?
对于 accountant, 它的姓被存储在names的last中。
对于 attorney,它的姓被存储在 attorney.last所指向的地址中。由于attorney未被初始化,所以attorney.last可能指向任何一个地址,所以这个操作可能会造成不想要的修改。
因此,如果要用结构存储字符串,用字符数组作为成员笔记简单。用指向char的指针也行,但是误用会导致严重的问题。