C語言進階-第15~16講:結構體應用(學生成績統計)

任務和代碼:

        學生成績統計:

        每位同學的信息學號、姓名、C、高數、英語成績。定義一個學生成績的結構體數組,其中的數據成員包括學號(char num[13])、姓名(name)、三門課的成績(grade)、總分(score))

         score.txt中是一些同學的學號、姓名、C++、高數、英語成績,利用前面定義的結構體數組,讀取文件score.txt中的數據,完成下面的應用:

  • 從文件中讀出數據,存放到結構體數組中;
  • 求出每名同學的總分(可以在讀入過程中“順便”計算);
  • 按總分降序排序並輸出成績單;
  • 按學號升序排序並輸出成績單;
  • 輸出C語言不及格的同學姓名和C語言成績;
  • 有30名同學可以獲得獎學金,規則是總分高者優先,有掛科不能得獎學金。請輸出可以得獎學金同學的名單。若符合得將的最後一名同學有重複(例如總分全爲S),則總分爲S的同學全得獎;
  • 將程序用一個“菜單”組織起來,做成一個“學生成績管理系統”之類的應用程序;
  • 所有功能在main()函數中實現,並用多文件組織完成。
 
//head.h
#ifndef HEAD_H_INCLUDED
#define HEAD_H_INCLUDED

typedef struct {
   char num[13];
   char name[10];
   int c;
   int math;
   int english;
   int score;
}Student;

Student stu[500];
int stuNum;

void readData();
int chooseInMenu();
void sort_grade(Student [],int);
void sort_num(Student [],int);
void output(Student [],int);
void outputExcellent(Student [],int);
void outputImpassC(Student [],int);

#endif // HEAD_H_INCLUDED        
//main.c
#include <stdio.h>
#include <stdlib.h>
#include "head.h"
int stuNum=0;
int main(){
    int iChoice,i;
    readData();
    Student stud[stuNum];                //因爲Student stu[]已經是一個全局變量了,故
    for(i=0;i<stuNum;i++)                //將按學號升序排序後的學生名單放入一個新定義結構體數組
        stud[i]=stu[i];
    printf("歡迎進入學生成績管理系統!\n");
    do{
        iChoice = chooseInMenu();        //從菜單中獲得功能代碼
        switch(iChoice){
        case 1:
            sort_grade(stu,stuNum);      //按總分排序並輸出成績單
            output(stu,stuNum);
            break;
        case 2:
            sort_num(stud,stuNum);       //按學號排序並輸出成績單
            output(stud,stuNum);
            break;
        case 3:
            outputExcellent(stu,stuNum); //輸出獲得獎學金同學的名單
            break;
        case 4:
            outputImpassC(stu,stuNum);   //輸出C語言不及格的同學姓名和C語言成績
            break;
        case 0:
            printf("結束. \n");
        }
    }while(iChoice);
    return 0;
}
//chooseInMenu.c
#include <stdio.h>
#include <stdlib.h>
#include "head.h"
int chooseInMenu(){
    int i;
    while(1){
        printf("1. 按總分降序並輸出成績單\n");
        printf("2. 按學號升序並輸出成績單\n");
        printf("3. 輸出獲得獎學金同學的名單\n");
        printf("4. 輸出C語言不及格的同學姓名和C語言成績\n");
        printf("0. 結束\n");
        printf("請選擇(0-4):");
        scanf("%d", &i);
        if(i>=0&&i<=4)
            break;
        else
            printf("請重新選擇\n");
    }
    return i;
}
//output.c
#include <stdio.h>
#include <stdlib.h>
#include "head.h"
void output(Student s[],int n){
    printf("學號\t姓名\t總分\n");
    int i;
    for(i=0;i<n;i++)
        printf("%s\t%s\t%d\n",s[i].num,s[i].name,s[i].score);
    printf("\n");
}
//outputExcellent.c
#include <stdio.h>
#include <stdlib.h>
#include "head.h"
void outputExcellent(Student s[],int n){
    FILE *fp;
    if ((fp=fopen("scholarship.txt", "w"))==NULL){
        printf("scholarship.txt cannot be opened!");
        exit(0);
    }
    int i,j,m=0,k=0;
    sort_grade(s,n);
    for(i=0,j=0;i<n;i++){
        if(s[i].c>60&&s[i].math>60&&s[i].english>60)
            s[j++]=s[i];
        if(j==30)
            break;
    }
    for(j=30;i<n;i++){
        if(s[i].c>60&&s[i].math>60&&s[i].english>60&&s[i].score==s[j-1].score&&s[i].name!=s[j-1].name)
            s[j++]=s[i];
    }
    for(i=0;i<j;i++){
        printf("%s\t",s[i].name);
        m++;
        if(m%5==0)
            printf("\n");
    }
    printf("\n\n");
    for(i=0;i<j;i++){
        fprintf(fp,"%s\t",s[i].name);
        k++;
        if(k%5==0)
            fprintf(fp,"\n");
    }
}
//outputImpassC.c
#include <stdio.h>
#include <stdlib.h>
#include "head.h"
void outputImpassC(Student s[],int n){
    FILE *fp;
    if ((fp=fopen("impassC.txt", "w"))==NULL){
        printf("impassC.txt cannot be opened!");
        exit(0);
    }
    int i,m=0;
    for(i=0;i<n;i++){
        if(s[i].c<60){
            printf("%s\t%d\t\t",s[i].name,s[i].c);
            m++;
            if(m%3==0)
                printf("\n");
        }
    }
    printf("\n\n");
    for(i=0;i<n;i++){
        if(s[i].c<60){
            fprintf(fp,"%s\t%d\t\t",s[i].name,s[i].c);
            m++;
            if(m%3==0)
                fprintf(fp,"\n");
        }
    }
}
//readData.c
#include <stdio.h>
#include <stdlib.h>
#include "head.h"
//extern int stuNum;
void readData(){
    FILE *fp;
    fp = fopen("score.txt","r");
    if(fp==NULL){
        printf("dictionary open error!\n");
        exit(1);
    }
    fscanf(fp,"%s%s%d%d%d",stu[stuNum].num,stu[stuNum].name,&stu[stuNum].c,&stu[stuNum].math,&stu[stuNum].english);
    while (!feof(fp)){
            stu[stuNum].score=stu[stuNum].math+stu[stuNum].english+stu[stuNum].c;
            ++stuNum;
            fscanf(fp,"%s%s%d%d%d",stu[stuNum].num,stu[stuNum].name,&stu[stuNum].c,&stu[stuNum].math,&stu[stuNum].english);
    }
    fclose(fp);
}
//sort_grade.c
#include <stdio.h>
#include <stdlib.h>
#include "head.h"
void sort_grade(Student s[],int n){
    int i,j;
    Student t;
        for(i=0;i<n-1;i++)
            for(j=0;j<n-i-1;j++)
                if(s[j].score<s[j+1].score){
                    t=s[j];
                    s[j]=s[j+1];
                    s[j+1]=t;
            }
    FILE *fp;
    if ((fp=fopen("roster1.txt", "w"))==NULL){
        printf("roster1.txt cannot be opened!");
        exit(0);
    }
    for(i=0; i<n; i++)
        fprintf(fp,"%s\t%s\t%d\n",s[i].num,s[i].name,s[i].score);
    fclose(fp);
}
//sort_num.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "head.h"
void sort_num(Student s[],int n){
    int i,j;
    Student t;
        for(i=0;i<n-1;i++)
            for(j=0;j<n-i-1;j++)
                if(strcmp(s[j].num,s[j+1].num)==1){  //注意字符串的比較
                    t=s[j];
                    s[j]=s[j+1];
                    s[j+1]=t;
            }
    FILE *fp;
    if ((fp=fopen("roster2.txt", "w"))==NULL){
        printf("roster2.txt cannot be opened!");
        exit(0);
    }
    for(i=0; i<n; i++)
        fprintf(fp,"%s\t%s\t%d\n",s[i].num,s[i].name,s[i].score);
    fclose(fp);
}


運行結果:



按成績排序的成績單按學號排序的成績單獎學金名單C未通過的名單(含成績)


遇到的問題:

        疑惑一:在readData.c中,原來的掃描語句是下面這樣的,結尾爲什麼會多出一個0來呢?

while (!feof(fp)){
        fscanf(fp,"%s%s%d%d%d",stu[stuNum].num,stu[stuNum].name,&stu[stuNum].c,&stu[stuNum].math,&stu[stuNum].english);
        stu[stuNum].score=stu[stuNum].math+stu[stuNum].english+stu[stuNum].c;
        ++stuNum;
 }
                      對比正確的:

fscanf(fp,"%s%s%d%d%d",stu[stuNum].num,stu[stuNum].name,&stu[stuNum].c,&stu[stuNum].math,&stu[stuNum].english);
    while (!feof(fp)){
        stu[stuNum].score=stu[stuNum].math+stu[stuNum].english+stu[stuNum].c;
        ++stuNum;
        fscanf(fp,"%s%s%d%d%d",stu[stuNum].num,stu[stuNum].name,&stu[stuNum].c,&stu[stuNum].math,&stu[stuNum].english);
    }

        疑惑二:在outputExcellent.c中,相同的程序一個打印到窗口,一個打印到文件中,爲什麼結果有點不同呢?

for(i=0;i<j;i++){
        printf("%s\t",s[i].name);
        m++;
        if(m%5==0)
            printf("\n");
    }
    printf("\n\n");
    for(i=0;i<j;i++){
        fprintf(fp,"%s\t",s[i].name);
        m++;
        if(m%5==0)
            fprintf(fp,"\n");
    }

         疑惑三:原來的這個成績管理的程序放在一個源程序中能夠正常運行,在一個程序,多個文件處理的時候,爲什麼會編譯不通過呢?

                       
                       
                        stuNum是全局變量,在頭文件中原來的定義如下:       
//head.h
#ifndef HEAD_H_INCLUDED
#define HEAD_H_INCLUDED

typedef struct {
   char num[13];
   char name[10];
   int c;
   int math;
   int english;
   int score;
}Student;

Student stu[500];
int stuNum=0;

void readData();
int chooseInMenu();
void sort_grade(Student [],int);
void sort_num(Student [],int);
void output(Student [],int);
void outputExcellent(Student [],int);
void outputImpassC(Student [],int);

#endif // HEAD_H_INCLUDED


對應的解決方案:

     1)掃描文件,結尾爲什麼會多出一個0?

答:這個問題正是讀文件使用feof()多讀一次的問題,對於feof()這個函數, 它是先讀再判斷是否到文件尾,
       也就是說在它之前一定要讀一次才能做出判斷。我們會這樣把它使用在循環中:
       對於第一個代碼,當我文件指針位於最後一個數據末尾,fp還是爲真,所以繼續讀入,

       兩個%s都讀入爲空,到了文件結尾就結束讀入。輸出0,是因爲多輸入了一行,數組時全局變量裏面默認的元素是0;
       對於第二個代碼,將掃描語句放置在輸出語句之後,當讀到最後一個數據時,也進行了一次掃描,但不再進入循環體輸出了。


      2)相同的程序一個打印到窗口,一個打印到文件中,爲什麼結果有點不同呢?

 答:仔細看上面的代碼,是放在同一個源文件中的,在第二個格式輸出時,m作爲前一個遍歷的變量又作爲第二個遍歷的變量,

        顯然存在問題。故,一些用於遍歷的變量,計數器,有時候使用要歸零。


      3)這個成績管理的程序放在一個源程序中能夠正常運行,在一個程序,多個文件處理的時候,爲什麼會編譯不通過呢?

答:int stuNum=0;作爲全局變量在一個程序一個源文件中運行沒有問題,

       但是在一個程序多個源文件中,頭文件是被包含在多個源程序中的,就不能在頭文件中直接給全局變量賦初值。

       修改方式有以下來兩種:
       修改一:
       刪除頭文件裏被賦初值的全局變量,剩下的就是如何不在頭文件中仍能在多個源文件中定義全局變量,

       答案就是在main函數所在源文件定義所需要的全局變量,在其他文件中用extern指定外部變量
       修改二:
       保留頭文件中的全局變量但不要賦初值,在main函數所在的源文件給該全局變量賦初值,
       注意該變量不能放在main函數裏,否則就成了局部變量,使外部變量無效。

總結:

         1)遇到while(!feof(fp))用fscanf掃描文件時,循環體內的掃描語句要放在輸出語句之後;更多

         2)一些用於遍歷的變量,再次使用時要歸零;

         3)多文件處理時不要在頭文件裏給全局變量賦初值。

發佈了94 篇原創文章 · 獲贊 44 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章