设计程序时,最重要的是去选择表示数据的方式。C的简单变量,数组和指针已经学过了,但是很多任务中数组,指针也捉襟见肘。结构是一种数据组织形式,算是一种可由多种数据类型组合的复合新类型吧,有点像python的字典变量,也像pascal的记录(record)结构会让你觉得你在创建新类型。
当然啦,还有很多更有用的数据形式,比如队列,二叉树,堆,哈希表,图,这些数据形式都是由链式结构组成的,用指针把一个结构和另一个结构链接起来,所以学习结构也是在为学习更高级的数据组织形式打下基础,结构的重要用途之一就是创建新的数据形式。
之前说数组只能存储同类型的变量,现在结构就像是一个超级数组,特殊数组,它长度也许不是很大,但是每个元素可以是不同的数据类型。
正因为结构可以储存不同类型的值,使他成为构建数据库的好工具。
文章目录
结构声明,初始化器
示例
#include <stdio.h>
#include <string.h>
#define MAXTITL 41
#define MAXAUTL 31
char *s_gets(char *st, int n);
struct book{ //结构模板,book是结构标记,结构模板只是告诉编译器存储数据的形式,并未分配内存空间;结构模板和C++的模板没有关系
char title[MAXTITL];
char author[MAXAUTL];
float value;
};
int main(void)
{
struct book library; //struct book像是一个新类型,创建了一个结构变量,编译器根据book模板为library分配空间
printf("Enter the book title:\n");
s_gets(library.title, MAXTITL);//title是成员member,或者字段field
printf("Enter the author:\n");
s_gets(library.author, MAXAUTL);
printf("Enter the value:\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, *find;
ret_val = fgets(st, n, stdin);
if(ret_val)
{
//如果不是空指针,就把fgets获取的字符串处理一下:
//如果有换行符则替换为空字符;否则把输入缓冲区的字符读取出来
find = strchr(st, '\n');
if(find)
*find = '\0';
else
while(getchar()!='\n')
;
}
return ret_val;
}
Enter the book title:
Little Prince
Enter the author:
Antoine
Enter the value:
30.99
Little Prince by Antoine: $30.99
Antoine: "Little Prince" ($30.99)
Done!
其中下面这部分代码是结构声明(structure declaration)只是创建一个格式,样式,描述了结构的组织布局
这个结构声明(也称为模板,或者结构布局)描述了一个由两个字符数组和一个float变量组成的结构。这种声明不会创建实际的数据对象。
struct关键字表明后面的是一个结构,struct后面的标记是可选的,可以通过标记去引用结构。
花括号括起来的是结构成员列表,列表里是成员自己的声明,成员可以是任意的数据类型,甚至可以是另一个结构。
struct book{ //结构模板,book是结构标记,结构模板只是告诉编译器存储数据的形式,并未分配内存空间;结构模板和C++的模板没有关系
char title[MAXTITL];
char author[MAXAUTL];
float value;
};//此分号说明结构布局定义结束
实际上,下面这句声明是一个简化版本,对计算机而言都是第二种
struct book library;
//完整版
struct book {
char title[MAXTITL];
char author[MAXAUTL];
float value;
} library;
//无结构标记版本,如果想多次使用这个结构布局,就不能用无标记版本
struct {
char title[MAXTITL];
char author[MAXAUTL];
float value;
} library;
初始化静态变量/静态结构必须使用常量表达式
不要忘记赋值符号!每个成员单独一行,提高可读性
struct book library = {
"Little Prince",
"Antoine",
23.24
};
//可以按照任意顺序初始化
struct book library = {
.title = "Little Prince",
.value = 89.23,
.author = "Antoine"
};
struct book library = {
.author = "Antoine",
89.23,//由于89.23紧跟author成员后面,编译器会把它初始化为value成员的值
.title = "Little Prince"
};
结构数组
看来太大的数组或者结构数组最好不要用自动存储类别,而是用静态类别,否则栈不够大,引发程序错误。
示例 结构数组
#include <stdio.h>
#include <string.h>
char *s_gets(char *st, int n);
#define MAXTITL 40
#define MAXAUTL 40
#define MAXBKS 100
struct book{
char title[MAXTITL];
char author[MAXAUTL];
float value;
};
int main()
{
struct book library[MAXBKS];//声明结构数组,每个元素都是一个结构变量
int count = 0;
int index;
printf("Enter the book title.\n");
printf("Press [enter] at the start of a line to stop.\n");
//用while循环读取多个项
while(count < MAXBKS && s_gets(library[count].title, MAXTITL)!=NULL && library[count].title[0]!='\0')
{
printf("Enter the author.\n");
s_gets(library[count].author, MAXAUTL);
printf("Enter the value.\n");
scanf("%f", &library[count++].value);
while(getchar()!= '\n')
;
if(count < MAXBKS)
printf("Enter the next title.\n");
}
if(count > 0)
{
printf("Here is the list of your books:\n");
for(index = 0;index<count;index++)
printf("%s by %s: $%.2f\n", library[index].title, library[index].author, library[index].value);
}
else
printf("No books? Too bad.\n");
return 0;
}
char *s_gets(char *st, int n)
{
char *ret_val, *find;
ret_val = fgets(st, n, stdin);
if(ret_val)
{
//如果不是空指针,就把fgets获取的字符串处理一下:
//如果有换行符则替换为空字符;否则把输入缓冲区的字符读取出来
find = strchr(st, '\n');
if(find)
*find = '\0';
else
while(getchar()!='\n')
;
}
return ret_val;
}
Enter the book title.
Press [enter] at the start of a line to stop.
Little Prince
Enter the author.
Antoine
Enter the value.
10.12
Enter the next title.
My Life as a Budgie
Enter the author.
Mack Zackles
Enter the value.
23.34
Enter the next title.
Here is the list of your books:
Little Prince by Antoine: $10.12
My Life as a Budgie by Mack Zackles: $23.34
示例2 传递结构数组名为参数
结构数组名是第一个结构的地址,本程序中则是jones[0]的地址
养成习惯,不改变数组的值就加个const
#include <stdio.h>
#define FUNDLEN 50
#define N 2
struct funds{
char bank[FUNDLEN];
double bankfund;
char save[FUNDLEN];
double savefund;
};
double sum(const struct funds money[], int n);
int main()
{
struct funds jones[N] = {
{"Garlic-Melon Bank",
3487.23,
"Lucky's Savings and Loan",
3452.78},
{"Honest Jack's Bank",
3452.90,
"Party Time Savings",
3421.12}
};
printf("The Joneses have a total of $%.2f.\n", sum(jones, N));//传递了结构数组名作为参数,即地址,sum(jones, N)等同于sum(&jones[0], N)
return 0;
}
//指针money的初始值相当于由money = &jones[0]获得
double sum(const struct funds money[], int n)
{
double total;
int i;
for(i = 0, total = 0;i < n;i++)
total += money[i].bankfund + money[i].savefund;//money[i]是第i个结构
return total;
}
The Joneses have a total of $13814.03.
嵌套结构
#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",
56329.00
};
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)
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;
}
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
匿名结构(匿名特性在嵌套联合中更有用)
匿名结构是嵌套结构中的内层结构成员,是成员哦。
如:
//非匿名
struct names {
char first[20];
char last[20];
};
struct person {
int id;
struct names name;//嵌套结构中的内层结构成员,但这里不是匿名
};
//声明
struct person ted = {123, {"Ted", "Grass"}};
//访问
ted.id;
ted.name.first;
//匿名
struct person{
int id;
struct {char first[20], char last[20]};//嵌套结构中的内层结构成员,匿名
};
//声明
struct person ted = {123, {"Ted", "Grass"}};//匿不匿名声明都一样,只是访问不一样
//访问
ted.id;
ted.first;
指向结构的指针
示例 成员运算符优先级高于解引用运算符
#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 fellow[2] = {
{
{"Ewen", "Villard"},
"grilled salmon",
"personality coach",
34876.00
},
{
{"Rodney", "Swillbelly"},
"tripe",
"tabloid editor",
43448.00
}
};
struct guy *him;//指向结构的指针
printf("address #1: %p #2: %p\n", &fellow[0], &fellow[1]);
him = &fellow[0];//him指向fellow的第一个结构元素
printf("pointer #1: %p #2: %p\n", him, him + 1);//him+1指向第二个结构元素
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;
}
可以看到,结构数组的第一个结构元素占了84字节,(c8-74=54,十六进制的54即十进制的84,因为54进位了5次,逢15进位,而十进制是逢九进位,所以每次进位少算了6,共有5次进位,所以少算了30,所以十进制是84)。当然这是因为结构数组的第一个元素占40字节,第二个元素20,第三个元素20,第四个元素4字节。
address #1: 0061fe74 #2: 0061fec8
pointer #1: 0061fe74 #2: 0061fec8
him->income is $34876.00: (*him).income is $34876.00
him->favfood is tripe: him->handle.last is Swillbelly
用指针访问结构的成员时要用->运算符
把结构作为函数参数
传递结构的成员
最简单的。因为结构成员一般是简单数据类型。就和之前的函数传参没啥区别。
#include <stdio.h>
#define FUNDLEN 50
struct funds{
char bank[FUNDLEN];
double bankfund;
char save[FUNDLEN];
double savefund;
} ;
double sum(double, double);
int main()
{
struct funds stan = {
"Garlic-Melon Bank",
4036.43,
"Lucky's Savings and Loan",
6789.56
};
printf("Stan has a total of $%.2f.\n", sum(stan.bankfund, stan.savefund));
return 0;
}
double sum(double x, double y)
{
return (x+y);
}
Stan has a total of $10825.99.
传递指向结构的指针
#include <stdio.h>
#define FUNDLEN 50
struct funds{
char bank[FUNDLEN];
double bankfund;
char save[FUNDLEN];
double savefund;
} ;
double sum(const struct funds *);//参数是指向结构的指针,因为不希望通过指针修改结构的值,所以声明const指针
int main()
{
struct funds stan = {
"Garlic-Melon Bank",
4036.43,
"Lucky's Savings and Loan",
6789.56
};
printf("Stan has a total of $%.2f.\n", sum(&stan));
return 0;
}
double sum(const struct funds *money)
{
return(money->bankfund + money->savefund);
}
Stan has a total of $10825.99.
示例 用指针实现函数间的通信
#include <string.h>
#include <stdio.h>
#define NLEN 30
struct namect {
char fname[NLEN];
char lname[NLEN];
int letters;
};
void getinfo(struct namect*);
void makeinfo(struct namect*);
void showinfo(const struct namect*);
char *s_gets(char *st, int n);
int main()
{
struct namect person;
getinfo(&person);
makeinfo(&person);
showinfo(&person);
return 0;
}
void getinfo(struct namect *pst)
{
printf("Enter your first name:\n");
s_gets(pst->fname, NLEN);
printf("Enter your last name:\n");
s_gets((*pst).lname, NLEN);
}
void makeinfo(struct namect *pst)
{
pst->letters = strlen(pst->fname) + strlen(pst->lname);
}
void showinfo(const struct namect *pst)
{
printf("%s %s, your name contains %d letters.\n", pst->fname, pst->lname, pst->letters);
}
char *s_gets(char *st, int n)
{
char *ret_val, *find;
ret_val = fgets(st, n, stdin);
if(ret_val)
{
find = strchr(st, '\n');
if(find)
*find = '\0';
else
while(getchar()!='\n')
;
}
return ret_val;
}
Enter your first name:
Mary
Enter your last name:
Green
Mary Green, your name contains 9 letters.
传递结构本身
这也很简单,直接传过去,用成员运算符访问就行
#include <stdio.h>
#define FUNDLEN 50
struct funds{
char bank[FUNDLEN];
double bankfund;
char save[FUNDLEN];
double savefund;
} ;
double sum(struct funds);//参数是结构体
int main()
{
struct funds stan = {
"Garlic-Melon Bank",
4036.43,
"Lucky's Savings and Loan",
6789.56
};
printf("Stan has a total of $%.2f.\n", sum(stan));
return 0;
}
double sum(struct funds money)
{
return(money.bankfund + money.savefund);
}
Stan has a total of $10825.99.
示例 返回和传递结构本身实现函数间的通信
这个程序实现的功能和上面的那个题目一样
但是这个实现中,由于传递结构返回结构而不是指向结构的指针,所以每个函数都需要为这个结构创建一个副本,每个函数内部实际上使用的是不同的结构,但是上面用指针方式则一直用的一个结构,没有任何副本。
结构中存的是字符串的指针,并不是字符串本身。
这很像以前处理字符串。传递和返回字符串需要创建数组副本,但是指针却操作原始字符串。并且用指针只需要传一个地址,不用传整个结构体,毕竟结构体可能很大,需要很大的栈内存来存储。
但是指针的缺点是无法保护原始数据,和之前用指针处理字符串还是数组处理字符串的优缺点是一样一样的。但是传递和返回结构的话,处理的是副本,原始数据就不会被破坏。
#include <string.h>
#include <stdio.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(const struct namect);
char *s_gets(char *st, int n);
int main()
{
struct namect person;
person = getinfo();
person = makeinfo(person);
showinfo(person);
return 0;
}
struct namect getinfo(void)
{
struct namect temp;
printf("Enter your first name:\n");
s_gets(temp.fname, NLEN);
printf("Enter your last name:\n");
s_gets(temp.lname, NLEN);
return temp;
}
struct namect makeinfo(struct namect temp)
{
temp.letters = strlen(temp.fname) + strlen(temp.lname);
return temp;
}
void showinfo(const struct namect stru)
{
printf("%s %s, your name contains %d letters.\n", stru.fname,stru.lname, stru.letters);
}
char *s_gets(char *st, int n)
{
char *ret_val, *find;
ret_val = fgets(st, n, stdin);
if(ret_val)
{
find = strchr(st, '\n');
if(find)
*find = '\0';
else
while(getchar()!='\n')
;
}
return ret_val;
}
Enter your first name:
Mary
Enter your last name:
Galler
Mary Galler, your name contains 10 letters.
总之,到底选择传指针还是传结构还是传成员,要看你的需求,要效率就指针,但要用const指针;要方便易懂且自己的结构也不大,就直接传结构;如果结构太大,又只用一两个成员,就传成员。
结构中的字符串:用char数组胜于用char 指针
这里说胜于,只是因为用char指针表示字符串在结构中非常容易出错,下面是一个例子,非常危险
示例
#define LEN 20
#include <stdio.h>
struct names{
char first[LEN];
char last[LEN];
};
struct pnames{
char *first;
char *last;
};
int main()
{
struct names veep = {"Talia", "Summers"};
struct pnames treas = {"Brad", "Fallingjaw"};
printf("%s and %s\n",veep.first, treas.first);
return 0;
}
我的编译器没有警告,也没有报错,输出了正确结果。
pnames结构的成员用指针表示,所以程序只需要存储两个指针,则只需要8字节;而数组表示法的names结构中的两个成员一共需要40字节。
但是实际上程序中有一个很隐蔽的问题:声明pnames结构模板时没有初始化指针,那么在主程序中直接初始化struct pnames 类型的treas时,first和last指针没有初始化,可能指向任何地方,主程序中给treas变量初始化时,不知道把"Brad"和"Fallingjaw"存到哪里去了。这可能会导致很严重的问题。
Talia and Brad
这个问题的解决方法是:使用malloc函数提前分配内存,再给char指针变量赋值。
#define SLEN 20
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct pnames{
char *first;
char *last;
};
char *s_gets(char *st, int n);
void getinfo(struct pnames *);
void cleanup(struct pnames *);
int main()
{
struct pnames treas;
getinfo(&treas);
printf("%s and %s\n",treas.first, treas.last);
cleanup(&treas);
return 0;
}
//先分配地址以初始化char指针,就规避了上面说的风险
void getinfo(struct pnames *pst)
{
char temp[SLEN];
printf("Enter your first name.\n");
s_gets(temp, SLEN);
//用malloc()分配内存
pst->first = (char *)malloc(strlen(temp)+1);
//把名字复制到malloc分配的内存
strcpy(pst->first, temp);
printf("Enter your last name.\n");
s_gets(temp, SLEN);
pst->last = (char *)malloc(strlen(temp)+1);
strcpy(pst->last, temp);
}
char *s_gets(char *st, int n)
{
char *ret_val, *find;
ret_val = fgets(st, n, stdin);
if(ret_val)
{
find = strchr(st, '\n');
if(find)
*find = '\0';
else
while(getchar()!='\n')
;
}
return ret_val;
}
void cleanup(struct pnames *pst)
{
free(pst->first);
free(pst->last);
}
Enter your first name.
Rachel
Enter your last name.
Green
Rachel and Green
复合字面量
#include <stdio.h>
#define MAXTITL 41
#define MAXAUTL 31
struct book{
char title[MAXTITL];
char author[MAXAUTL];
float value;
};
int main()
{
struct book readfirst;
int score;
printf("Enter test score:\n");
scanf("%d", &score);
if(score >= 84)
//复合字面量
readfirst = (struct book) {"Crime and Punishment",
"Fyodor Dostoyevsky",
11.24};
else
readfirst = (struct book){"Mr.Bouncy's Nice Hat",
"Fred Winsome",
3.78
};
printf("Your assigned reading:\n");
printf("%s by %s: $%.2f\n", readfirst.title, readfirst.author, readfirst.value);
return 0;
}
Enter test score:
99
Your assigned reading:
Crime and Punishment by Fyodor Dostoyevsky: $11.24
伸缩型数组成员 (必须使用指向结构的指针)
这个成员不会被编译器分配空间,必须用malloc分配,所以也是运行时分配的空间。
为什么叫伸缩型?因为你想让他的长度是多少就是多少,比如下面的程序,设置数组长度为5或者9.
限制很多,基本上使用带有伸缩数组的结构就要多用指针;带伸缩结构的结构还不能作为数组成员
#include <stdio.h>
#include <stdlib.h>
struct flex{
size_t count;
double average;
double scores[];//伸缩型数组成员
};
void showFlex(const struct flex *p);
int main()
{
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->scores[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->scores[i];
}
pf2->average = tot / n;
showFlex(pf2);
//很容易忘记!
free(pf1);
free(pf2);
return 0;
}
void showFlex(const struct flex *p)
{
int i;
printf("Scores:\n");
for(i=0;i<p->count;i++)
printf("%.2f ", p->scores[i]);
printf("\nAverage: %.2f\n", p->average);
}
Scores:
20.00 19.00 18.00 17.00 16.00
Average: 18.00
Scores:
20.00 19.50 19.00 18.50 18.00 17.50 17.00 16.50 16.00
Average: 17.00
不明觉厉,不明
把结构存到.dat文件中(fread() fwrite())
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXTITL 40
#define MAXAUTL 40
#define MAXBKS 10
char *s_gets(char *st, int n);
struct book{//book模板
char title[MAXTITL];
char author[MAXAUTL];
float value;
};
int main()
{
struct book library[MAXBKS];
int count = 0;
int index, filecount;
FILE *pbooks;
int size = sizeof(struct book);//隐式把size_t转换为int类型
if((pbooks = fopen("book.dat", "a+b"))==NULL)
{
fputs("Can't open book.dat file\n", stderr);
exit(1);
}
rewind(pbooks);//确保指针位于文件开始处
while(count<MAXBKS && fread(&library[count], size, 1, pbooks)==1)
{
if(count==0)
puts("Current contents of book.dat:");//这句话只输出一次
printf("%s by %s: $%.2f\n", library[count].title, library[count].author, library[count].value);
count++;
}
filecount = count;
if(count == MAXBKS)
{
fputs("The book.dat file is full.", stderr);
exit(2);
}
puts("Please add new book titles.");
puts("Press [enter] at the start of a line to stop.");
while(count < MAXBKS && s_gets(library[count].title, MAXTITL)!=NULL && library[count].title[0]!='\0')//不要写成&library[count].title
{
printf("Enter the author:\n");
s_gets(library[count].author, MAXAUTL);//不要写成&library[count].author
printf("Enter the value:\n");
scanf("%f", &library[count].value);
//我老忘记读取输入缓冲区了
while(getchar()!='\n')
;
if(count < MAXBKS)
puts("Enter the next title.");
count++;
}
//输出总的条目数
if(count > 0)
{
puts("Here is the list of your books:");
for(index = 0;index < count; index++)
printf("%s by %s: $%.2f\n", library[index].title, library[index].author, library[index].value);
fwrite(&library[filecount], size, count - filecount, pbooks);//&library[filecount]是开始写向文件的起始位置
}
else
puts("No books? Too bad.");
return 0;
}
char *s_gets(char *st, int n)
{
char *ret_val, *find;
ret_val = fgets(st, n, stdin);
if(ret_val)
{
//如果不是空指针,就把fgets获取的字符串处理一下:
//如果有换行符则替换为空字符;否则把输入缓冲区的字符读取出来
find = strchr(st, '\n');
if(find)
*find = '\0';
else
while(getchar()!='\n')
;
}
return ret_val;
}
Please add new book titles.
Press [enter] at the start of a line to stop.
AA
Enter the author:
aa
Enter the value:
1
Enter the next title.
BB
Enter the author:
bb
Enter the value:
2
Enter the next title.
CC
Enter the author:
cc
Enter the value:
3
Enter the next title.
Here is the list of your books:
AA by aa: $1.00
BB by bb: $2.00
CC by cc: $3.00
记事本打开是乱码
b是ANSI的标识,表明程序使用二进制文件格式。
第二次运次,则可以从book.dat文件读取之前写进去的内容
Current contents of book.dat:
AA by aa: $1.00
BB by bb: $2.00
CC by cc: $3.00
Please add new book titles.
Press [enter] at the start of a line to stop.
DD
Enter the author:
dd
Enter the value:
4
Enter the next title.
Here is the list of your books:
AA by aa: $1.00
BB by bb: $2.00
CC by cc: $3.00
DD by dd: $4.00
链式结构(二叉树 队列 堆 哈希表)
这是数据结构里的内容。这些数据结构都可以用C结构实现。
二叉树(非常便于查找)
联合 union
这一章学了三个数据类型,即结构,联合,枚举类型。
联合和结构很像,所以放在结构说完之后立刻来说。联合也是可以存储多种不同数据类型的值,但不同的是:同一时间只能存一个数据类型的值!!这是联合的最本质的特点。
创建联合
和创建结构一样,都需要一个模板。
//带标记的联合模板,这个联合可以存储一个int类型的值,或者存一个double类型的值或一个字符
union hold{
int digit;
double big;
char letter;
};
union hold fit;//声明一个单独的union hold类型的变量,编译器分配足够的空间便于存储联合声明中占用最大字节的类型
union hold save[10];//声明union hold类型的数组,每个元素都是8字节,所以一共占80字节
union hold *pu;//声明指向union hold类型的指针,存储联合变量的地址
初始化
union hold valA;
valA.letter = 'R';
union hold valB = valA;//用一个结构初始化另一个结构
union hold valC = {88};//初始化的是第一个,digit变量
union hold valD = {.big = 11.2};//这种叫做指定初始化器,前面接触过的
联合的赋值
//联合也使用成员运算符
valA.digit = 23;//存储int类型的23,用4字节
valA.letter = 'B';//清除int的23,存储char类型的B,用1字节
valA.big = 67.9;//清除char,存储double,用8字节
总之。联合一次只能存一个值。 就算空间够同时存char和int,也不行。
用指针访问联合 (间接成员运算符->)
pu = &valA;
x = pu->letter;//等效于valA.letter
struct {
int code;
float cost;
}item, *ptrst;
ptrst = &item;
//三种方式等效
ptrst->code
(*ptrst).code
item.code
把联合作为结构成员
有时候结构的成员需要不同的类型,比如这里,假设用于描述一辆车,用一个结构去描述车的各项信息,包括制造公司和所有权,如果是私有的则所有权要用一个结构描述车主的信息,如果是租赁的车则需要一个结构来描述租赁公司的信息,这两种结构不会同时出现,因为车要么是自己的要么是租来的。所以用一个联合来描述所有权信息是很合适的。
struct owner{
char socsecurity[12];
};
struct leasecompany{
char name[40];
char headquarters[40];
};
union data{
struct owner owncar;
struct leasecompany leasecar;
};
struct car_data{
char make[15];
int status;//私有为0,租赁为1
union data ownerinfo;
};
如果a是car_data类型的结构变量,如果a.status是0,则程序应该使用a.ownerinfo.owncar.socsecurity,如果a.status是1,则使用a.ownerinfo.leasecar.name
匿名联合(结构或联合的匿名成员)
struct owner{
char socsecurity[12];
};
struct leasecompany{
char name[40];
char headquarters[40];
};
struct car_data{
char make[15];
int status;//私有为0,租赁为1
union {//匿名联合
struct owner owncar;
struct leasecompany leasecar;
};
};
访问则用a.owncar.socsecurity 或a.leasecar.
枚举 enumerated type
枚举类型实际上是整型,用来声明符号名称以表示整型常量,这些符号常量被称为枚举符enumerator。
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
char *s_gets(char *st, int n);
enum spectrum{red, orange, yellow, green, blue, violet};
const char *colors[] = {"red", "orange", "yellow", "green", "blue", "violet"};
#define LEN 30
int main()
{
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(colors[color], choice)==0)
{color_is_found = true;
break;}
}
if(color_is_found)
switch(color)
{
case red: puts("Roses are red.");break;
case orange: puts("Poppies are orange.");break;
case yellow: puts("Sunflowers are yellow.");break;
case green: puts("Grass is green.");break;
case blue: puts("Bluebells are blue.");break;
case violet: puts("Violets are violet.");break;
}
else
printf("I don't know about the color %s.\n", choice);
color_is_found = false;
puts("Next color, please (empty line to quit):");
}
puts("Goodbye!");
return 0;
}
char *s_gets(char *st, int n)
{
char *ret_val, *find;
ret_val = fgets(st, n, stdin);
if(ret_val)
{
//如果不是空指针,就把fgets获取的字符串处理一下:
//如果有换行符则替换为空字符;否则把输入缓冲区的字符读取出来
find = strchr(st, '\n');
if(find)
*find = '\0';
else
while(getchar()!='\n')
;
}
return ret_val;
}
Enter a color (empty line to quit):
yellow
Sunflowers are yellow.
Next color, please (empty line to quit):
pink
I don't know about the color pink.
Next color, please (empty line to quit):
blue
Bluebells are blue.
Next color, please (empty line to quit):
Goodbye!
名称空间 namespace
c++也用名称空间,原来也是从C来的。
相同作用域中,标记和变量名是可以相同的!!!但是不可以有两个同名的标记或同名变量。
但是尽量别这么做,因为C++不支持,C++把标记和变量名放在一个名称空间,C却放在不同的名称空间
typedef(为类型自定义名称 高级特性)
其实主要用途是为复杂的类型自定义一个简单的名称
和#define的区别
typedef定义结构体类型
看几个比较复杂的声明(* [] ()的优先级)
[]和()的优先级相同,都是从左向右结合,高于*
晕了,这一点真的很难诶,所以typedef也不是那么简单的