跳到主要内容

结构和其他数据结构

视频地址: p127

本章介绍以下内容:

  • 关键字:struct、union、typedef
  • 运算符:.、->
  • 什么是C结构,如何创建结构模板和结构变量
  • 如何访问结构的成员,如何编写处理结构的函数
  • 联合和指向函数的指针

示例问题:创建图书目录

//* book.c -- 一本书的图书目录 */
#include <stdio.h>
#include <string.h>
char *s_gets(char *st, int n);
#define MAXTITL 41 /* 书名的最大长度 + 1 */
#define MAXAUTL 31 /* 作者姓名的最大长度 + 1*/

struct book
{ /* 结构模版:标记是 book */
char title[MAXTITL];
char author[MAXAUTL];
float value;
}; /* 结构模版结束 */

int main(void)
{
struct book library; /* 把 library 声明为一个 book 类型的变量 */
printf("Please enter the book title.\n");
s_gets(library.title, MAXTITL); /* 访问title部分*/
printf("Now enter the author.\n");
s_gets(library.author, MAXAUTL);
printf("Now 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;
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;
}

建立结构声明

struct book
{ /* 结构模版:标记是 book */
char title[MAXTITL];
char author[MAXAUTL];
float value;
}; /* 结构模版结束 */

struct book library; /* 把 library 声明为一个 book 类型的变量 */

定义结构变量

struct book library;

编译器执行这行代码便创建了一个结构变量library。编译器使用book模板为该变量分配空间:一个内含MAXTITL个元素的char数组、一个内含MAXAUTL个元素的char数组和一个float类型的变量。这些存储空间都与一个名称library结合在一起

可以定义两个struct book类型的变量,或者甚至是指向structbook类型结构的指针:

struct book doyle, panshin, * ptbook;

初始化结构

struct book library = {
"The Pious Pirate and the Devious Damsel",
"Renee Vivotte",
1.95};

访问结构成员

(.)

结构的初始化器

指定初始化器(designated initializer):

struct book gift = {.value = 25.99,
.author = "James Broadfool",
.title = "Rue for the Toad"};

结构数组

/* manybook.c -- 包含多本书的图书目录 */
#include <stdio.h>
#include <string.h>
char *s_gets(char *st, int n);
#define MAXTITL 40
#define MAXAUTL 40
#define MAXBKS 100 /* 书籍的最大数量 */

struct book
{ /* 简历 book 模板 */
char title[MAXTITL];
char author[MAXAUTL];
float value;
};

int main(void)
{
struct book library[MAXBKS]; /* book 类型结构的数组 */
int count = 0;
int index;
printf("Please enter the book title.\n");
printf("Press [enter] at the start of a line to stop.\n");
while (count < MAXBKS && s_gets(library[count].title, MAXTITL) != NULL && library[count].title[0] != '\0')
{
printf("Now enter the author.\n");
s_gets(library[count].author, MAXAUTL);
printf("Now enter the value.\n");
scanf("%f", &library[count++].value);
while (getchar() != '\n')
continue; /* 清理输入行*/
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;
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;
}

嵌套结构

// friend.c -- 嵌套结构示例
#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
{
// 第1个结构
char first[LEN];
char last[LEN];
};
struct guy
{
// 第2个结构
struct names handle;
// 嵌套结构
char favfood[LEN];
char job[LEN];
float income;
};
int main(void)
{
struct guy fellow = {// 初始化一个结构变量
{"Ewen", "Villard"},
"grilled salmon",
"personality coach",
68112.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.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;
}

指向结构的指针

/* friends.c -- 使用指向结构的指针 */
#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(void)
{
struct guy fellow[2] = {
{{"Ewen", "Villard"},
"grilled salmon",
"personality coach",
68112.00},
{{"Rodney", "Swillbelly"},
"tripe",
"tabloid editor",
432400.00}};
struct guy *him; /* 这是一个指向结构的指针 */
printf("address #1: %p #2: %p\n", &fellow[0], &fellow[1]);
him = &fellow[0]; /* 告诉编译器该指针指向何处 */
printf("pointer #1: %p #2: %p\n", him, 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;
}
/*address #1: 0061FE64 #2: 0061FEB8
pointer #1: 0061FE64 #2: 0061FEB8
him->income is $68112.00: (*him).income is $68112.00
him->favfood is tripe: him->handle.last is Swillbelly*/

声明和初始化结构指针

struct guy * him;

用指针访问成员

第1种方法也是最常用的方法:使用->运算符。该运算符由一个连接号(-)后跟一个大于号(>)组成。我们有下面的关系:

如果him == &barney,那么him->income 即是 barney.income

如果him == &fellow[0],那么him->income 即是 fellow[0].income

第2种方法是,以这样的顺序指定结构成员的值:如果him == &fellow[0],那么*him == fellow[0],因为&和*是一对互逆运算符。因此,可以做以下替代:

fellow[0].income == (*him).income

向函数传递结构的信息

传递结构成员

/* funds1.c -- 把结构成员作为参数传递 */
#include <stdio.h>
#define FUNDLEN 50

struct funds
{
char bank[FUNDLEN];
double bankfund;
char save[FUNDLEN];
double savefund;
};

double sum(double, double);

int main(void)
{
struct funds stan = {
"Garlic-Melon Bank",
4032.27,
"Lucky's Savings and Loan",
8543.94};
printf("Stan has a total of $%.2f.\n", sum(stan.bankfund, stan.savefund));
return 0;
}

/* 两个double类型的数相加 */
double sum(double x, double y) // 传递的是副本
{
return (x + y);
}

传递结构的地址

/* funds2.c -- 传递指向结构的指针 */
#include <stdio.h>
#define FUNDLEN 50

struct funds
{
char bank[FUNDLEN];
double bankfund;
char save[FUNDLEN];
double savefund;
};

double sum(const struct funds *); /* 参数是一个指针 */

int main(void)
{
struct funds stan = {
"Garlic-Melon Bank",
4032.27,
"Lucky's Savings and Loan",
8543.94};
printf("Stan has a total of $%.2f.\n", sum(&stan));
return 0;
}
double sum(const struct funds *money)
{
return (money->bankfund + money->savefund);
}

传递结构

/* funds3.c -- 传递一个结构 */
#include <stdio.h>
#define FUNDLEN 50
struct funds
{
char bank[FUNDLEN];
double bankfund;
char save[FUNDLEN];
double savefund;
};
double sum(struct funds moolah); /* 参数是一个结构 */
int main(void)
{
struct funds stan = {
"Garlic-Melon Bank",
4032.27,
"Lucky's Savings and Loan",
8543.94};
printf("Stan has a total of $%.2f.\n", sum(stan));
return 0;
}
double sum(struct funds moolah)
{
return (moolah.bankfund + moolah.savefund);
}

其他结构特性

  • C允许把一个结构赋值给另一个结构
  • 还可以把一个结构初始化为相同类型的另一个结构
  • 把结构作为返回值的函数能把结构的信息从被调函数传回主调函数。结构指针也允许这种双向通信
/* names1.c -- 使用指向结构的指针 */
#include <stdio.h>
#include <string.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(void)
{
struct namect person;
getinfo(&person);
makeinfo(&person);
showinfo(&person);
return 0;
}

void getinfo(struct namect *pst)
{
printf("Please enter your first name.\n");
s_gets(pst->fname, NLEN);
printf("Please 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;
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;
}
/* names2.c -- 传递并返回结构 */
#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();
printf("%d ===\n", &person);
person = makeinfo(person);
printf("%d ===\n", &person);
showinfo(person);
return 0;
}

struct namect getinfo(void)
{
struct namect temp;
printf("Please enter your first name.\n");
s_gets(temp.fname, NLEN);
printf("Please enter your last name.\n");
s_gets(temp.lname, NLEN);
return temp;
}

struct namect makeinfo(struct namect info)
{
printf("%d === info\n", &info);
info.letters = strlen(info.fname) + strlen(info.lname);
return info;
}

void showinfo(struct namect info)
{
printf("%s %s, your name contains %d letters.\n", info.fname, info.lname, info.letters);
}

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;
}

结构和结构指针的选择

把指针作为参数有两个优点:无论是以前还是现在的C实现都能使用这种方法,而且执行起来很快,只需要传递一个地址。缺点是无法保护数据。新增的const限定符解决了这个问题。

把结构作为参数传递的优点是,函数处理的是原始数据的副本,这保护了原始数据。

传递结构的两个缺点是:较老版本的实现可能无法处理这样的代码,而且传递结构浪费时间和存储空间。尤其是把大型结构传递给函数,而它只使用结构中的一两个成员时特别浪费。这种情况下传递指针或只传递函数所需的成员更合理。

通常,程序员为了追求效率会使用结构指针作为函数参数,如需防止原始数据被意外修改,使用const限定符。按值传递结构是处理小型结构最常用的方法。

结构中的字符数和字符指针

#define LEN 20
struct names
{
char first[LEN];
char last[LEN];
};

其中的结构声明是否可以这样写:

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字节。尤其是,struct pnames结构不用为字符串分配任何存储空间。它使用的是储存在别处的字符串如,字符串常量或数组中的字符串)。

简而言之,在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),他的名储存在accountant结构变量的last成员中,该结构中有一个储存字符串的数组。对于律师(attorney),scanf()把字符串放到attorney.last表示的地址上。由于这是未经初始化的变量,地址可以是任何值,因此程序可以把名放在任何地方。如果走运的话,程序不会出问题,至少暂时不会出问题,否则这一操作会导致程序崩溃。实际上,如果程序能正常运行并不是好事,因为这意味着一个未被觉察的危险潜伏在程序中。

因此,如果要用结构储存字符串,用字符数组作为成员比较简单。用指向 char 的指针也行,但是误用会导致严重的问题。

结构、指针和malloc()

如果使用malloc()分配内存并使用指针储存该地址,那么在结构中使用指针处理字符串就比较合理。这种方法的优点是,可以请求malloc()为字符串分配合适的存储空间。

// names3.c -- 使用指针和 malloc()
#include <stdio.h>
#include <string.h> // 提供 strcpy()、strlen() 的原型
#include <stdlib.h> // 提供 malloc()、free() 的原型
#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 *); // 调用该函数时释放内存
char *s_gets(char *st, int n);

int main(void)
{
struct namect person;
getinfo(&person);
makeinfo(&person);
showinfo(&person);
cleanup(&person);
return 0;
}

void getinfo(struct namect *pst)
{
char temp[SLEN];
printf("Please enter your first name.\n");
s_gets(temp, SLEN);
// 分配内存以储存名
pst->fname = (char *)malloc(strlen(temp) + 1);
// 把名拷贝到动态分配的内存中
strcpy(pst->fname, temp);
printf("Please enter your last name.\n");
s_gets(temp, SLEN);
pst->lname = (char *)malloc(strlen(temp) + 1);
strcpy(pst->lname, temp);
}

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);
}

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)

伸缩类型数组成员(C99)

匿名结构(C11)

使用结构数组的函数

/* funds4.c -- 把结构数组传递给函数 */
#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(void)
{
struct funds jones[N] = {
{"Garlic-Melon Bank",
4032.27,
"Lucky's Savings and Loan",
8543.94},
{"Honest Jack's Bank",
3620.88,
"Party Time Savings",
3802.91}};
printf("The Joneses have a total of $%.2f.\n", sum(jones, N));
return 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;
return (total);
}
  • 可以把数组名作为数组中第1个结构的地址传递给函数。
  • 然后可以用数组表示法访问数组中的其他结构。注意下面的函数调用与使用数组名效果相同: sum(&jones[0], N)因为jones和&jones[0]的地址相同,使用数组名是传递结构地址的一种间接的方法。
  • 由于sum()函数不能改变原始数据,所以该函数使用了ANSI C的限定符const。

把结构保存到文件中

保存结构的程序示例

/* booksave.c -- 在文件中保存结构中的内容 */
#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(void)
{
struct book library[MAXBKS]; /* 结构数组 */
int count = 0;
int index, filecount;
FILE *pbooks;
int size = sizeof(struct book);
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')
{
puts("Now enter the author.");
s_gets(library[count].author, MAXAUTL);
puts("Now enter the value.");
scanf("%f", &library[count++].value);
while (getchar() != '\n')
continue; /* 清理输入行 */
if (count < MAXBKS)
puts("Enter the next title.");
}
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);
}
else
puts("No books? Too bad.\n");
puts("Bye.\n");
fclose(pbooks);
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;
}

链式结构

联合

联合(union)是一种数据类型,它能在同一个内存空间中储存不同的数据类型(不是同时储存)。其典型的用法是,设计一种表以储存既无规律、事先也不知道顺序的混合类型。使用联合类型的数组,其中的联合都大小相等,每个联合可以储存各种数据类型。

枚举类型

/* enum.c -- 使用枚举类型的值 */
#include <stdio.h>
#include <string.h> // 提供 strcmp()、strchr()函数的原型
#include <stdbool.h> // C99 特性
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(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("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;
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;
}

typedef 简介

typedef工具是一个高级数据特性,利用typedef可以为某一类型自定义名称。这方面与#define类似,但是两者有3处不同:

  • 与#define不同,typedef创建的符号名只受限于类型,不能用于值。
  • typedef由编译器解释,不是预处理器。
  • 在其受限范围内,typedef比#define更灵活。

其他复杂的声明

函数和指针

// func_ptr.c -- 使用函数指针
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define LEN 81
char *s_gets(char *st, int n);
char showmenu(void);
void eatline(void); // 读取至行末尾
void show(void (*fp)(char *), char *str);
void ToUpper(char *); // 把字符串转换为大写
void ToLower(char *); // 把字符串转换为小写
void Transpose(char *); // 大小写转置
void Dummy(char *); // 不更改字符串
int main(void)
{
char line[LEN];
char copy[LEN];
char choice;
void (*pfun)(char *); // 声明一个函数指针,被指向的函数接受char *类型的参数,无返回值
puts("Enter a string (empty line to quit):");
while (s_gets(line, LEN) != NULL && line[0] != '\0')
{
while ((choice = showmenu()) != 'n')
{
switch (choice) // switch语句设置指针
{
case 'u':
pfun = ToUpper;
break;
case 'l':
pfun = ToLower;
break;
case 't':
pfun = Transpose;
break;
case 'o':
pfun = Dummy;
break;
}
strcpy(copy, line); // 为show()函数拷贝一份
show(pfun, copy); // 根据用户的选择,使用选定的函数
}
puts("Enter a string (empty line to quit):");
}
puts("Bye!");
return 0;
}

char showmenu(void)
{
char ans;
puts("Enter menu choice:");
puts("u) uppercase l) lowercase");
puts("t) transposed case o) original case");
puts("n) next string");
ans = getchar(); // 获取用户的输入
ans = tolower(ans); // 转换为小写
eatline(); // 清理输入行
while (strchr("ulton", ans) == NULL)
{
puts("Please enter a u, l, t, o, or n:");
ans = tolower(getchar());
eatline();
}
return ans;
}

void eatline(void)
{
while (getchar() != '\n')
continue;
}

void ToUpper(char *str)
{
while (*str)
{
*str = toupper(*str);
str++;
}
}

void ToLower(char *str)
{
while (*str)
{
*str = tolower(*str);
str++;
}
}

void Transpose(char *str)
{
while (*str)
{
if (islower(*str))
*str = toupper(*str);
else if (isupper(*str))
*str = tolower(*str);
str++;
}
}
void Dummy(char *str)
{
// 不改变字符串
}
void show(void (*fp)(char *), char *str)
{
(*fp)(str); // 把用户选定的函数作用于str
puts(str); // 显示结果
}
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;
}