1.DEVC建工程
1.1 新建項目
打開文件,選擇新建-->項目-->ConsoleApplication(控制台程序),輸入項目名,選擇保存路徑。(單獨建一個文件夾存放)
項目建成功后,打開項目管理,看到自動生成的一個main文件。注意每個項目只能有一個main函數,是系統的入口。默認main.cpp已經包含main函數,其他文件不能再有main函數。
1.2 工程中新建c文件
項目管理中,右擊項目名,選擇New File,則可以在項目新建一個c或頭文件。比如新建頭文件,可以頭文件寫結構體定義、函數聲明內容后,選擇保存類型為*.h即可生成頭文件
項目管理中多了自定義頭文件student.h。其他c文件生成一樣操作。
我們會生成了項目,但是如何把多個文件用項目管理一起呢,現在以學生管理系統為例。
2.多文件的C語言系統示例--學生成績管理
示例功能比較簡單,主要實現功能:
- 讀取學生文件數據
- 修改學生成績
- 控制台上展示學生數據
- 保存數據到文件中
設計3個文件實現該系統。
- 頭文件student.h:封裝結構體定義、函數聲明
- blog.c:函數具體實現,包括文件讀取、數據展示等功能
- main.c:實現菜單操作,功能函數調用
3個文件實現不同功能。函數定義和函數實現分2個文件實現。更好封裝數據類型。main文件主要做函數調用。
文件數據:blog_score.txt
具體數據,如圖所示:
看到上述數據,每行數據代表一個學生記錄,故可以用結構體封裝,具體見2.1節介紹。
2.1 頭文件student.h
頭文件主要結構體定義、函數聲明。也就是數據表達這塊,可以寫在頭文件中。不要在頭文件定義變量,因為其他c文件也要包含頭文件,如果多次包含,容易出現變量重定義。如學生系統頭文件如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct{
char stuNum[13]; //學號
char stuName[8];//姓名
double score[8];//成績
}STU;//定義結構體類型
int ReadSTUFile(FILE *fp,STU stu[]); //讀取文件數據
void PrintSTU(STU stu[],int total);//展示數據,total表示學生數量
void UpdateSTU(STU stu[],int total);//修改成績
int FindSTU(STU *stuPtr,int total,const char *stuName,int *loc);//按學號查找數據
void SaveSTUFile(STU *stuPtr,int total,FILE *fp);//保存數據到文件
建議:如果你的系統功能比較復雜,而且讀取又是多個文件,每個文件代表不同類型數據,可以每種數據類型封裝一個自定義頭文件。這有點像面向對象類的概念,頭文件就像類的接口,把函數具體實現封裝了,只展示函數的接口。
2.2 函數實現文件blog.c
在頭文件中定義函數,其實現盡量封裝在一個獨立c文件中,后續要修改,只要找對應c文件修改即可,這樣避免我們的主文件main.c過長。具體實現函數為:
- 讀取文件數據函數
- 修改結構體數據
- 查找數據
- 寫入數據到文件
#include "student.h"
int ReadSTUFile(FILE *fp,STU stu[])
{
int i=0,j;
while(!feof(fp))
{
fscanf(fp,"%s%s",stu[i].stuNum,stu[i].stuName);
for(j=0;j<3;j++) fscanf(fp,"%lf",&stu[i].score[j]);
i++;
}
return i;
}
void UpdateSTU(STU stu[],int total)
{
char stuName[20];//存放更改學號
int courseID;//更改課程號,從1開始編號,數組編號從0開始
double score;//更改成績
int loc;//記錄在數組編號
printf("輸入更改學號:");
scanf("%s",stuName);
if(FindSTU(stu,total,stuName,&loc))//找到
{
printf("輸入修改課程號:");
scanf("%d",&courseID);
printf("輸入修改成績:");
scanf("%lf",&score);
stu[loc].score[courseID-1]=score;//修改結構體中對應數據。
}
else
{
printf("\n沒有該學生!\n");
}
}
int FindSTU(STU *stuPtr,int total,const char *stuNum,int *loc)
{
int i=0,j;
for(i=0;i<total;i++,stuPtr++)
{
if(strcmp(stuPtr->stuNum,stuNum)==0) //找到記錄
{
*loc=i;//記住找到的記錄編號
return 1;
}
}
return 0;
}
void PrintSTU(STU stu[],int total)
{
int i=0,j;
for(i=0;i<total;i++)
{
printf("%s %s",stu[i].stuNum,stu[i].stuName);
for(j=0;j<3;j++)
printf(" %lf ",stu[i].score[j]);
printf("\n");
}
}
void SaveSTUFile(STU *stuPtr,int total,FILE *fp)
{
int i=0,j;
rewind(fp);//文件前面讀后,在文件尾,所以得重新定位文件指針。
for(i=0;i<total;i++,stuPtr++)
{
fprintf(fp,"%s %s",stuPtr->stuNum,stuPtr->stuName);
for(j=0;j<3;j++) fprintf(fp," %.1f ",stuPtr->score[j]);
if(i<total-1) fprintf(fp,"%s","\n"); // 最后一行不寫空行
}
printf("\n文件保存成功!!\n");
}
這里注意就是:
- blog.c文件中用到結構體類型及一些庫函數,所以這個文件需要把自定義頭文件包含進來。
#include "student.h"
- 上述文件讀取是用
fscanf
函數,不過對於結構體數據的增刪改,還是用隨機文件訪問更快,建議用fread和fwrite
讀寫數據。 - 這個文件里面可以繼續擴展多學生數據增刪改實現函數。
- 感謝林穎賢老師的指正,原
SaveSTUFile
文件里面多寫了換行符,導致文件再次讀出,最后一行是0。
2.3 主函數文件main.c
主要實現分散在各個c文件中的函數調用及一些主要數組、全局變量定義。
#include "student.h"
void Menu();
int main(void)
{
FILE *fp;
int sel;//輸入選擇菜單操作
STU students[100];
int total;
if((fp=fopen("blog_score.txt","r+")) == NULL)
{
printf("File open error!\n"); exit(0);
}
total=ReadSTUFile(fp,students);//讀文件
while(1)
{
Menu();
scanf("%d",&sel);
if(sel==0) break;
switch(sel)
{
case 1:UpdateSTU(students,total);break;//修改成績
case 2: PrintSTU(students,total); break;//展示成績
case 3:SaveSTUFile(students,total,fp);break;//保存成績
}
system("pause");//dos命令暫停
system("cls");//dos命令清屏
}
fclose(fp);
return 0;
}
void Menu()
{
printf("1:修改成績\n");
printf("2:展示成績\n");
printf("3:保存成績\n");
printf("0:退出\n");
printf("\n輸入操作項:");
}
main文件一樣要包含#include "student.h"
,數組不建議設為全局數組,雖然多個函數共享,但是由於多文件,容易出錯。建議用數組地址做函數參數傳遞,效率更高。注意點:
- 菜單單獨封裝一個函數,確實比較好用,如果需要修改菜單選項,只要Menu函數修改即可。這個學習班上同學做法。確實不錯。
- 保存文件單獨一個菜單,提示用戶注意數據保存。也把文件寫入單獨一個函數去完成。
- 文件打開方式用
w+
,數據會被清空,所以用'r+',才能覆蓋,重新寫入。 - 菜單加了清屏操作,可視化更好!
至此,項目所有文件創建完畢,可以編譯運行測試。
3.小結
綜合設計中,一般我們是給一個文件數據,如:
- 學會用C語法對文件數據的表達,即如何保存數據。可以定義結構體表示每條數據。
- 學會文件中數據讀取,存入結構體數組,如上述的
int ReadSTUFile(FILE *fp,STU stu[]);
函數 - 學會在結構體數組增刪改、查找、排序操作。可參考浙大版C教材P255內容,或上述函數
void UpdateSTU(STU stu[],int total);//修改成績
int FindSTU(STU *stuPtr,int total,const char *stuName,int *loc);//按學號查找數據
- 學會結構體數據寫入文件操作。如
void SaveSTUFile(STU *stuPtr,int total,FILE *fp)
4.項目文件容易出現問題
-
自定義頭文件定義變量,其他文件又都包含頭文件,導致變量多次定義,如:
-
自定義頭文件中聲明的函數原型名稱和main函數調用中函數名稱不一樣,或者main中要調用函數,沒有函數實現,則會出現:
undefined reference to 函數
-
待補充。。