c plus第14章-详细解法+注释(欢迎一起讨论,顺带帮助我复习)

博客围绕指针展开,涉及天数统计函数用指针修改实参值,结构体嵌套中指针操作,还介绍了球员id的普通排序和快速排序,文件写入操作及乱码问题,堆区内存开辟存储书籍信息,以及多维数组理解、函数指针使用等信息技术相关内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

14.1

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define LEN 12

struct month
{
    char name[10];
    char abbrev[4];
    int days;
    int monnmb;
};

//创建了一个结构体数组
const struct month months[LEN] = {

       {"January", "Jan", 31, 1},
       {"February", "Feb", 28, 2},
       {"March", "Mar", 31, 3},
       {"April", "Apr", 30, 4},
       {"May", "May", 31, 5},
       {"June", "Jun", 30, 6},
       {"July", "Jul", 31, 7},
       {"August", "Aug", 31, 8},
       {"September", "Sep", 30, 9},
       {"October", "Oct", 31, 10},
       {"November", "Nov", 30, 11},
       {"December", "Dec", 31, 12}

};

int days(char *name, int *temp);

int main(void)
{
    int tp;
    int daytotal;
    char input[LEN];
    printf("请输入一个月份名(按q退出本程序):");
    while((scanf("%11s", input) == 1) && (input[0] != 'q'))
    {
        daytotal = days(input, &tp);
        	//tp对其取地址,是为了在下面方便赋值
        if(daytotal > 0)
        {
            printf("一年中到%d月(包括%d月)总共有%d天.\n", tp, tp, daytotal);
        }
        else
        {
            printf("%s不是一个月份名!\n", input);
        }
        printf("您可以再次输入一个月份名(或按q退出):");
        while(getchar() != '\n')
            continue;
    }
    printf("本程序完成!\n");
    return 0;
}

int days(char *m, int *temp)
{
    int i = 1;
    int num = 0;
    int total = 0;

    m[0] = toupper(m[0]);
        //第一个字符转换成大写
    while(m[i])
    {
        m[i] = tolower(m[i]);
        i++;
        //数组中剩余字符转换成小写
    }

    for(i = 0; i < LEN; i++)
    {
        puts("111");
        if(0 == strcmp(m, months[i].name))
        {
            num = months[i].monnmb;
             //输入和结构体数组里的月份字符相同,把当前月分日期赋值过去
            *temp = i + 1;
             //二者相同,循环停止,
             //把i值赋值过去(意思:包括%d月)
            break;
        }
    }
    if(num == 0)
    {
        total = -1;
    }
    else
    {
     	//将月份当作循环条件,统计所有天数
        for(i = 0; i < num; i++)
        {
            total += months[i].days;
             //统计一下num个月(包括%d月)的所有天数
        }
    }

    return total;
}

在这里插入图片描述在这里插入图片描述

.
.

14.2

   输入30 5 1 【日、月、年】:先进入for循环,打印44,在对if中的条件进行比较,在下标i = 0、1、2、3的情况下都不满足,走else1、2、3、4月份的天数进行直接统计;在下标为4(5月份)满足if条件,进入if,对当前月份数进行计算(*tp),且加上当前月份的天数。
.
   天数统计函数==》days_result(month, day, &n):要对月份、天数进行判断,且返回天数统计的总和、以及当前月份(所以形参采用指针的方式接收实参传递的地址,来达到对实参值修改的目的)。
.
   结构体数组创建,每个数组的元素下标,对应一个结构体类型
.
.

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#define LEN 12

struct month
{
    char name[10];
    char abbrev[4];
    int days;
    int monumb;
};

struct month months[LEN] =
{
    {"January", "Jan", 31, 1},
    {"February", "Feb", 28, 2},
    {"March", "Mar", 31, 3},
    {"April", "Apr", 30, 4},
    {"May", "May", 31, 5},
    {"June", "Jun", 30, 6},
    {"July", "Jul", 31, 7},
    {"August", "Aug", 31, 8},
    {"September", "Sep", 30, 9},
    {"October", "Oct", 31, 10},
    {"November", "Nov", 30, 11},
    {"December", "Dec", 31, 12}
};


int days_result(char *month, int days, int *tp);

int main(void)
{
    //int i = 0;
    int n, val;
    int day, year;
    char month[LEN];

    printf("请您输入日,月和年(按q退出本程序):\n");
    while (scanf("%d %11s %d", &day, month, &year) == 3)
    {
        if((year % 4 ==0 && year % 100 != 0) || (year % 400 == 0))
        {
            months[1].days = 29;//判断是否是闰年;
        }
        val = days_result(month, day, &n);
        if (val < 0)
        {
            printf("您输入的数据有误!\n");
        }
        else
        {
            printf("从%d年初到%d月%d日总共有%d天.\n", year, n, day, val);
        }
        months[1].days = 28;//2月份天数还原为非闰年时的天数;
        printf("您可以继续输入日,月和年(或按q退出):\n");
    }
    printf("本程序完成!\n");

    return 0;
}

int days_result(char *month, int days, int *tp)
{
    int i;
    int total = 0;
    int temp = atoi(month);//输入全是字符串未进行有效转化,最后int值为0
                           //月份可以是月份号的情况下,字符‘5’-->整数5(字符串转换成数字的函数)
    month[0] = toupper(month[0]);//转换成大写


    if(days < 1 || days > 31)
    {
        return -1; //输入的天数有误则终止本函数;
    }
    for(i = 0; i < LEN; i++)
    {
         puts("44");
        if ((temp == months[i].monumb) ||(strcmp(month, months[i].name) == 0) ||(strcmp(month, months[i].abbrev) == 0))
        {
            puts("33");
            //⬆三种情况下满足一个即可
            if(days > months[i].days)
            {
                return -1;//输入的天数不符合月份的规则;
            }
            else
            {
                puts("11");
                *tp = i + 1;//循环判断上述条件,满足i停止,即为本月月数(数组下标从0开始)
                return total + days;//累加到此月后再加上--本月天数;我们输入的天数
            }
        }
        else
        {
            puts("22");
            total += months[i].days;//i在循环,加上每次i变化时对应的天数
        }
    }
     return -1; //输入月份有误后的终止条件;
}

在这里插入图片描述
.
.

14.3

count++;
            //服务于结构体中的所有属性,不能仅仅在一个属性中采取count++的做法
  1. struct book *book[MAXBKS];一般会通过创建一个新的指针,来满足间接操作的目的
#include <stdio.h>
#include <string.h>
#define MAXTITL 40
#define MAXAUTL 40
#define MAXBKS 100

struct book
{
    char title[MAXTITL];
    char author[MAXAUTL];
    float value;
};


char *s_gets(char *st, int n);
void sort_title(struct book *pb[], int n);
void sort_value(struct book *pb[], int n);

int main(void)
{
    struct book library[MAXBKS];
    struct book *book[MAXBKS];//创建一个结构体指针数组~
    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) && 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);
         book[count] = &library[count];
            //将library数组中各个元素下标的地址信息,赋值给指针数组book中的对应指针元素
            //他们都是指向struct book类型的指针
            
        count++;
            //服务于结构体中的所有属性,不能仅仅在一个属性中采取count++的做法
            
         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);
        }
        putchar('\n');
        
        sort_title(book, count);//按照标题排序
        printf("\nHere is the list of your books sorted by title letters:\n");
        for(index = 0; index < count; index++)
        {
            printf("%s by %s: $%.2f\n", book[index]->title,
                   book[index]->author, book[index]->value);
        }

        putchar('\n');
        sort_value(book, count);
        printf("\nHere is the list of your books sorted by value(from low to high):\n");
        for(index = 0; index < count; index++)
        {
            printf("%s by %s: $%.2f\n", book[index]->title,
                   book[index]->author, book[index]->value);
        }
    }
    else
    {
         printf("No books? Too bad.\n");
    }


    return 0;
}

void sort_title(struct book *pb[], int n)
{
    int i, j;
    struct book *temp;
    for(i = 0; i < n - 1; i++)
    {
        for(j = i + 1; j < n; j++)
        {
            if(strcmp(pb[j]->title, pb[i]->title) < 0)
            {
                temp = pb[j];
                pb[j] = pb[i];
                pb[i] = temp;
            }
        }
    }

    return;
}

void sort_value(struct book *pb[], int n)
{
    int i, j;
    struct book *temp;
    for(i = 0; i < n - 1; i++)
    {
        for(j = i + 1; j < n; j++)
        {
            if(pb[j]->value < pb[i]->value)
            {
                temp = pb[j];
                pb[j] = pb[i];
                pb[i] = temp;
            }
        }
    }
    return;
}

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)
        {
            *find = '\0';
        }
        else
        {
            while (getchar() != '\n')
                continue;
        }
    }
    return ret_val;
}

在这里插入图片描述
.
.

14.4a

1. 结构体的嵌套;
2. show(m, count);中的m是结构体数组的首地址,所以形参采取pt[ ]的形式接收实参的地址信息,从而去指向实参中的成员属性(等价于 *pt )

#include <stdio.h>
#include <string.h>
#define N 15
#define LEN 30

struct names
{
    char fname[N];
    char mname[N];
    char lname[N];
};

struct messages
{
    char ins_num[LEN];
    struct names name;
};

char *s_gets(char *st, int n);
void show(const struct messages pt[], int n);

int main(void)
{
    int count = 0;
    struct messages m[5];

    printf("Please enter the insurance number:\n");
    printf("Press [enter] at the start of a line to stop.\n");
    while(count < 5 && s_gets(m[count].ins_num, LEN) && m[count].ins_num[0] != '\0'
                        && m[count].name.fname[0] != '\0')
    {
        printf("Now enter the former name.\n");
        s_gets(m[count].name.fname, N);
        printf("Now enter the middle name(Without,[enter] to the next).\n");
        s_gets(m[count].name.mname, N);
        printf("Now enter the last name.\n");
        s_gets(m[count].name.lname, N);

        if(count++ < 5)
        {
             printf("Enter the next insurance number:\n");
        }
    }
    if(count > 0)
    {
        show(m, count);
    }
    else
    {
        printf("No data!\n");
    }

    return 0;
}

void show(const struct messages pt[], int n)
{
    int i = 0;

    printf("All numbers messages:\n");
    for(; i < n; i++)
    {
        if(pt[i].name.mname[0] == '\0')
        {
            printf("%s, %s", pt[i].name.fname, pt[i].name.lname);
            printf(" -- %s\n", pt[i].ins_num);
        }
        else
        {
            printf("%s, %s %c.", pt[i].name.fname, pt[i].name.lname, pt[i].name.mname[0]);
            printf(" -- %s\n", pt[i].ins_num);
        }
    }

    return;
}

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;

}

在这里插入图片描述

14.4b

.

  1. 值传递,接收主函数中的实参变量的所有信息且不会改变实参的数据,在功能函数中运行过后便会清零,栈的思想
  2. void show_messages(const struct messages pt); 注意函数的形参

.

#include <stdio.h>
#include <string.h>
#define N 15
#define LEN 30

struct names
{
    char fname[N];
    char mname[N];
    char lname[N];
};

struct messages
{
    char ins_num[LEN];
    struct names name;
};

char *s_gets(char *st, int n);
void show_messages(const struct messages pt);

int main(void)
{
    int i;
    int count = 0;
    struct messages m[5];

    printf("Please enter the insurance number:\n");
    printf("Press [enter] at the start of a line to stop.\n");
    while(count < 5 && s_gets(m[count].ins_num, LEN) && m[count].ins_num[0] != '\0'
                        /*&& m[count].name.fname[0] != '\0'*/)
    {
        printf("Now enter the former name:\n");
        s_gets(m[count].name.fname, N);
        printf("Now enter the middle name(Without,[enter] to the next):\n");
        s_gets(m[count].name.mname, N);
        printf("Now enter the last name:\n");
        s_gets(m[count].name.lname, N);

        if(count++ < 5)
        {
             printf("Enter the next insurance number:\n");
        }
    }
    if(count > 0)
    {
        printf("All numbers messages:\n");
        for(i = 0; i < count; i++)
        {
            show_messages(m[i]);
        }
    }
    else
    {
        printf("No data!\n");
    }

    return 0;
}

void show_messages(const struct messages pt)//直接传递结构体的变量
{
    //int i = 0;

    //printf("All numbers messages:\n");
    //for(; i < count; i++)
    //{
        if(pt/*[i]*/.name.mname[0] == '\0')
        {
            printf("%s, %s", pt/*[i]*/.name.fname, pt/*[i]*/.name.lname);
            printf(" -- %s\n", pt/*[i]*/.ins_num);
        }
        else
        {
            printf("%s, %s %c.", pt/*[i]*/.name.fname, pt/*[i]*/.name.lname, pt/*[i]*/.name.mname[0]);
            printf(" -- %s\n", pt/*[i]*/.ins_num);
        }
    //}

    return;
}

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;

}

在这里插入图片描述
.

14.5

  1. 注意需要结构体的嵌套,学生的结构体中包含名字的结构体
  2. 分步走
  3. 2021.2.05复看于此
  4. grade[j]是一个float值,因此需要‘&’,来获取地址信息
#include <stdio.h>
#include <string.h>
#define LEN 15
#define CSIZE 4//声明一个内含CSIZE(CSIZE = 4)个student类型结构的数组
#define SCORES 3

struct name
{
    char fname[LEN];
    char lname[LEN];
};

struct student
{
    struct name mes;
    float grade[SCORES];//储存3个浮点型分数
    float aver;//储存3个分数平均数
};

void set_students(struct student ar[], int n);
void find_averages(struct student ar[], int n);
void show_messages(const struct student ar[], int n);
void show_averages(const struct student ar[], int n);

int main(void)
{
    struct student classes[CSIZE]  =
    {
        {"Flip", "Snide"},
        {"Clare", "Voyans"},
        {"Bingo", "Higgs"},
        {"Fawn", "Hunter"}
    };

    set_students(classes,CSIZE);//为结构体数组中的学生设置成绩
    find_averages(classes,CSIZE);
    show_messages(classes,CSIZE);
    show_averages(classes,CSIZE);

    return 0;

}

void set_students(struct student ar[], int n)
{
    int i, j;
    for(i = 0; i < n; i++)
    {
        puts("@");
        printf("请为%s %s输入3个成绩 ;\n", ar[i].mes.fname, ar[i].mes.lname);
        for(j = 0; j < SCORES; j++)
        {
              //grade是一个数组,j相当于元素下标
              //grade[j]是一个float值,因此需要‘&’,来获取地址信息
            while(scanf("%f", &ar[i].grade[j]) != 1)
            {
                while(getchar() != '\n')
                {
                    continue;
                }
                puts("数据无效!请重新输入:");
            }
        }
    }
    return;
}

void find_averages(struct student ar[], int n)
{
    int i, j;
    float sum;

    for(i = 0; i< n; i++)
    {
        for(j = 0, sum = 0.0f; j < SCORES; j++)
        {
            sum += ar[i].grade[j];
        }
          //i表示第几个学生,aver是struct student结构体中的成员属性
        ar[i].aver = sum / SCORES;
    }
    return;
}

void show_messages(const struct student ar[], int n)
{
    int i, j;
    for(i = 0; i < n; i++)
    {
        printf("\n%s %s的3科成绩是:\n", ar[i].mes.fname, ar[i].mes.lname);
        for (j = 0; j < SCORES; j++)
        {
            printf("每门成绩 :%g.\n", ar[i].grade[j]);
        }
        printf("最后平均成绩 :%g.\n", ar[i].aver);
    }
    return;
}

void show_averages(const struct student ar[], int n)
{
    int i;
    float total;
    for(i = 0, total = 0.0f; i < n; i++)
    {
        total += ar[i].aver;
    }
    printf("\n班级平均分是:%g\n", total / n);
    return;
}

在这里插入图片描述

.

14.6.1

  1. 全局静态变量已经清零了;
  2. 在从文件中读取数据的函数中,第一个fscanf已经读取过一次信息,若不rewind(fp)返回到文件开始处,结果将会少一个球员的数据;
  3. count_hit_rate()、show_messages()、read_datas()函数中传递的实参都是结构体数组首地址以及数组大小
  4. read_datas()函数中,对结构体数组中的成员赋值数据要注意,是给对应id(这里是m)的球员进行赋值的,就是fscanf中写入到m中的id号;例如:players[m].id = m;
  5. 普通for循环顺序排序。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LEN 19

typedef struct
{
    int id;          //球员的编号
    char fname[LEN]; //球员的名;
    char lname[LEN]; //球员的姓;
    int stage_num;   //球员的上场次数;
    int hit_num;     //球员的击中数;
    int base_num;    //球员的走垒数;
    int rbi;         //球员的打点;
    float hit_rate;  //球员的安打率;
}TEAM;

//void clear_datas(TEAM players[], int n);
int read_datas(TEAM players[], int n, FILE *fp);
void count_hit_rate(TEAM players[], int n);
void show_messages(TEAM players[], int n);

static TEAM players[LEN]; //全局静态变量中数据已清0;

int main(void)
{
    FILE *fp;
    int len;
    //TEAM players[LEN];/*全局静态变量中数据已清0;*/
                        /*这里就不需要专门去清理结构体数组中的成员信息清理了*/

    //球员等信息在文件中
    if((fp = fopen("datas.txt", "r")) == NULL)
    {
        fprintf(stderr,"Can't open file datas.txt.\n");
        exit(EXIT_FAILURE);
    }

    //clear_datas(players, LEN);
    len = read_datas(players, LEN, fp);//统计文件中不同球员的数量;
    printf("读取到的球员个数 :len = %d\n", len);
    count_hit_rate(players, LEN);
    show_messages(players, LEN);

    if(fclose(fp) != 0)
    {
        fprintf(stderr, "Can't close file datas.txt.\n");
        exit(EXIT_FAILURE);
    }

    return 0;

}

//原数组内容全部清零
/*void clear_datas(TEAM players[], int n)
{
    int i;
    for(i = 0; i < n; i++)
    {
        players[i].id = 0;
        players[i].hit_num = 0;
        players[i].hit_rate = 0;
        players[i].base_num = 0;
        players[i].rbi = 0;
        players[i].stage_num = 0;
        memset(players[i].fname, '\0', sizeof(players[i].fname));
        memset(players[i].lname, '\0', sizeof(players[i].lname));
    }
}*/

int read_datas(TEAM players[], int n, FILE *fp)
{
    int ch = 0;
    int count = 0;
    float hit_rate;//球员的安打率;
    char fname[LEN], lname[LEN];
    int m , stage_num, hit_num, base_num, rbi;

    count = fscanf(fp, "%d %18s %18s %d %d %d %d",
                   &m, fname, lname, &stage_num, &hit_num,
                   &base_num, &rbi);//返回我们输入成功的次数
    printf("统计我们输入成功的次数(每个球员有7条信息) :count = %d\n", count);


    //while循环外的fscanf已经将文件的内容读入到对应变量的地址中一次
    //所以要新定位到文件的开始,否则会从文本的第二行开始读取
    rewind(fp);
    while((count == 7) && !feof(fp) && (ch < n))
    {

        if(players[m].stage_num == 0)
        {
            ch++;//上场次数为0,就去统计下一个球员信息;
        }

        //这里从文件中的第一行向变量中输入信息,去赋值到结构体数组对应的成员中去
        //
        count = fscanf(fp, "%d %18s %18s %d %d %d %d",
                       &m, fname, lname, &stage_num, &hit_num,
                       &base_num, &rbi);//返回我们输入成功的次数

        //对应变量的地址中的数据,赋值给结构体中的成员
        strcpy(players[m].fname, fname);
        strcpy(players[m].lname, lname);
        players[m].id = m;
        players[m].stage_num += stage_num;//同一个球员就累加
        players[m].hit_num += hit_num;
        players[m].base_num += base_num;
        players[m].rbi += rbi;


    }

    return ch;

}

//计算安打率;
void count_hit_rate(TEAM players[], int n)
{
    int i;
    if(n > 0)
    {
        for(i = 0; i < n; i++)
        {
            players[i].hit_rate = (float)players[i].hit_num / (float)players[i].stage_num;
            //计算每个运动员的安打率;i
            //球员的累计击中数除以上场累计次数
        }
    }

    return;
}

void show_messages(TEAM players[], int n)
{
    int i;
    if(n == 0)
    {
        printf("No datas!\n");
    }
    else
    {
        //for循环会按序排列
        printf("Datas for all players:\n");
        printf("Id   First_name   Last_name   Stage   Hit_rate   Base_num   RBI   Hit_rate\n");
        for(i = 0; i < n; i++)
        {
            puts("===");
            printf("%-4d %-12s %-10s %5d %7d %11d %8d %8.2f\n",
                   players[i].id, players[i].fname, players[i].lname,
                   players[i].stage_num, players[i].hit_num, players[i].base_num,
                   players[i].rbi, players[i].hit_rate);
        }
    }
    return;
}

在这里插入图片描述
在这里插入图片描述
.
.

标题 14.6.2—采用快速排序,进行球员id的顺序排列

  1. 增加了快速排序函数
  2. 快速排序结束后,增加了累加的函数
  3. 注意快速排序中的,基准值的设定,我们要排序什么类型的值,基准值的值类型就要与排序值的类型一致这里是对结构体数组中的id进行排序,因此基准值类型要是结构体
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LEN 19

typedef struct
{
    int id;          //球员的编号
    char fname[LEN]; //球员的名;
    char lname[LEN]; //球员的姓;
    int stage_num;   //球员的上场次数;
    int hit_num;     //球员的击中数;
    int base_num;    //球员的走垒数;
    int rbi;         //球员的打点;
    float hit_rate;  //球员的安打率;
}TEAM;


int read_datas(TEAM players[], int n, FILE *fp);
void count_hit_rate(TEAM players[], int n);
void show_messages(TEAM players[], int n);

//信息累加
void add_messages(TEAM players[],int n);

//快速排序
//int partition(TEAM players[],int begin,int end);
void quickSort(TEAM players[],int begin,int end);
void my_shos_messages(TEAM players[], int n);

static TEAM players[LEN]; //全局静态变量中数据已清0;

int main(void)
{
    FILE *fp;
    int len;


    //球员等信息在文件中
    if((fp = fopen("datas.txt", "r")) == NULL)
    {
        fprintf(stderr,"Can't open file datas.txt.\n");
        exit(EXIT_FAILURE);
    }


    len = read_datas(players, LEN, fp);//统计文件中不同球员的数量;
    count_hit_rate(players, len);
    show_messages(players, len);
	//数据累加处理
	add_messages(players, len);
	my_shos_messages(players, len);

    if(fclose(fp) != 0)
    {
        fprintf(stderr, "Can't close file datas.txt.\n");
        exit(EXIT_FAILURE);
    }

    return 0;

}

int read_datas(TEAM players[], int n, FILE *fp)
{
    int ch = 0;
    int count = 0;
    float hit_rate;//球员的安打率;
    char fname[LEN], lname[LEN];
    int m , stage_num, hit_num, base_num, rbi;

    count = fscanf(fp, "%d %18s %18s %d %d %d %d",
                   &m, fname, lname, &stage_num, &hit_num,
                   &base_num, &rbi);//返回我们输入成功的次数
	printf("count = %d\n",count);


    while((count == 7)  && (ch <= n+1))
    {		
        puts("##");
        ch++;//ch从0开始++,这里的ch == 1;而ch - 1 : 相当于rewind(fp)

        //首先会拷贝我们的第一列信息
        strcpy(players[ch-1].fname, fname);
        strcpy(players[ch-1].lname, lname);
        players[ch-1].id = m;
        players[ch-1].stage_num += stage_num;//同一个球员就累加
        players[ch-1].hit_num += hit_num;
        players[ch-1].base_num += base_num;
        players[ch-1].rbi += rbi;
		
        printf("ch -1  = %d m = %d\n",ch-1,m);
        //这里再次从文件中向变量中输入信息,去赋值到结构体数组中去
        count = fscanf(fp, "%d %18s %18s %d %d %d %d",
                       &m, fname, lname, &stage_num, &hit_num,
                       &base_num, &rbi);//返回我们输入成功的次数
	}
	
    return ch;
}

//计算安打率;
void count_hit_rate(TEAM players[], int n)
{
    int i;
    if(n > 0)
    {
        for(i = 0; i < n; i++)
        {
            players[i].hit_rate = (float)players[i].hit_num / (float)players[i].stage_num;
            //计算每个运动员的安打率;i
            //球员的累计击中数除以上场累计次数
        }
    }

    return;
}


//快排
void quickSort(TEAM players[],int begin,int end)
{
   if(begin < end)
   {
    int i = begin;//左地址、起始地址
    int j = end;//右地址、终止地址

    //*注意此处,基准值的类型!***给定一个基准值,用来比较***
    TEAM key_id = players[i];

    while(i < j)
	{	


        while (i < j && players[j].id >= key_id.id)
		{
            j--;
		}	
        //将右边小于等于基准元素的数填入右边相应位置
        players[i] = players[j];
 
        while (i < j && players[i].id <= key_id.id)
		{
            i++;
		}
        //将左边大于基准元素的数填入左边相应位置
        players[j] = players[i];
	}

    //将基准元素填入相应位置
    players[i] = key_id;
    quickSort( players, begin, i-1);//左区间继续比较
    quickSort( players, i+1, end);//右区间比较

   }

}

void show_messages(TEAM players[], int n)
{
    puts("排序后的数组:");
    int i;
    //int i2;
    //TEAM temp;
    if(n == 0)
    {
        printf("No datas!\n");
    }
    else
    {
        //for循环会按序排列
        printf("Datas for all players:\n");
        printf("Id   First_name   Last_name   Stage   Hit_rate   Base_num   RBI   Hit_rate\n");

        //方法① :按照id号进行排序
        quickSort(players, 0, n-1);

        /*
        //方法② :冒泡排序
        for(i = 0; i < n-1; i++)
        {
            for (i2 = 0; i2 < n-i-1; i2++)
            {
                if (players[i2].id > players[i2+1].id)
                {
                    temp = players[i2];
                    players[i2] = players[i2+1];
                    players[i2+1] = temp;
                }
            }
        }*/

        for(i=0;i<n;i++)
        {
            puts("===");
            printf("%-4d %-12s %-10s %5d %7d %11d %8d %8.2f\n",
                   players[i].id, players[i].fname, players[i].lname,
                   players[i].stage_num, players[i].hit_num, players[i].base_num,
                   players[i].rbi, players[i].hit_rate);
        }
    }
    return;
}



//相同编号信息累加
void add_messages(TEAM players[],int n)
{
	int i,i2;
	if(n == 0)
    {
        printf("No datas!\n");
    }
    
    //前后编号交互比较(冒泡法)
    //①与②比了,②还会与①比,用memset来防止这样的操作,只让其顺向进行比较
	for (i = 0; i < n; i++)
	{
		for (i2 = 0; i2 < n; i2++)
		{
			if (i == i2)
			{
				continue;
			}
			if (strcmp(players[i].fname,players[i2].fname) == 0 && strcmp(players[i].lname,players[i2].lname) == 0)
			{
				players[i].base_num +=players[i2].base_num;
				players[i].hit_num +=players[i2].hit_num;
				players[i].hit_rate +=players[i2].hit_rate;
				players[i].rbi +=players[i2].rbi;
				players[i].stage_num +=players[i2].stage_num;
				players[i2].id = -1;//如果相近两个编号都是同一个人,该处可以防止累加完之后,第二个编号还存在
				printf("go to add -----\n");
				memset(players[i2].fname,i,sizeof(players[i2].fname));  //打乱数据防止再进入....
				memset(players[i2].fname,i2,sizeof(players[i2].fname)); //
			}
		}
	}
    printf("ADD ---------------------------------OVER\n");
    putchar('\n');
	return ;
}

void my_shos_messages(TEAM players[], int n)
{
    puts("累加后的结构体数组:");
    int i;
    for(i=0;i<n;i++)
    {
        if(players[i].id != -1)
        {
            puts("===");
            printf("%-4d %-12s %-10s %5d %7d %11d %8d %8.2f\n",
               players[i].id, players[i].fname, players[i].lname,
               players[i].stage_num, players[i].hit_num, players[i].base_num,
               players[i].rbi, players[i].hit_rate);
        }
    }
}

在这里插入图片描述
累加函数处可以这样修改 :
修改第二个for循环的条件。

//相同编号信息累加
void add_messages(TEAM players[],int n)
{
	int i,i2;
	if(n == 0)
    {
        printf("No datas!\n");
    }
    
    //冒泡比较
	for (i = 0; i < n; i++)
	{
		for (i2 = i + 1; i2 < n; i2++)//i2 = 0 --> i2 = i + 1
		{
			if (i == i2)
			{
				continue;
			}
			if (strcmp(players[i].fname,players[i2].fname) == 0 && strcmp(players[i].lname,players[i2].lname) == 0)
			{
				players[i].base_num +=players[i2].base_num;
				players[i].hit_num +=players[i2].hit_num;
				players[i].hit_rate +=players[i2].hit_rate;
				players[i].rbi +=players[i2].rbi;
				players[i].stage_num +=players[i2].stage_num;
				players[i2].id = -1;//如果相近两个编号都是同一个人,该处可以防止累加完之后,第二个编号还存在
				printf("go to add -----\n");
				//memset(players[i2].fname,i,sizeof(players[i2].fname));  //打乱数据防止再进入....
				//memset(players[i2].fname,i2,sizeof(players[i2].fname)); //
			}
		}
	}
    printf("ADD ---------------------------------OVER\n");
    putchar('\n');
	return ;
}

14.7 (1)

  1. 首地址可以做简写操作,因为&name[0]和name的地址是相同的,而数组后面的其余元素,就需要进行取地址操作&name[index]。在这里插入图片描述
  2. 注意结构体的嵌套,结构体数组中包含结构体变量、布尔类型值–>结构体变量中含有自身结构体的各类成员属性。
  3. 这里的结构体数组,每个下标对应两个成员属性(书本信息的结构体、是否删除的布尔判断);书本信息的结构体含有 :标题、作者、价格信息
  4. 2-21号的代码因为会每次清空文件,所以 优先打印【不用删除的书籍数量filecount = %d】的信息,提示用户输入数据。不过输入过后,txt文件中可以看到我们的输入
  5. 写入文件 :要fwrite写入下午那种结构体 可以先把结构体内容用sprintf放到一个字符串char buf[]中 在把buf用fwirte写入 就不会乱码了
//2-21
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define MAXTITL 40
#define MAXAUTL 40
#define MAXBKS 10
#define CONTINUE 0
#define DONE 1

struct book
{
    char title[MAXTITL];
    char author[MAXAUTL];
    float value;
};

struct pack
{
    struct book books;
    bool canceled;

};

struct pack lib;
char *s_gets(char *st, int n);
int get_first(const char *str);
int get_books(struct pack *pb);
void update(struct pack *item);
void eatline(void);

int main(void)
{
    struct pack library[MAXBKS];
     //这里的结构体数组,每个下标对应两个成员属性(书本信息的结构体、是否删除的布尔判断)
     //书本信息的结构体含有 :标题、作者、价格信息
    int count = 0;
    int deleted = 0;


    int index, filecount, open;
    FILE *pbooks;
    int size = sizeof(struct book);

    if ((pbooks = fopen("book.txt", "rb")) != NULL)
    {
        while (count < MAXBKS && fread(&library[count], size, 1, pbooks) == 1)
        {
            if (count == 0)
            {
                puts("book.dat的当前第一行的内容是:");
            }
            printf("%s by %s: $%.2f\n", library[count].books.title,
                   library[count].books.author, library[count].books.value);


            putchar('\n');
            printf("Do you want to change or delete this entry(y/n)?\n");
            printf("Please you enter to choose: ");
            if(get_first("yn") == 'y')
            {
                puts("=$$$");
                 printf("Enter c to change, d to delete entry: ");
                 if(get_first("cd") == 'd')
                 {
                     library[count].canceled = true;//作为删除标记
                     deleted ++;//删除数量加加

                     printf("deleted = %d\n",deleted);
                     puts("标记为删除的标记(下一本书的空格)。");
                 }
                 else
                 {
                     puts("修改原书籍内容");
                     update(&library[count]);//修改原书籍内容,传递结构体数组的地址
                 }
            }

             count ++;//统计数量加加
             putchar('\n');


        }
        if(fclose(pbooks) != 0)
        {
            fprintf(stderr, "Error in closing file.\n");
        }
        printf("书籍数量 :count = %d\n", count);
    }



//==============
    filecount = count - deleted; //不用删除的书籍数量;
    printf("不用删除的书籍数量filecount = %d\n",filecount);

    if(count == MAXBKS)
    {
        puts("=!!");
        fputs("The book.dat file is full.", stderr);
        exit(EXIT_FAILURE);
    }

//==============
    putchar('\n');
    if(deleted > 0)
    {
         printf("If you delete some books, 您应该输入书籍来替换 .\n");
    }

    puts("Please add new book titles.");
    puts("Press [enter] at the start of a line to stop.");
    open = 0;
    while(filecount < MAXBKS)
    {
        puts("不用删除的书籍数量比总数10少,那么:");

        if(filecount < count) //删除书可以循环的次数,无法直接删除,只能用新书替换
        {
            putchar('\n');
            puts("删除书可以循环的次数,无法直接删除,只能用新书替换");

            while(library[open].canceled == false)
            {
                puts("跳转到要删除的书的位置处");
                open++;
                  //该结构体数组元素位置上的数据删除失败,跳转到要删除的书的位置处(下一个元素处);
            }
            if(get_books(&library[open]) == DONE)
            {
                 puts("=*=*=*");
                break;//若未输入新书覆盖原有书籍则循环终止
            }

        }
        else if(get_books(&library[filecount]) == DONE)
        {
            puts("添加了新的书籍");
            break;//添加新的书籍;
        }

        filecount++;//未删除书籍数量加加
        putchar('\n');
        printf("当前书籍数量filecount2= %d\n",filecount);

        if(filecount < MAXBKS)
        {
            puts("Enter the next book title.");
        }

    }

//==============
    putchar('\n');
    puts("Here is the list of your books:");
    for(index = 0; index < filecount; index++)
    {
        if(library[index].canceled == false)
        {
            puts("书本信息:");
            printf("%s by %s : $%.2f\n", library[index].books.title,
                   library[index].books.author, library[index].books.value);

        }
    }

    //把修改的内容写到文件中去
    putchar('\n');
    if((pbooks = fopen("book.txt", "wb")) == NULL)
    {
        fputs("Can't open book.dat file for output.\n", stderr);
        exit(EXIT_FAILURE);
    }

    for(index = 0; index < filecount; index++)
    {
        int s_number = 0;
        int w_number = 0;
        char buf[200] = {0};
        puts("修改内容写入到文件:");

        if(library[index].canceled == false)
        {
            s_number = sprintf(buf,"%s %s %f \n",library[index].books.title,
                           library[index].books.author,library[index].books.value);
            w_number = fwrite(buf, s_number, 1, pbooks);
            if( w_number != 1)
            {
                printf("write error\n");
            }
        }
    }

    if (fclose(pbooks) != 0)
    {
        fprintf(stderr, "Error in closing file.\n");
    }


    puts("Bye.\n");
    return 0;
}




//判断我们的输入getchar(输入的是字符),在传递过来的字符串中吗
int get_first(const char *str)
{
    int ch;
    ch = tolower(getchar());

    while(strchr(str, ch) == NULL)
    {
        putchar('\n');

        printf("Invalid data! Please enter again:\n");
        eatline();
         //如果不在,清理一下输入行,再次输入

        ch = tolower(getchar());
    }
    eatline();

    return ch;
}

//保存用户输入的书籍的数据(该类信息都在结构体中)
int get_books(struct pack *pb)
{
    int status = CONTINUE;
     //辅助判断
    printf("Now enter the title /or/ Press Enter to exit: \n");
    if(s_gets(pb->books.title, MAXTITL) == NULL || pb->books.title[0] == '\0')
    {
        status = DONE;
          //若是输入EOF或换行符则改变status的值并将其返回(无新内容输入)
    }
    else
    {
        putchar('\n');

        printf("Now enter the author: \n");
        s_gets(pb->books.author, MAXAUTL);

        printf("Now enter the value: ");
        while(scanf("%f", &pb->books.value) != 1)
        {
            eatline();
            puts("Please enter a valid value: ");
        }

        eatline();
        pb->canceled = false;
    }
    printf("成功、失败的标志(0成功,1失败)status = %d\n",status);
    return status;
     //返回成功、失败的标志
}

//对书籍内容的修改,需要对结构体中的成员变量进行操作
void update(struct pack *item)
{
    int ch;
    struct book copy;

    copy = item->books;//保存原书的内容以便修改(对临时变量进行操作)

    putchar('\n');
    puts("============================================");
    puts("t) modify title a) modify author");
    puts("v) modify value s) quit and save changes");
    puts("q) quit and ignore changes");
    puts("============================================");
    printf("Please you enter to choose: ");

    //可以先选择对结构体临时变量中的成员属性进行赋值操作
    while((ch = get_first("tavsq")) != 's' && ch != 'q')
    {
        switch(ch)
        {
        case 't':
            {
                printf("Please enter new title: ");
                s_gets(copy.title, MAXTITL);
                break;
            }
        case 'a':
            {
                 printf("Please enter new author: ");
                 s_gets(copy.author, MAXAUTL);
                 break;
            }
        case 'v':
            {
                printf("Please enter new value: ");
                while(scanf("%f", &copy.value) != 1)
                {
                    eatline();
                    puts("Please enter a valid value: ");
                }
                eatline();
                break;
            }
        }

        putchar('\n');
        puts("============================================");
        puts("t) modify title a) modify author");
        puts("v) modify value s) quit, saving changes");
        puts("q) quit, ignore changes");
        puts("============================================");
        printf("You can choose again: ");
            //在输入成功后不想修改书籍内容,输入q退出程序且不改变原书的内容;

    }
    if(ch == 's')
    {
        item->books = copy;//修改了原书的内容
    }

    return;
}

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)
        {
            *find = '\0';
        }
        else
        {
            while (getchar() != '\n')
                continue;
        }
    }
    return ret_val;
}
void eatline(void)
{
    while (getchar() != '\n')
        continue;
    return;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2-21修改的代码,每次运行程序,都会把txt文件中的数据清零

.
.

14.7(2)这次虽然不会情况文档,但是会是txt中打开会显示乱码

  1. 将向写入文件的操作,改成了直接fwrite的操作,不再经过数组了
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define MAXTITL 40
#define MAXAUTL 40
#define MAXBKS 10
#define CONTINUE 0
#define DONE 1

struct book
{
    char title[MAXTITL];
    char author[MAXAUTL];
    float value;
};

struct pack
{
    struct book books;
    bool canceled;
};

char *s_gets(char *st, int n);
int get_first(const char *str);
int get_books(struct pack *pb);
void update(struct pack *item);
void eatline(void);

int main(void)
{
    struct pack library[MAXBKS];
     //这里的结构体数组,每个下标对应两个成员属性(书本信息的结构体、是否删除的布尔判断)
     //书本信息的结构体含有 :标题、作者、价格信息

    int count = 0;
    int deleted = 0;

    int index, filecount, open;//书籍结构体数组下标
    FILE *pbooks;
    FILE *pbooks1;

    int size = sizeof(struct book);

    if ((pbooks = fopen("book.txt", "rb")) != NULL)
    {
        while (count < MAXBKS && fread(&library[count], size, 1, pbooks) == 1)
        {
            if (count == 0)
            {
                puts("book.dat的当前第一行的内容是:");
            }
            //for(count = 0; count < MAXBKS; count ++)
            //{
                printf("%s by %s: $%.2f\n", library[count].books.title,
                   library[count].books.author, library[count].books.value);
            //}


            putchar('\n');
            printf("Do you want to change or delete this entry(y/n)?\n");
            printf("Please you enter to choose: ");
            if(get_first("yn") == 'y')
            {
                 puts("=$$$");
                 printf("Enter c to change, d to delete entry: ");
                 if(get_first("cd") == 'd')
                 {
                     library[count].canceled = true;//作为删除标记
                     deleted ++;//删除数量加加

                     printf("deleted = %d\n",deleted);
                     puts("标记为删除的标记(下一本书的空格)");
                 }
                 else
                 {
                     puts("修改原书籍内容");
                     update(&library[count]);//修改原书籍内容,传递结构体数组的地址
                 }
            }

             count ++;//统计数量加加


        }
        putchar('\n');
        printf("循环次数count = %d\n", count);
        if(fclose(pbooks) != 0)
        {
            fprintf(stderr, "Error in closing file.\n");
        }
    }

//==============
    filecount = count - deleted; //不用删除的书籍数量(若内容为0,会优先打印该行)
    printf("不用删除的书籍数量filecount = %d\n",filecount);

    if(count == MAXBKS)
    {
        puts("=!!");
        fputs("The book.dat file is full.", stderr);
        exit(EXIT_FAILURE);
    }

//==============
    putchar('\n');
    if(deleted > 0)
    {
         printf("If you delete some books, 您应该输入书籍来替换 .\n");
    }

    puts("Please add new book titles.");
    puts("Press [enter] at the start of a line to stop.");
    open = 0;
    while(filecount < MAXBKS)
    {
        puts("不用删除的书籍数量比总数10少,那么:");

        if(filecount < count) //删除书可以循环的次数,无法直接删除,只能用新书替换
        {
            putchar('\n');
            puts("删除书可以循环的次数,无法直接删除,只能用新书替换");

            while(library[open].canceled == false)
            {
                puts("跳转到要删除的书的位置处==>");
                open++;
                  //该结构体数组元素位置上的数据删除失败,跳转到要删除的书的位置处(下一个元素处);
            }

            if(get_books(&library[open]) == DONE)
            {
                puts("若未输入新书覆盖原有书籍则循环终止==>");
                break;//若未输入新书覆盖原有书籍则循环终止
            }

        }
        else if(get_books(&library[filecount]) == DONE)
        {
            puts("添加了新的书籍==>");
            break;//添加新的书籍;
        }

        filecount++;//未删除书籍数量加加
        printf("filecount2= %d\n",filecount);

        if(filecount < MAXBKS)
        {
            puts("Enter the next book title.");
        }

    }

//==============
    putchar('\n');
    puts("Here is the list of your books:");
    for(index = 0; index < filecount; index++)
    {
        if(library[index].canceled == false)
        {
            puts("书本信息:");
            printf("%s by %s : $%.2f\n", library[index].books.title,
                   library[index].books.author, library[index].books.value);

        }
    }

    //把修改的内容写到文件中去
    putchar('\n');
    if((pbooks1 = fopen("book.txt", "wb")) == NULL)
    {
        fputs("Can't open book.dat file for output.\n", stderr);
        exit(EXIT_FAILURE);
    }


    for(index = 0; index < filecount; index++)
    {
        puts("修改内容写入到文件:");
        char buf[200] = {0};
        int s_number = 0;
        int w_number = 0;
        if(library[index].canceled == false)//非删除项
        {
            //s_number = sprintf(buf, "%s %s %f", library[index].books.title, library[index].books.author,
                    //library[index].books.value);
            fwrite(&(library[index].books), size, 1, pbooks1);
        }
        //w_number = fwrite(buf, s_number, 1, pbooks1);
        //直接修改原文本内容;
        /* 要fwrite写入下午那种结构体
         *可以先把结构体内容用sprintf放到一个字符串char buf[]中
         *在把buf用fwirte写入 就不会乱码了*/

        //if(w_number != 1)
        //{
        //    puts("fwrite error!");
        //}
    }



    if (fclose(pbooks) != 0)
    {
        fprintf(stderr, "Error in closing file.\n");
    }


    puts("Bye.\n");
    return 0;
}




//判断我们的输入getchar(输入的是字符),在传递过来的字符串中吗
int get_first(const char *str)
{
    int ch;
    ch = tolower(getchar());

    while(strchr(str, ch) == NULL)
    {
        putchar('\n');

        printf("Invalid data! Please enter again:\n");
        eatline();
         //如果不在,清理一下输入行,再次输入

        ch = tolower(getchar());
    }
    eatline();

    return ch;
}

//保存用户输入的书籍的数据(该类信息都在结构体中)
int get_books(struct pack *pb)
{
    int status = CONTINUE;
     //辅助判断

    printf("Now enter the title: \n");
    if(s_gets(pb->books.title, MAXTITL) == NULL || pb->books.title[0] == '\0')
    {
        status = DONE;
          //若是输入EOF或换行符则改变status的值并将其返回(无新内容输入)
    }
    else
    {
        putchar('\n');

        printf("Now enter the author: \n");
        s_gets(pb->books.author, MAXAUTL);

        printf("Now enter the value: ");
        while(scanf("%f", &pb->books.value) != 1)
        {
            eatline();
            puts("Please enter a valid value: ");
        }

        eatline();
        pb->canceled = false;
    }
    return status;
     //返回成功、失败的标志
}

//对书籍内容的修改,需要对结构体中的成员变量进行操作
void update(struct pack *item)
{
    int ch;
    struct book copy;

    copy = item->books;//保存原书的内容以便修改(对临时变量进行操作)

    putchar('\n');
    puts("============================================");
    puts("t) modify title a) modify author");
    puts("v) modify value s) quit and save changes");
    puts("q) quit and ignore changes");
    puts("============================================");
    printf("Please you enter to choose: ");

    //可以先选择对结构体临时变量中的成员属性进行赋值操作
    while((ch = get_first("tavsq")) != 's' && ch != 'q')
    {
        switch(ch)
        {
        case 't':
            {
                printf("Please enter new title: ");
                s_gets(copy.title, MAXTITL);
                break;
            }
        case 'a':
            {
                 printf("Please enter new author: ");
                 s_gets(copy.author, MAXAUTL);
                 break;
            }
        case 'v':
            {
                printf("Please enter new value: ");
                while(scanf("%f", &copy.value) != 1)
                {
                    eatline();
                    puts("Please enter a valid value: ");
                }
                eatline();
                break;
            }
        }

        putchar('\n');
        puts("============================================");
        puts("t) modify title a) modify author");
        puts("v) modify value s) quit, saving changes");
        puts("q) quit, ignore changes");
        puts("============================================");
        printf("You can choose again: ");
            //在输入成功后不想修改书籍内容,输入q退出程序且不改变原书的内容;

    }
    if(ch == 's')
    {
        item->books = copy;//修改了原书的内容,并保存
    }

    return;
}

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)
        {
            *find = '\0';
        }
        else
        {
            while (getchar() != '\n')
                continue;
        }
    }
    return ret_val;
}
void eatline(void)
{
    while (getchar() != '\n')
        continue;
    return;
}

14.7 (3)-开辟堆区内存

   感觉这样的方法会更好一点,所有地方都分块去完成,且使用了函数指针的方法,更简洁;采用了在堆区开辟空间的方式去存储我们的书籍信息,相当于一个安全的临时变量(结构体指针变量-指向堆区的地址),不会影响到文本本来的内容。

//由于no7.c代码结构混乱,语句冗长,可读性,与维护性较低,所以最终决定重构no7.c
//以下程序思路,基于内存操作,使用了函数指针等简洁,可读性,维护性较高的技巧
//执行原理,程序启动将读取指定文件内所有内容,将内容整个放在内存中,关闭文件.
//接下来的增删查改等操作都对内存操作,当程序结束时,将内存中的所有数据重新写入
//文件内.
//缺点是对内存开销较大...但是目前我还未想到更好的办法
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
//# include <windows.h>

# define MAXTITL 20			//最大书名长度
# define MAXAUTL 20			//最大作者名长度
# define FILENAME "book.dat" 		//宏定义文件名
# define FLDWIDTH 15			//字段宽度(表格)


// 声明书籍结构体
typedef struct
{
    char 	title[MAXTITL] ;
    char 	author[MAXAUTL] ;
    float 	value ;
}book;

//定义全局结构体
static struct
{
    long COUNT ;
    book * BOOKS ;
}LIBRARY;

long load_file(char * file);//从文件读数据
long refresh_file(char * file);//写入文件
void print_hline(const char ch , int count);
void print_squared(const char * st , int fwidth);
char * s_gets(char * st , int n);

void out_menu(void);
int menu_choice(void);
void menu_end(void);
void lib_out(void);			//查
void lib_modify(void);		//改
void lib_add(void);			//增
void lib_del(void);			//删
int abort_operation();		//放弃操作

int main(void)
{
    char ch[20] ;

    load_file(FILENAME) ;

    system("cls");
    while (menu_choice())              //对数字的输入,让其在while循环中,不断提示用户哪些功能可选择
    {
        system("pause");
        system("cls");
    }

    refresh_file(FILENAME);
    free(LIBRARY.BOOKS);
    return 0 ;
}

long load_file(char * file)
{
    FILE * fp  ;
    long size = 0;
    long item = 0 ;

    if (!(fp = fopen(file , "rb")))					//打开文件失败
    {
        if (!(fp = fopen(file , "wb")))				//创建新的文件
        {
            fputs("创建文件失败" , stderr);
            exit(1);
        }

        fclose(fp);
        if (!(fp = fopen(file , "rb")))				//关闭文件并以读方式重新打开
        {
            fputs("打开文件失败" , stderr);
            exit(1);
        }
    }

    fseek(fp , 0L , SEEK_END);                  //定位到文件的结尾
    size = ftell(fp);                           //统计文件中字节的大小
    LIBRARY.COUNT = size / sizeof(book) ;
    LIBRARY.BOOKS = (book *) calloc(LIBRARY.COUNT , sizeof(book)) ;	//分配内存
    rewind(fp);							//文件指针重新定位到文件首
    item = fread(LIBRARY.BOOKS , sizeof(book) , LIBRARY.COUNT , fp);//读取文件
    fclose(fp);

    return item ;
}

long refresh_file(char * file)
{
    FILE * fp ;
    long item = 0 ;

    if (!(fp = fopen(file , "wb")))
    {
        fputs("更新文件失败" , stderr);
        exit(2);
    }

    item = fwrite(LIBRARY.BOOKS , sizeof(book) , LIBRARY.COUNT , fp);
    fclose(fp);

    return item ;
}

void print_squared(const char * st , int fwidth)        //实参传递过来的是字符串的地址信息 以及 字段宽度
{
    printf("%-*c%s%*c" , (fwidth - strlen(st)) / 2 , '|' , st ,
            (fwidth - strlen(st)) / 2 , '|');
    printf("%s\n",(fwidth - strlen(st)) / 2);

}

void print_hline(const char ch , int count)
{
    putchar(' ');
    while (--count)
    {
        putchar(ch);
    }
    putchar('\n');
}



int menu_choice(void)
{
     //meun包含函数指针的数组(每个都指向无形参,且无返回值类型的函数)
    static const void (* menu[])(void) = {menu_end ,
        lib_out , lib_modify , lib_add , lib_del};
    int cmd = 0 ;

    out_menu();     
    while (!scanf(" %d" , &cmd) || cmd < 0 || cmd > 4)          //开始输入我们的选择
    {
        if ((cmd < 0) || (cmd > 4))
        {
            fputs("无效选择,请输入正确的序号:\n" , stderr);
        }
        else
        {
            fputs("非法输入\n" , stderr);
        }

        while (getchar() != '\n') ;
    }
    while (getchar() != '\n') ;

    menu[cmd]();                                                //函数指针,根据我们输入的数字,接收相对应的函数的地址,来实现功能

    return cmd ;                                                //返回成功输入的个数
}

void out_menu(void)
{
    static char * menu[] =
    {
        "#################################",
        "请选择您需要的操作:" ,
        "1) 查看		2) 修改",
        "3) 添加		4) 删除",
        "0) 退出" ,
        "#################################"
    };
    int i;

    for( i = 0 ; i < sizeof(menu) / sizeof(menu[0]) ; i++)
    {
        puts(menu[i]);
         //这是个指针数组,i对应字符串所在行号(6个字符串)
    }
}
void menu_end(void)
{
    puts("再见,祝您生活愉快!");
}

void lib_out(void)
{
    static const char * label[] =
    {
        "书名" ,
        "作者" ,
        "价格"
    } ;                                         //指针数组
    char tmp[100] ;
    int i;

    // 打印表头
    print_hline('=' , (sizeof(label) / sizeof(label[0])) * FLDWIDTH - 4);
    for ( i = 0 ; i < sizeof(label) / sizeof(label[0]) ; i++)
    {
        print_squared(label[i] , FLDWIDTH);      //输出指针数组中的字符串
    }
    putchar('\n');
    print_hline('=' , (sizeof(label) / sizeof(label[0])) * FLDWIDTH - 4);

    // 打印数据
    for (i = 0 ; i < LIBRARY.COUNT; i++)
    {
        print_squared(LIBRARY.BOOKS[i].title , FLDWIDTH);
        print_squared(LIBRARY.BOOKS[i].author , FLDWIDTH);
        sprintf(tmp ,"%.1f" ,LIBRARY.BOOKS[i].value) ;
        print_squared(tmp , FLDWIDTH);
        putchar('\n');
        print_hline('-' , (sizeof(label) / sizeof(label[0])) * FLDWIDTH - 4);
    }
}

void lib_modify(void)
{
    book tbook ;
    char tstr[100] ;
    char answer ;
    int i ;
    float tfloat = 0 ;

    if (!LIBRARY.COUNT)
    {
        puts("书籍为空,不能修改");
        return ;
    }

    lib_out();
    puts("请输入您要修改的书名(输入空行退出):");
    while (s_gets(tstr , 100) && tstr[0])
    {
        for (i = 0 ; i < LIBRARY.COUNT ; i++)
        {
            if (strcmp(LIBRARY.BOOKS[i].title , tstr) == 0)
            {
                break;
            }
        }

        if (i > LIBRARY.COUNT - 1)
        {   //未找到书名给出提示
            puts("您输入的书名不存在!");
        }
        else
        {
            tbook = LIBRARY.BOOKS[i] ;

            printf("书名: %s\n无需修改请按[Enter]输入空行\n" , tbook.title);
            if (s_gets(tstr , 100) && tstr[0])
                strncpy(tbook.title , tstr , MAXTITL) ;

            printf("作者: %s\n无需修改请按[Enter]输入空行\n" , tbook.author);
            if (s_gets(tstr , 100) && tstr[0])
                strncpy(tbook.author , tstr , MAXAUTL);

            printf("价格: %.1f\n无需修改请按[Enter]输入空行\n" , tbook.value);
            while (!s_gets(tstr , 100) || !tstr[0] || (tfloat = atof(tstr)) <= 0)
            {
                if (!tstr[0])
                {
                    tfloat = tbook.value ;
                    break ;
                }
                else
                    puts("输入价格无效,请重新输入");
            }
            tbook.value = tfloat ;

            puts("=============================================");
            printf("书名: %s\n作者: %s\n价格: %.1f\n" , tbook.title , tbook.author , tbook.value);
            puts("您确定要修改?(y/n)");
            if (scanf(" %c" , &answer) && 'y' == tolower(answer))
            {
                LIBRARY.BOOKS[i] = tbook ;
                puts("修改成功!");
            }
            while (getchar() != '\n') ;
        }
        puts("请输入您要修改的下一本书名(空行退出)");
    }
}

void lib_add(void)
{
    book tbook = { 0} ;
    char ch ;
    char tmp[100] ;
    float tfloat = 0 ;

    puts("请输入您要添加的书名(输入空行退出)");
    while (s_gets(tbook.title , MAXTITL) && !tbook.title[0])
    {
        if (abort_operation())                      //放弃时都会进行提示
            return ;
        else
            puts("请输入您要添加的书名(输入空行退出)");
    }


    puts("请输入该书的作者,或者出版社(输入空行退出)");
    while (s_gets(tbook.author , MAXAUTL) && !tbook.author[0])
    {
        if (abort_operation())
            return ;
        else
            puts("请输入该书的作者,或者出版社(输入空行退出)");
    }

    puts("请输入该书的价格(输入空行退出)");
    while (!s_gets(tmp , 100) || !tmp[0] || (tfloat = atof(tmp)) <= 0)
    {
        if (!tmp[0])
        {
            if (abort_operation())
                return ;
            else
                puts("请输入该书的价格(输入空行退出)");
        }
        else
            puts("输入价格无效,请重新输入(输入空行退出)");
    }
    tbook.value = tfloat ;

    puts("================================");
    puts("以下是您要添加的书籍信息:");
    printf("书名: %s\n" , tbook.title);
    printf("作者: %s\n" , tbook.author);
    printf("价格: %.1f\n" , tbook.value);
    puts("确认添加吗?(y/n)");
    if(scanf(" %c" , &ch) && 'y' == tolower(ch))
    {
        puts("添加成功");

        LIBRARY.BOOKS = (book *)realloc(LIBRARY.BOOKS ,++LIBRARY.COUNT * sizeof(book)) ;
             //为新添加的书籍分配地址空间

        LIBRARY.BOOKS[LIBRARY.COUNT - 1] = tbook ;
             //LIBRARY.BOOKS[LIBRARY.COUNT - 1] :相当于指针数组,其中的每个元素都指向book结构体中的各类成员信息
             //将为book结构体中新添加的成员信息,赋值给对应的数组元素中
    }
}

void lib_del(void)
{
    char tmp_title[MAXTITL] ;
    int i ;
    int j;
    if (!LIBRARY.COUNT)
    {
        puts("书籍为空,不能删除");
        return ;
    }

    lib_out();
    puts("请输入您要删除的书名(空行退出)");
    while (s_gets(tmp_title ,MAXTITL) && tmp_title[0])
    {
        //寻找相同的书名
        for (i = 0 ; i < LIBRARY.COUNT ; i++)
        {
            if (strcmp(LIBRARY.BOOKS[i].title , tmp_title) == 0)
                break;
        }

        if (i > LIBRARY.COUNT - 1)
        {   //未找到书名给出提示
            puts("您输入的书名不存在!");
        }
        else
        {
            for ( j = i ; j < LIBRARY.COUNT ; j++)			//在后一本覆盖掉上一本的同时,就删除掉一个book结构体指针所指的堆区空间
                LIBRARY.BOOKS[j] = LIBRARY.BOOKS[j + 1] ;
                LIBRARY.BOOKS = (book *)realloc(LIBRARY.BOOKS , --LIBRARY.COUNT * sizeof(book));
                //去掉一个堆区的内存空间
            puts("删除成功!");
        }
        puts("请输入您要删除的书名(空行退出)");
    }
}

int abort_operation()
{
    int answer = 0 ;
    char ch ;

    puts("你确定要放弃本次操作,并返回菜单页面?(y/n)");
    if (scanf(" %c" , &ch) && 'y' == tolower(ch))
    {
        answer = 1 ;
    }
    else
    {
        answer = 0 ;
    }
    while (getchar() != '\n');

    return answer ;
}

char * s_gets(char * st , int n)
{
    char * find ;

    if (fgets(st , n , stdin))
    {
        if (find = strchr(st , '\n'))
            *find = '\0' ;
        else
            while (getchar() != '\n') ;
    }

    return st ;
}

14.8(1)-有BUG想找问题的可以到这里

//巨人航空公司的机群由12个座位的飞机组成.它每天飞行一个航班.根据下面的要求,编写一个座位预定程序
//a. 该程序使用一个内含12个结构的数组.每个结构中包括:一个成员表示座位编号.一个成员表示座位是否
//   已经被预定,一个成员表示预定人名,一个成员表示预定人的姓.
//b. 该程序显示下面的菜单:
//	To choose a function enter its letter label:
//	a) Show number of empty seats
//	b) Show list of emppty seats
//	c) Show alphabetical list of seats
//	d) Assign a customer to a seat assignment
//	e) Delete a seat assignment
//	f) Quit
//c. 该程序能成功执行上面给出的菜单.选择d)和e)要提示用户进行额外输入,每个选项都能让用户终止输入.
//d. 执行特定程序后,该程序再次显示菜单,除非用户选择f).

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>   //该头文件别忘加,否则使用malloc会报错


#define NAME_LEN  50
#define SEAT_COUNT  12

typedef struct
{
    int number;
    int selected;
    char name[NAME_LEN];
    char s_name[NAME_LEN];
}seat;

static seat plane_seat[SEAT_COUNT]; //全局静态变量中数据已清0;

int initialize_seat(seat * pstr , int n);
void print_menu(void);
char choice_menu(void);
int switch_menu(char choice, seat *pstr);
int empty_seat_count(int n, const seat * pstr);
int show_seat_list(int n, const seat * pstr);
int sort_seat_list(int n, seat * pstr );
int choice_seat(int n, seat * pstr );
int delte_seat(int n, seat * pstr );
char * s_gets(char * st , int n);



int main(void)
{

    seat plane_seat[SEAT_COUNT];
    initialize_seat(plane_seat, SEAT_COUNT);        //初始化结构体成员

    while(switch_menu(choice_menu(), plane_seat));

    return 0;
}

void eatline(void)
{
    while (getchar() != '\n')//处理掉不需要的字符
        continue;
}

int initialize_seat(seat *ps, int n)
{
    int i;
    for(i = 0; i < n; i++)
    {
        ps[i].number = i + 1;
        ps[i].selected = 0;
        memset(ps[i].name, '\0', sizeof(ps[i].name));
        memset(ps[i].s_name, '\0', sizeof(ps[i].s_name));
    }

    return (i == n ? i : 0);
}

void print_menu(void)
{
    static char *menu[] =
    {

        "To choose a function enter its letter label:",
        "a)  Show number of empty seats",
        "b)  Show list of empty seats",
        "c)  Show alphabetical list of seats",
        "d)  Assign a customer to a seat assignment",
        "e)  Delete a seat assignment",

    };
    int i;
    for(i = 0; i < sizeof(menu)/sizeof(menu[0]); i++)
    {
        puts(menu[i]);
    }

}

char gets_input(void)
{
    int ch;
    //char p[20];
    //p = (char)malloc(20 * sizeof(p));
    //ch = tolower(scanf("%c", &ch));
    ch = getchar();
    ch = tolower(ch);

    eatline();          // 清理输入行
    return ch;
}

char choice_menu(void)
{
    char ch;
    print_menu();
    while(gets_input())
    {
        if(strchr("abcde", ch) == NULL)
        {
            fputs("无效选择,请输入abcde.\n" , stderr);
        }
        else
        {
            fputs("输入正确~\n" , stderr);
        }
    }

    eatline();
    return ch;
    puts(ch);
}

int switch_menu(char choice, seat *pstr)
{
    switch (choice)
{
    case 'a' :
        //显示未预定的座位数量
        empty_seat_count(pstr , SEAT_COUNT);
        system("pause");
        return 1;
    case 'b' :
        //显示空的座位列表
        show_seat_list(pstr , SEAT_COUNT);
        system("pause");
        return 1 ;
    case 'c' :
        //按字母顺序显示座位列表
        sort_seat_list(pstr , SEAT_COUNT) ;
        system("pause");
        return 1 ;
    case 'd' :
        //为客户分配座位
        choice_seat(pstr , SEAT_COUNT);
        show_seat_list(pstr , SEAT_COUNT);
        system("pause");
        return 1 ;
    case 'e' :
        //删除座位分配
        delte_seat(pstr , SEAT_COUNT);
        show_seat_list(pstr , SEAT_COUNT);
        system("pause");
        return 1 ;
    case 'f' :
        //退出
        return 0 ;
    default :
        //错误选择
        puts("无效选择,请重新选择");
        system("pause");
        return 1 ;

    }

}

int empty_seat_count(int n, const seat *ps)
{
    puts("显示未预定的座位数量");
    int count = 0;
    int i;

    for(i = 0; i < n; i++)
    {
        if(ps[i].selected == 0)
        {
            count ++;
        }
    }
    printf("现在空座为还剩%d个\n" , count);

    return count;
}

int show_seat_list(int n, const seat *ps)
{
    int i;
    puts("显示空的座位列表");
    puts("当前座位信息:");

    for(i = 0; i < n; i++)
    {
        printf("座位号 :%-5d %-5d 名 :%-20s 姓:%-20s\n",ps[i].number,
               ps[i].selected ? "已预定" : "未预定",
               ps[i].name,
               ps[i].s_name);
    }

    return i;
}

int sort_seat_list(int n , seat *ps)
{
    puts("按字母顺序显示座位列表");
    seat **ps_arr;
    seat *tmp;
    int min;
    int i, j;
    ps_arr = (seat **)calloc(n, sizeof(seat *));

    for(i = 0; i < n; i++)
    {
        ps_arr[i] = &ps[i];
    }

    for(i = 0; i < n - 1; i++)
    {
        min = i;
        for(j = i+1; j < n; j++)
        {
            if(strcmp(ps_arr[min]->name, ps_arr[j]->name) > 0)
            {
                min = j;
            }

        }

        if(min != i)
        {
            tmp = ps_arr[i];
            ps_arr[i] = ps_arr[min];
            ps_arr[min] = tmp;
        }

    }
    for(i = 0; i < n; i++)
    {
        printf("座位号 :%-5d %-5d 名 :%-20s 姓:%-20s\n",ps[i].number,
               ps[i].selected ? "已预定" : "未预定",
               ps[i].name,
               ps[i].s_name);
    }


    free(ps_arr);
}

int choice_seat(int n, seat *ps)
{
    puts("为客户分配座位");
    seat t;
    char ch;
    int flag = 0;

    puts("请输入您要挑选的座位号:");
    while(!scanf(" %d", &t.number) || t.number < 1 || t.number > n \
          || (flag = ps[t.number -1].selected))
    {
        eatline();

        if(flag)
        {
            puts("此座位已被预定,请重新选择座位:");
            flag = 0 ;
        }
        else
        {
            puts("非法输入,请重新输入座位号:");
        }

    }
    eatline();
    puts("锁定此座位后,请输入姓名:");
    t.selected = 1;         //锁定此座位

    puts("请输入您的名:");
    while(!s_gets(t.name, NAME_LEN));
    {
        puts("非法输入,请重新输入您的名:");
    }
    puts("请输入您的姓:");
    while (!s_gets(t.s_name , NAME_LEN))
    {
        puts("非法输入,请重新输入您的姓:");
    }

    puts("======================================");
    printf("座位号: %d\n名:%s\n姓:%s\n" ,t.number, t.name, t.s_name);
    puts("确认预约此座吗?(y/n):");
    while(!(scanf(" %d"), &ch) || !isalpha(ch = tolower(ch)))
    {
        puts("非法输入,请重新输入");
    }
    if(ch == 'y')
    {
        ps[t.number -1] = t;
        return 1;
    }
    else
        return 0;
}

int delte_seat(int n, seat *pstr)
{
    int num;
    int flag = 0;
    //int i;
    char ch;

    puts("请输入您要删除的座位号:");
    while(!scanf(" %d", &num) || num < 1 || num > n \
                     || (flag = !pstr[num -1].selected))
    {
        eatline();
        if (flag)
        {
            puts("取消预约失败,此座位并未被预约");
            flag = 0 ;
        }
        else
        {
             puts("非法输入,请重新输入要取消预约的座位号:");
        }
    }
     eatline();

     puts("======================================");
     printf("座位号: %d\n名:%s\n姓:%s\n" , pstr[num - 1].number , pstr[num - 1].name , pstr[num - 1].s_name);

     puts("确认此座取消预约吗?(y/n):");
     while(!scanf(" %c", &ch) || !isalpha(ch = tolower(ch)))
     {
         ;
     }

     if(ch == 'y')
     {
         pstr[num - 1].selected = 0;
         pstr[num - 1].name[0] = '\0';
         pstr[num - 1].s_name[0] = '\0' ;
         return 1 ;
     }
     else
         return 0;

}


char * s_gets(char * st , int n)
{
    char * ret_value = NULL ;
    char * find ;

    if (ret_value = fgets(st , n , stdin))
    {
        if (find = (strchr(st , '\n')))
            *find = '\0' ;
        else
            while (getchar() != '\n') ;
    }

    return ret_value ;
}

14.8(2)已经解决问题版本

flag = pstr[tmp.number - 1].selected :其中

   1、tmp.number - 1表示 :我们输入的number ,按照数组下标理解–>数组下标从0开始排列,因此此处需要-1
   2、selected 是标记该位置有没有被选取的标志,1已经选取,0未选取
   3、flag判断位置有无预订
.
.

删除函数中flag = !pstr[num - 1].selected

   1、当selected = 0时,该位置未被预订,对其取反使得flag = 1,进入下面的"取消预约失败,此座位并未被预约"的判断语句中
   2、此处flag依旧是判断用
.
.
sort_seat_list(seat *ps, int n)函数中
1、 ps_arr = (seat **)calloc(n, sizeof(seat *)); //为ps_arr在堆区分配n个sizeof(seat *)大小的空间让其去指向
2、ps_arr[i] 就相当于指针数组,保存我们所定义的结构体指针ps_arr[i] = &ps[i]

//巨人航空公司的机群由12个座位的飞机组成.它每天飞行一个航班.根据下面的要求,编写一个座位预定程序
//a. 该程序使用一个内含12个结构的数组.每个结构中包括:一个成员表示座位编号.一个成员表示座位是否
//   已经被预定,一个成员表示预定人名,一个成员表示预定人的姓.
//b. 该程序显示下面的菜单:
//	To choose a function enter its letter label:
//	a) Show number of empty seats
//	b) Show list of emppty seats
//	c) Show alphabetical list of seats
//	d) Assign a customer to a seat assignment
//	e) Delete a seat assignment
//	f) Quit
//c. 该程序能成功执行上面给出的菜单.选择d)和e)要提示用户进行额外输入,每个选项都能让用户终止输入.
//d. 执行特定程序后,该程序再次显示菜单,除非用户选择f).

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>   //该头文件别忘加,否则使用malloc会报错


#define NAME_LEN  50
#define SEAT_COUNT  12

typedef struct
{
    int number;
    int selected;
    char name[NAME_LEN];
    char s_name[NAME_LEN];
}seat;

//static seat plane_seat[SEAT_COUNT]; //全局静态变量中数据已清0;

int initialize_seat(seat * pstr , int n);
void print_menu(void);
char choice_menu(void);
int switch_menu(int choice, seat *pstr);
int empty_seat_count(const seat * ps,int n);
int show_seat_list(const seat * ps,int n);
int sort_seat_list(seat * ps,int n);
int choice_seat(seat * ps,int n);
int delte_seat(seat * pstr,int n );
char * s_gets(char * st , int n);
//char gets_input(void);



int main(void)
{
    seat plane_seat[SEAT_COUNT] ;

    initialize_seat(plane_seat , SEAT_COUNT);			//初始化座位

    while (switch_menu(choice_menu() , plane_seat)) ;

    puts("Bye. See you next!");

    return 0 ;
}

void eatline(void)
{
    while (getchar() != '\n')//处理掉不需要的字符
        continue;
}

int initialize_seat(seat *ps, int n)
{
    int i;
    for(i = 0; i < n; i++)
    {
        ps[i].number = i + 1;
        ps[i].selected = 0;
        memset(ps[i].name, '\0', sizeof(ps[i].name));
        memset(ps[i].s_name, '\0', sizeof(ps[i].s_name));
    }

    return (i == n ? i : 0);
}

void print_menu(void)
{
    static char *menu[] =
    {

        "To choose a function enter its letter label:",
        "a)  Show number of empty seats",
        "b)  Show list of empty seats",
        "c)  Show alphabetical list of seats",
        "d)  Assign a customer to a seat assignment",
        "e)  Delete a seat assignment",
        "f) Quit"
    };
    int i;
    for(i = 0; i < sizeof(menu)/sizeof(menu[0]); i++)
    {
        puts(menu[i]);
    }

}

char choice_menu(void)
{
    char ch ;

    print_menu();
    while (!scanf(" %c" , &ch) || !isalpha(ch = tolower(ch)))
    {
        while (getchar() != '\n') ;
        puts("输入非法,请重新输入");
    }

    while (getchar() != '\n') ;

    return ch ;
}
 
int switch_menu(int choice , seat * pstr)
{
    switch (choice)
{
    case 'a' :
        //显示未预定的座位数量
        empty_seat_count(pstr , SEAT_COUNT);
        system("pause");
        return 1;
    case 'b' :
        //显示空的座位列表
        show_seat_list(pstr , SEAT_COUNT);
        system("pause");
        return 1 ;
    case 'c' :
        //按字母顺序显示座位列表
        sort_seat_list(pstr , SEAT_COUNT) ;
        system("pause");
        return 1 ;
    case 'd' :
        //为客户分配座位
        choice_seat(pstr , SEAT_COUNT);
        show_seat_list(pstr , SEAT_COUNT);
        system("pause");
        return 1 ;
    case 'e' :
        //删除座位分配
        delte_seat(pstr , SEAT_COUNT);
        show_seat_list(pstr , SEAT_COUNT);
        system("pause");
        return 1 ;
    case 'f' :
        //退出
        return 0 ;
    default :
        //错误选择
        puts("无效选择,请重新选择");
        system("pause");
        return 1 ;

    }

}

int empty_seat_count(const seat *ps, int n)
{
    puts("显示未预定的座位数量");
    int count = 0;
    int i;

    for(i = 0; i < n; i++)
    {
        if(ps[i].selected == 0)
        {
            count ++;
        }
    }
    printf("现在空座为还剩%d个\n" , count);

    return count;
}

int show_seat_list( const seat *ps, int n)
{
    int i;
    puts("显示空的座位列表");
    puts("当前座位信息:");

    for(i = 0; i < n; i++)
    {
        printf("座位号 :%-5d %-5d 名 :%-20s 姓:%-20s\n",ps[i].number,
               ps[i].selected ? "已预定" : "未预定",
               ps[i].name,
               ps[i].s_name);
    }

    return i;
}

int sort_seat_list(seat *ps, int n)
{
    puts("按字母顺序显示座位列表");
    seat **ps_arr;
    seat *tmp;
    int min;
    int i, j;
    ps_arr = (seat **)calloc(n, sizeof(seat *));        //为ps_arr在堆区分配n个sizeof(seat *)大小的空间让其去指向

    for(i = 0; i < n; i++)
    {
        ps_arr[i] = &ps[i];     //ps_arr相当于指针数组,保存有0-11个结构体指针
    }

    for(i = 0; i < n - 1; i++)
    {
        min = i;
        for(j = i+1; j < n; j++)
        {
            if(strcmp(ps_arr[min]->name, ps_arr[j]->name) > 0)
            {
                min = j;
            }

        }

        if(min != i)
        {
            tmp = ps_arr[i];
            ps_arr[i] = ps_arr[min];
            ps_arr[min] = tmp;
        }

    }
    for(i = 0; i < n; i++)
    {
        printf("座位号 :%-5d %-5s 名 :%-20s 姓:%-20s\n",ps_arr[i]->number,
               ps_arr[i]->selected ? "已预定" : "未预定",
               ps_arr[i]->name,
               ps_arr[i]->s_name);
    }


    free(ps_arr);
}

int choice_seat(seat *ps, int n)
{
    puts("为客户分配座位");
    seat t;
    char ch;
    int flag = 0;

    puts("请输入您要挑选的座位号:");
    while(!scanf(" %d", &t.number) || t.number < 1 || t.number > n \
          || (flag = ps[t.number -1].selected))
    {
        eatline();

        if(flag)             //selected==1 说明被预订
        {
            puts("此座位已被预定,请重新选择座位:");
            flag = 0 ;
        }
        else
        {
            puts("非法输入,请重新输入座位号:");
        }

    }
    eatline();
    puts("锁定此座位后,请输入姓名:");
    t.selected = 1;         //锁定此座位

    puts("请输入您的名:");
    while(!s_gets(t.name, NAME_LEN));
    {
        puts("非法输入,请重新输入您的名:");
    }
    puts("请输入您的姓:");
    while (!s_gets(t.s_name , NAME_LEN))
    {
        puts("非法输入,请重新输入您的姓:");
    }

    puts("================输入完毕的结构体成员属性======================");
    printf("座位号: %d\n名:%s\n姓:%s\n" ,t.number, t.name, t.s_name);
    puts("确认预约此座吗?(y/n):");
    while(!scanf(" %c", &ch) || !isalpha(ch = tolower(ch)))
    {
        puts("非法输入,请重新输入");
    }
    if(ch == 'y')
    {
        ps[t.number -1] = t;        //在为结构体中成员属性赋值完毕后,将其赋给结构体数组对应元素中
        return 1;
    }
    else
        return 0;
}

int delte_seat(seat *pstr, int n)
{
    int num;
    int flag = 0;
    //int i;
    char ch;

    puts("请输入您要删除的座位号:");
    while(!scanf(" %d", &num) || num < 1 || num > n \
                     || (flag = !pstr[num -1].selected))
    {
        eatline();
        if (flag)
        {
            puts("取消预约失败,此座位并未被预约");
            flag = 0 ;
        }
        else
        {
             puts("非法输入,请重新输入要取消预约的座位号:");
        }
    }
     eatline();

     puts("================删除结构体对应下标所属成员======================");
     printf("座位号: %d\n名:%s\n姓:%s\n" , pstr[num - 1].number , pstr[num - 1].name , pstr[num - 1].s_name);

     puts("确认此座取消预约吗?(y/n):");
     while(!scanf(" %c", &ch) || !isalpha(ch = tolower(ch)))
     {
         ;
     }

     if(ch == 'y')              //输入数字作为下标索引,找到对应结构体数组,将该元素下标对应的成员全部清零
     {
         pstr[num - 1].selected = 0;
         pstr[num - 1].name[0] = '\0';
         pstr[num - 1].s_name[0] = '\0' ;
         return 1 ;
     }
     else
         return 0;

}


char * s_gets(char * st , int n)
{
    char * ret_value = NULL ;
    char * find ;

    if (ret_value = fgets(st , n , stdin))
    {
        if (find = (strchr(st , '\n')))
            *find = '\0' ;
        else
            while (getchar() != '\n') ;
    }

    return ret_value ;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

bug

`choice_menu’函数的实现没有,只有声明在,添加上功能实现代码即可
在这里插入图片描述

14.9

一些小地方的理解

对清空函数initialize_airline()中,多维数组的理解

这里的多维数组,其实已经可以按照一个一维数组进行理解 :因为我们这里的“清零”,是针对座位而言,并不附加对航班的解释,所以单独对座位进行“清零”,只需要知道有多少个座位即可,一切按照一维数组首地址+数字去理解
(首地址+数字 :顺序指向其在数组中的位置)
在这里插入图片描述
在这里插入图片描述

//巨人航空公司的机群由12个座位的飞机组成.它每天飞行一个航班.根据下面的要求,编写一个座位预定程序
//a. 该程序使用一个内含12个结构的数组.每个结构中包括:一个成员表示座位编号.一个成员表示座位是否
//   已经被预定,一个成员表示预定人名,一个成员表示预定人的姓.
//b. 该程序显示下面的菜单:
//	To choose a function enter its letter label:
//	a) Show number of empty seats
//	b) Show list of emppty seats
//	c) Show alphabetical list of seats
//	d) Assign a customer to a seat assignment
//	e) Delete a seat assignment
//	f) Quit
//c. 该程序能成功执行上面给出的菜单.选择d)和e)要提示用户进行额外输入,每个选项都能让用户终止输入.
//d. 执行特定程序后,该程序再次显示菜单,除非用户选择f).

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>   //该头文件别忘加,否则使用malloc会报错


#define NAME_LEN  50
# define FLIGHT_COUNT 4
#define SEAT_COUNT  12

typedef struct
{
	int number;
	int selected;
	char name[NAME_LEN];
	char s_name[NAME_LEN];
}seat;

//static seat plane_seat[SEAT_COUNT]; //全局静态变量中数据已清0;

int initialize_airline(seat * pstr, int r, int c);
void print_menu(int n);
char choice_menu(int n);
int switch_menu(int choice, seat * pstr);
int empty_seat_count(const seat * pstr, int n);
int show_seat_list(const seat * pstr, int n);
int sort_seat_list(seat * pstr, int n);
int choice_seat(seat * pstr, int n);
int delte_seat(seat * pstr, int n);
char * s_gets(char * st, int n);
//void print_flight(void);
char choice_fight(void);
int flight_switch(char ch);
void eatline(void);



int main(void)
{
	seat airline[FLIGHT_COUNT][SEAT_COUNT];
	int num;

	initialize_airline(*airline, FLIGHT_COUNT, SEAT_COUNT);
		//多维数组清空函数

	while (num = flight_switch(choice_fight()))
	{
		if (num > 0)
			while (switch_menu(choice_menu(num - 1), airline[num - 1]));
	}

	puts("Bye. See you next!");

	return 0;
}

void eatline(void)
{
	while (getchar() != '\n')//处理掉不需要的字符
		continue;
}

//清空多维数组中成员属性
int initialize_airline(seat *pstr, int r, int c)
{
	int i;
	int j;
	for (i = 0; i < r; i++)
	{
		for (j = 0; j < c; j++)
		{
			(pstr + (i * c + j))->number = j + 1; 
				//j + 1 值:从第一号到第48号
			(pstr + (i * c + j))->selected = 0;
			(pstr + (i * c + j))->name[0] = '\0';
			(pstr + (i * c + j))->s_name[0] = '\0';
		}
	}

	return ((i * j) == (r * c) ? (i * j) : 0);
			//(i * j) :表示全部座位数
}

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

//飞机航班编号选定
char choice_fight(void)
{
	puts("飞机航班编号选定:");
	int ch;
	while (!scanf("%c", &ch) || !isalpha(ch))
	{
		eatline();
		puts("非法输入,请重新选择");
	}

	return tolower(ch);
}

//航班选择
int flight_switch(char ch)
{
	puts("开始飞机航班编号选择:");
	switch (ch)
	{
	case 'a':
		//厦航102
		return 1;  //num - 1 == 对应数组索引
		break;
	case 'b':
		//国航311
		return 2;
		break;
	case 'c':
		//马航444
		return 3;
		break;
	case 'd':
		//南航519
		return 4;
		break;
	case 'e':
		//退出
		return 0;
		break;
	default:
		//错误
		puts("无效输入,请重新输入一个字母:");
		system("pause");
		return -1;
		break;
	}
}

//航班已经选定,对可操作进行选择
char choice_menu(int n)
{
	int ch;
	print_menu(n);

	puts("航班已经选定,请选择相应的需求");
	while (!scanf("%c", &ch) || !isalpha(ch) || strchr("abcdef", ch) == NULL)
	{
		eatline();
		puts("输入非法,请重新输入");
	}
	puts("输入非法,请重新输入");

	return tolower(ch);
}

void print_menu(int n)
{
	static const char *menu[] = {
		"请选择所要执行的操作:",
		"a) 显示剩余未预定的座位数",
		"b) 显示座位列表",
		"c) 按首字母排序显示座位列表",
		"d) 预定座位",
		"e) 取消预定座位",
		"f) 返回上一级菜单"

	};

	static const char * flight[] = {
		"厦航102" , //0
		"国航311" ,
		"马航444" , //2
		"南航519" , 
	};
	int i;
	printf("您选择的是[%s]航班\n", flight[n]);
		   //此处选择了航班,在对-对应航班的座位进行操作,因此switch_menu中的功能函数中
		   //只需要接受一维数组地址以及12整形值
	puts("-------------------------------------------------");
	for (i = 0; i < sizeof(menu) / sizeof(menu[0]); i++)
	{
		puts(menu[i]);
	}
}


int switch_menu(int choice, seat * pstr)
{
	switch (choice)
	{
	case 'a':
		//显示未预定的座位数量
		empty_seat_count(pstr, SEAT_COUNT);
		system("pause");
		return 1;
	case 'b':
		//显示空的座位列表
		show_seat_list(pstr, SEAT_COUNT);
		system("pause");
		return 1;
	case 'c':
		//按字母顺序显示座位列表
		sort_seat_list(pstr, SEAT_COUNT);
		system("pause");
		return 1;
	case 'd':
		//为客户分配座位
		choice_seat(pstr, SEAT_COUNT);
		show_seat_list(pstr, SEAT_COUNT);
		system("pause");
		return 1;
	case 'e':
		//删除座位分配
		delte_seat(pstr, SEAT_COUNT);
		show_seat_list(pstr, SEAT_COUNT);
		system("pause");
		return 1;
	case 'f':
		//退出
		return 0;
	default:
		//错误选择
		puts("无效选择,请重新选择");
		system("pause");
		return 1;

	}

}

int empty_seat_count(const seat *pstr, int n)
{
	puts("显示未预定的座位数量");
	int count = 0;
	int i;

	for (i = 0; i < n; i++)
	{
		if (pstr[i].selected == 0)
		{
			count++;
		}
	}
	printf("现在空座为还剩%d个\n", count);

	return count;
}

int show_seat_list(const seat *pstr, int n)
{
	int i;
	puts("显示空的座位列表");
	puts("当前座位信息:");

	for (i = 0; i < n; i++)
	{
		printf("座位号 :%-5d %-5d 名 :%-20s 姓:%-20s\n", pstr[i].number,
			pstr[i].selected ? "已预定" : "未预定",
			pstr[i].name,
			pstr[i].s_name);
	}

	return i;
}

int sort_seat_list(seat *ps, int n)
{
	puts("按字母顺序显示座位列表");
	seat **ps_arr;
	seat *tmp;
	int min;
	int i, j;
	ps_arr = (seat **)calloc(n, sizeof(seat *));        
		//为ps_arr在堆区分配【n】个sizeof(seat *)大小的空间让其去指向
		//每个sizeof(seat *)就相当于结构体大小(所有成员在内)

	for (i = 0; i < n; i++)
	{
		ps_arr[i] = &ps[i];     
			//ps_arr相当于指针数组,保存有0-11个结构体指针(每个指针又都指向一个结构体)
	}

	for (i = 0; i < n - 1; i++)//冒泡法
	{
		min = i;
		for (j = i + 1; j < n; j++)
		{
			if (strcmp(ps_arr[min]->name, ps_arr[j]->name) > 0)
			{
				min = j;
			}

		}

		if (min != i)
		{
			//上诉的min = i是我们假定i是最小的,当i不是的时候,就把二者互换一下,使得min永远是最小
			tmp = ps_arr[i];
			ps_arr[i] = ps_arr[min];
			ps_arr[min] = tmp;
		}

	}

	for (i = 0; i < n; i++)
	{
		printf("座位号 :%-5d %-5s 名 :%-20s 姓:%-20s\n", 
			ps_arr[i]->number,
			ps_arr[i]->selected ? "已预定" : "未预定",
			ps_arr[i]->name,
			ps_arr[i]->s_name);
	}


	free(ps_arr);
}

int choice_seat(seat *ps, int n)
{
	puts("为客户分配座位");
	seat t;
	char ch;
	int flag = 0;

	puts("请输入您要挑选的座位号:");
	while (!scanf(" %d", &t.number) || t.number < 1 || t.number > n 
			|| (flag = ps[t.number - 1].selected))
	{
		eatline();

		if (flag)             //selected==1 说明被预订
		{
			puts("此座位已被预定,请重新选择座位:");
			flag = 0;
		}
		else
		{
			puts("非法输入,请重新输入座位号:");
		}

	}
	eatline();
	puts("锁定此座位后,请输入姓名:");
	t.selected = 1;         //锁定此座位

	puts("请输入您的名:");
	while (!s_gets(t.name, NAME_LEN));
	{
		puts("非法输入,请重新输入您的名:");
	}
	puts("请输入您的姓:");
	while (!s_gets(t.s_name, NAME_LEN))
	{
		puts("非法输入,请重新输入您的姓:");
	}

	puts("================输入完毕的结构体成员属性======================");
	printf("座位号: %d\n名:%s\n姓:%s\n", t.number, t.name, t.s_name);
	puts("确认预约此座吗?(y/n):");
	while (!scanf(" %c", &ch) || !isalpha(ch = tolower(ch)))
	{
		puts("非法输入,请重新输入");
	}
	if (ch == 'y')
	{
		ps[t.number - 1] = t;        //在为临时结构体中成员属性赋值完毕后,将其赋给结构体数组对应元素中
		return 1;
	}
	else
		return 0;
}

int delte_seat(seat *pstr, int n)
{
	int num;
	int flag = 0;
	//int i;
	char ch;

	puts("请输入您要删除的座位号:");
	while (!scanf(" %d", &num) || num < 1 || num > n 
		|| (flag = !pstr[num - 1].selected))
	{
		eatline();
		if (flag)
		{
			puts("取消预约失败,此座位并未被预约");
			flag = 0;
		}
		else
		{
			puts("非法输入,请重新输入要取消预约的座位号:");
		}
	}
	eatline();

	puts("================删除结构体对应下标所属成员======================");
	printf("座位号: %d\n名:%s\n姓:%s\n", pstr[num - 1].number, pstr[num - 1].name, pstr[num - 1].s_name);

	puts("确认此座取消预约吗?(y/n):");
	while (!scanf(" %c", &ch) || !isalpha(ch = tolower(ch)))
	{
		;
	}

	if (ch == 'y')              //输入数字作为下标索引,找到对应结构体数组,将该元素下标对应的成员全部清零
	{
		pstr[num - 1].selected = 0;
		pstr[num - 1].name[0] = '\0';
		pstr[num - 1].s_name[0] = '\0';
		return 1;
	}
	else
		return 0;

}


char * s_gets(char * st, int n)
{
	char * ret_value = NULL;
	char * find;

	if (ret_value = fgets(st, n, stdin))
	{
		if (find = (strchr(st, '\n')))
			*find = '\0';
		else
			while (getchar() != '\n');
	}

	return ret_value;
}

.
.

14.10

稍微要注意的是 :函数指针的形参、返回值,要和其所指向的函数对应上

# include <stdio.h>
# include <math.h>
# include <windows.h>

void print_menu(void);
int choice_meun(void);
void calculation(int cmd);
double fun_add(double x, double y);
double fun_sub(double x, double y);
double fun_mul(double x, double y);
double fun_div(double x, double y);
double get_number(void);


int main(void)
{
	int n;
	while ((n = choice_meun()))  //在条件中直接显示我们选择的数字
	{
		calculation(n - 1);  //而后把数字作为形参,值传递给可以实现函数指针的功能函数实现中
	}

	return 0;

}

int choice_meun(void)
{
	int num;
	int i;

	print_menu();
	for (i = 0; !scanf("%d", &num) || num < 1 || num > 5; i++) //中间的表达式应该是作为判断使用,也就是可循环次数
	{
		while (getchar() != '\n');

		if((i+1) % 10 == 0)
			print_menu();
		puts("无效输入,请输入整数");
	}

	while (getchar() != '\n');
	return (num == 5 ? 0 : num);		//5就推出,不是就返回我们选择的数字对应的功能
}

void print_menu(void)
{
	static const char* menu[] =
	{
		"======================================" ,
		"请选择您需要的功能:" ,
		"1 加			2 减" ,
		"3 乘			4 除" ,
		"5 退出" ,
		"======================================"
	};

	system("cls");
	int i;
	printf("menu = %d", sizeof(menu) / sizeof(menu[0]));
	for (i = 0; i < sizeof(menu)/sizeof(menu[0]); i++)
	{
		puts(menu[i]);
	}
}

void calculation(int cmd)	//cmd接受的(n - 1)数字
{

	double  n1, n2;
	double(*f[4])(double x, double y) =
	{ fun_add, fun_sub , fun_mul , fun_div };

	puts("请输入您要计算的第一个数:");
	n1 = get_number();
	puts("请输入您要计算的第二个数:");
	n2 = get_number();

	printf("计算结果为: %.2lf\n", f[cmd](n1, n2));

	system("pause");
}

double fun_add(double x, double y)
{
	double sum = 0;
	sum = x + y;
	return sum;
}

double fun_sub(double x, double y)
{
	double results = 0;
	results = x - y;
	return results;
}
double fun_mul(double x, double y)
{
	double results = 0;

	results = x * y;

	return results;
}
double fun_div(double x, double y)
{
	double results = 0;
	if (y == 0)
	{
		fputs("参数错误 :除数不能是0\n", stderr);
	}
	else
		results = x / y;

	return results;
}

double get_number(void)
{
	double x;
	while (!scanf(" %lf", &x))
	{
		while (getchar() != '\n');
		puts("无效输入,请输入正确的数字");
	}

	while (getchar() != '\n');
	return x;
}

在这里插入图片描述
.
.
.

14.11

# include <stdio.h>
# include <windows.h>
# include <math.h>

int transform(double *source, double *traget, int n, double(*pfun)(double x));
void print_results(const double*source, int n);

int main(void)
{
	double source[4] = { 1, 2, 3, 4 };
	double traget[4] = { 0 };

	transform(source, traget, 4, sin);
	puts("source :");
	print_results(source, 4);
	puts("traget :");
	print_results(traget, 4);

	return 0;
}

int transform(double *source, double *traget, int n, double(*pfun)(double x))
{
	int i = 0;
	for (i; i < n; i++)
	{
		traget[i] = pfun(source[i]);
	}

	return i;
}

void print_results(const double *source, int n)
{
	int i = 0;
	for (i; i < n; i++)
	{
		printf(" %lf", source[i]);
	}
	putchar('\n');
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

扳手的海角

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值