介绍

  • 软件方面采用的是 Visual Studio 2019 IDE 工具。
  • 程序方面采用结构体数组,成员包括学号、姓名以及一科成绩。程序整体采用分模块编程形式,包括主函数在内共10个函数,实现功能有信息的录入、浏览、查询、修改、删除、数据保存到文件以及成绩的排序。程序比较简单,结构体成员只有三个,没有什么技术含量,后期看看有没有能力用链表来编写这个程序。

代码

  • 结构体数组的定义

函数开始前,是结构体数组的定义。这里直接使用typedef定义结构体数组,一步到位。后续可用STUDENT来进行变量声明了


  • home_page() 函数

这个是主界面的显示函数,完整的声明为:void home_page(),无形参,无返回值,单纯用printf()函数打印的界面。
完整代码

void home_page()
{
	printf("\n\t\t******************************** \n");
	printf("\t\t     学生成绩信息管理系统       \n\n");
	printf("\t\t        1、录入信息            \n\n");
	printf("\t\t        2、浏览信息            \n\n");
	printf("\t\t        3、查询信息            \n\n");
	printf("\t\t        4、修改信息            \n\n");
	printf("\t\t        5、成绩排序            \n\n");
	printf("\t\t        6、删除信息            \n\n");
	printf("\t\t        7、向文件写入数据       \n\n");
	printf("\t\t        8、从文件读出数据       \n\n");
	printf("\t\t        9、退出系统            \n\n");
	printf("\t\t******************************** \n\n");
}

运行效果
在这里插入图片描述
其中,“输入标号选择功能:”字样是在 main() 函数中调用 home_page() 函数后,打印的提示语。


  • add_infor() 函数

这是实现信息录入功能的函数,完整的函数声明是:int add_infor(STUDENT stu[], int n),形参为结构体数组stu[]和结构体数组个数n,返回值n+i是添加完信息后的结构体数组的总个数。函数内部,i变量是每次添加的信息的个数,sign变量作为连续输入的标志。这里判断是否连续输入,我原来用的方法是判断stu[n+i].num的值是否为-1,编译成功没有报错,但是实际运行过程中会出现一些问题,应该是逻辑上的问题。我暂且换成每输入一条信息后,判断是否继续输入第二条信息,实际使用时会有一些繁琐,但总体效果相差无几。
完整代码

int add_infor(STUDENT stu[], int n)
{
	int i = 0;
	char sign = 'Y';

	while ((sign == 'Y') || (sign == 'y'))
	{
		printf("\n\t\t\t学号:");
		scanf("\t\t%s", stu[n + i].num);
		printf("\t\t\t姓名:");
		scanf("\t\t%s", stu[n + i].name);
		printf("\t\t\t成绩:");
		scanf("\t\t%f", &stu[n + i].score);

		printf("\n\t\t\t是否继续录入?(Y/N):");
		sign = getchar();

		i++;
	}

	return (n + i);
}

上述代码在 Visual Studio 2019 IDE 中编译时,scanf()函数会报出警告,警告代码为:C6031,说明为:返回值被忽略。解决方法有两个,第一种是将scanf替换为scanf_s,我没有采用这种方法,因为scanf_s并不是 C 语言中的,而是 Visual Studio 开发工具提供的一个功能相同的安全标准输入函数,如果换为scanf_s,在另一个编译器下就可能会出问题;第二种是使用#define _CRT_SECURE_NO_WARNINGS 1,具体做法可百度查找,经实测,这种方法是可行的。但是,如果两种方法都不采取,即不消除这个警告,实际上对程序的正确运行也没有影响。
运行效果
在这里插入图片描述
“录入学生成绩信息”字样由 main() 函数在调用 add_infor() 函数前打印


  • browse_infor() 函数

这是实现浏览录入信息功能的函数,完整声明为:void browse_infor(STUDENT stu[], int n),形参为结构体数组stu[]和结构体数组个数n,无返回值。函数内部,i是作为循环控制变量,传入数组个数n,然后通过 for 循环,依次打印出结构体成员的值。
完整代码

void browse_infor(STUDENT stu[], int n)
{
	int i;

	printf("\n\n");
	printf("\t\t学号\t姓名\t成绩\n\n");

	for (i = 0; i < n; i++)
	{
		printf("\t\t%s\t%s\t %3.1f\n",stu[i].num,stu[i].name,stu[i].score);
	}
}

运行效果
在这里插入图片描述
“浏览学生成绩信息”字样由 main() 函数在调用 browse_infor() 函数前打印


  • find_infor() 函数

这是实现信息查找功能的函数,完整的声明为:void find_infor(STUDENT stu[], int n),形参为结构体数组stu[]和结构体数组个数n,无返回值。函数内部,又分为通过学号查找和通过姓名查找两种方式,变量option存放键入的选项,两个字符串数组分别存放查找的学号和姓名。先选择查找方式,输入对应方式的值,然后遍历数组,通过strcmp()函数判断数组中是否有与输入相同的值,若无相同的值则显示“未找到!”的信息,若有相同值则将该结构体成员的信息打印到屏幕上。
完整代码

void find_infor(STUDENT stu[], int n)
{
	int option;
	int i = 0;
	char search_num[10] = { 0 };
	char search_name[20] = { 0 };

	printf("\n\t\t\t1、按学号查找\n");
	printf("\t\t\t2、按姓名查找\n");
	printf("\n\t\t\t请选择相应的选项:");
	scanf("%d",&option);

	system("cls");

	if (1 == option)
	{
		printf("\n\t\t请输入要查找的学号:");
		scanf("%s", search_num);

		while ((strcmp(stu[i].num, search_num) != 0) && i < n)
		{
			i++;
		}
	}

	if (2 == option)
	{
		printf("\n\t\t请输入要查找的姓名:");
		scanf("%s", search_name);

		while ((strcmp(stu[i].name, search_name) != 0) && i < n)
		{
			i++;
		}
	}

	if (i == n)
	{
		printf("\n\t\t未找到!");
		return;
	}

	printf("\n\t\t学号:%s\n", stu[i].num);
	printf("\t\t姓名:%s\n", stu[i].name);
	printf("\t\t成绩:%3.1f\n", stu[i].score);
}

从上述代码中可以看到,在定义两个字符串数组时,均赋了初值0,若此处没有赋初值0时,上段代码在 Visual Studio 2019 IDE 中编译时,strcmp()函数会报警告,警告代码为:C6054,说明为:可能没有为字符串“xxx”添加字符串零终止符。深入了解strcmp()函数可以知道,其功能是两个字符串自左向右逐个字符相比(按 ASCII 值大小相比较),直到出现不同的字符或遇 ‘\0’ 为止。所以此处在定义字符串数组时,将其初始化为 ‘\0’ ,即char search_num[10] = { 0 };,就可以消除此类警告。
运行效果
在这里插入图片描述


  • modify_infor() 函数

这是实现信息修改功能的函数,函数声明为:int modify_infor(STUDENT stu[], int n),形参为结构体数组stu[]和结构体数组个数n。函数内部,可选择通过学号查找修改和通过姓名查找修改两种方式。查找部分与void find_infor(STUDENT stu[], int n)函数一致;修改部分有修改学号、姓名、成绩三个选项,修改的实现形式是:通过查找部分得出待修改的数组下标,然后通过strcpy()函数,将输入的修改后的学号和姓名赋值给下标对应的成员,分数的修改则直接赋值。
完整代码

int modify_infor(STUDENT stu[], int n)
{
	int option, choice;
	int i = 0;
	char old_temp[20] = { 0 };
	char new_temp[20] = { 0 };
	float new_score;

	printf("\n\t\t\t1、按学号查找修改\n");
	printf("\t\t\t2、按姓名查找修改\n");
	printf("\n\t\t请输入选项选择功能:");
	scanf("%d", &option);

	system("cls");

	if (1 == option)
	{
		printf("\n\t\t请输入学号:");
		scanf("%s", &old_temp);

		while ((strcmp(stu[i].num, old_temp) != 0) && i < n)
		{
			i++;
		}

		if (i == n)
		{
			printf("\n\t\t未找到!");
		}
	}

	if (2 == option)
	{
		printf("\n\t\t请输入姓名:");
		scanf("%s", &old_temp);

		while ((strcmp(stu[i].name, old_temp) != 0) && i < n)
		{
			i++;
		}

		if (i == n)
		{
			printf("\n\t\t未找到!");
		}
	}

	printf("\n\t\t待修改学生的信息:\n");
	printf("\n\t\t学号:%s\n\t\t姓名:%s\n\t\t成绩:%3.1f\n", stu[i].num, stu[i].name, stu[i].score);

	printf("\n\t\t请选择要修改的信息(1.学号 2.姓名 3.成绩):");
	scanf("%d", &choice);

	if (1 == choice)
	{
		printf("\n\t\t输入修改后的学号:");
		scanf("%s", &new_temp);

		strcpy(stu[i].num, new_temp);
		printf("\t\t修改成功!\n");
	}

	if (2 == choice)
	{
		printf("\n\t\t输入修改后的姓名:");
		scanf("%s", &new_temp);

		strcpy(stu[i].name, new_temp);
		printf("\t\t修改成功!\n");
	}

	if (3 == choice)
	{
		printf("\n\t\t输入修改后的成绩:");
		scanf("%f", &new_score);

		stu[i].score = new_score;
		printf("\t\t修改成功!\n");
	}
}

此函数中的两个字符串数组在定义时,也需要赋初值,以消除函数strcmp()strcpy()使用时的警告。
运行效果
在这里插入图片描述


  • sort_score() 函数

此函数完整声明为:void sort_score(STUDENT stu[], int n),形参为结构体数组stu[]和结构体数组个数n,实现的功能是排序,用的是冒泡排序法,按从小到大的顺序给成绩排序。具体实现形式就是,通过两层循环,外层循环是排序趟数,内层循环是每趟比较的次数,相邻两元素比较,若前者大于后者,则两者进行交换,此时为顺序,即从小到大;若前者小于后者且两者交换,则为逆序,即从大到小。此函数为顺序,且在交换两者值时,学号和姓名使用strcpy()函数,成绩的交换使用指针。
完整代码

void sort_score(STUDENT stu[], int n)
{
	int i, j;
	float * p, * q, s;
	char temp[20];

	for (i = 0; i < n - 1; i++)
	{
		for (j = 0; j < n - 1 - i; j++)
		{
			if (stu[j].score > stu[j + 1].score)
			{
				strcpy(temp, stu[j].num);
				strcpy(stu[j].num, stu[j + 1].num);
				strcpy(stu[j + 1].num, temp);

				strcpy(temp, stu[j].name);
				strcpy(stu[j].name, stu[j+1].name);
				strcpy(stu[j+1].name,temp);

				p = &stu[j].score;
				q = &stu[j + 1].score;
				s = *p;
				*p = *q;
				*q = s;
			}
		}
	}
}

运行效果
在这里插入图片描述


  • delete_infor() 函数

此函数是信息删除函数,完整声明为:int delete_infor(STUDENT stu[], int n),形参为结构体数组stu[]和结构体数组个数n,返回值是删除后的数组个数。函数内部,可通过按学号和按姓名两种方式查找并删除,其中查找部分与void find_infor(STUDENT stu[], int n)函数一致;删除部分的实现形式是,从查找部分获得找到的数组下标,从下标处开始,将后一值赋给前一值,直至数组的末尾,以此实现删除的功能。
完整代码

int delete_infor(STUDENT stu[], int n)
{
	char search_num[10] = { 0 };
	char search_name[20] = { 0 };
	int i = 0;
	int j;
	int option;
	
	printf("\n\t\t\t1、按学号查找删除\n");
	printf("\t\t\t2、按姓名查找删除\n");
	printf("\n\t\t\t请输入选项选择功能:");
	scanf("%d", &option);
	
	system("cls");

	if (1 == option)
	{
		printf("\n\t\t请输入要删除的学生的学号:");
		scanf("%s", &search_num);

		while ((strcmp(stu[i].num, search_num)) != 0 && (i < n))
		{
			i++;
		}

		if (i == n)
		{
			printf("\t\t未找到!\n");
			return n;
		}
	}
	
	if (2 == option)
	{
		printf("\n\t\t请输入要删除的学生的姓名:");
		scanf("%s", &search_name);

		while ((strcmp(stu[i].num, search_name) != 0) && (i < n))
		{
			i++;
		}

		if (i == n)
		{
			printf("\t\t未找到!\n");
			return n;
		}
	}

	for (j = i; j < n; j++)
	{
		strcpy(stu[j].num, stu[j + 1].num);
		strcpy(stu[j].name, stu[j + 1].name);
		stu[j].score = stu[j + 1].score;
	}

	printf("\t\t删除成功!\n");
	return (n - 1);
}

注意此函数的返回值,由于删除了一条信息,即从结构体数组中删除了一项,所以返回值应当是传入参数n减去1之后的值,即返回值return (n-1);
运行效果
在这里插入图片描述


  • writetofile() 函数

此函数的功能是将录入的数据保存到文件中,完整声明为:void writetofile(STUDENT stu[], int n),形参为结构体数组stu[]和结构体数组个数n。函数内部,用 FILE 结构体类型定义文件类型的指针变量 fp,输入要写入的文件的名称,然后以只写 “w” 的方式,向文件写入数据,若指定的文件不存在则创建它,如果存在则先删除它再重建一个新文件,首先向文件内写结构体数组的个数,然后通过 while 循环,将所有信息写入文件中,最后fclose(fp)关闭文件。
完整代码

void writetofile(STUDENT stu[], int n)
{
	FILE* fp;
	int i = 0;
	char filename[20] = { 0 };
	
	printf("\n\t\t\t请输入文件名称:");
	scanf("%s", filename);

	if ((fp = fopen(filename, "w")) == NULL)
	{
		printf("\n\t\t\t打开文件失败!\n");
		return;
	}

	fprintf(fp, "%d\n", n);

	while (i < n)
	{
		fprintf(fp, "%-6s%-10s%3.1f\n", stu[i].num, stu[i].name, stu[i].score);
		i++;
	}

	fclose(fp);

	printf("\n\t\t\t写入文件成功!\n");
}

由于只需要向文件写数据,所以fopen()中 mode 参数只需要"w"即可,数据写入文件成功后,一定要使用fclose(fp)关闭文件。
运行效果
在这里插入图片描述
在这里插入图片描述
“向文件写入数据”字样由 main() 函数在调用 writetofile() 函数前打印


  • readfromfile() 函数

此函数的功能是从文件中读出数据,完整声明为:int readfromfile(STUDENT stu[], int n),形参为结构体数组stu[]和结构体数组个数n,返回值为读入数据后的数组总个数。函数内部,用 FILE 结构体类型定义文件类型的指针变量 fp,输入要读出数据的文件的名称,以只读方式,通过 while 循环,从文件中读出数据,存放到数组中,然后将读到的数据个数加上传进的参数 n 作为返回值,最后关闭文件。
完整代码

int readfromfile(STUDENT stu[], int n)
{
	FILE* fp;
	int i, num;
	char filename[20];

	i = 0;

	printf("\n\t\t\t请输入文件名称:");
	scanf("%s", filename);

	if ((fp = fopen(filename, "r")) == NULL)
	{
		printf("\n\t\t\t无法打开文件!\n");

		return n;
	}

	fscanf(fp, "%d", &num);

	while (i < num)
	{
		fscanf(fp, "%-5s%-10s%3.1f\n", stu[n + i].num, stu[n + i].name, stu[n + i].score);
		i++;
	}

	n += num;
	fclose(fp);

	printf("\n\t\t\t读出数据成功!\n");

	return n;
}

同样,在打开文件并执行操作完毕后,需要fclose(fp)来关闭文件。注意,此函数的返回值应该是读到的数据个数 num 加上传进的参数 n ,返回的才是此时总的数组个数。
运行效果
在这里插入图片描述
“从文件读出数据”字样由 main() 函数在调用 readfromfile() 函数前打印


  • main() 函数

main函数部分,主要是由while(1)死循环构成的,循环内部首先调用home_page()函数显示主界面,然后通过 switch-case 语句,来匹配用户输入的选项。

完整代码

int main()
{
	int select = 0;
	int n = 0;

	while (1)
	{
		system("cls");
		
		home_page();

		printf("\t\t输入标号选择功能: ");
		scanf("%d", &select);

		switch (select)
		{
			case 1:
				system("cls");
				printf("\n\t\t********录入学生成绩信息********\n");
				n = add_infor(stu, n);
				break;
			case 2:
				system("cls");
				printf("\n\t\t********浏览学生成绩信息********\n");
				browse_infor(stu, n);
				getchar();
				break;			
			case 3:
				system("cls");
				printf("\n\t\t**********查询学生信息**********\n");
				find_infor(stu, n);
				getchar();
				break;
			case 4:
				system("cls");
				printf("\n\t\t*********修改学生信息*********\n");
				n = modify_infor(stu, n);
				getchar();
				break;
			case 5:
				system("cls");
				printf("\n\t\t*********按成绩排序*********\n");
				sort_score(stu, n);
				printf("\n\t\t\t排序成功!");
				getchar();
				break;	
			case 6:
				system("cls");
				printf("\n\t\t*********删除学生信息*********\n");
				n = delete_infor(stu, n);
				getchar();
				break;
			case 7:
				system("cls");
				printf("\n\t\t********向文件写入数据********\n");
				writetofile(stu, n);
				getchar();
				break;
			case 8:
				system("cls");
				printf("\n\t\t********从文件读出数据********\n");
				n = readfromfile(stu, n);
				getchar();
				break;
			default:
				system("cls");
				printf("\n\n\t\t**********谢谢使用!**********\n");
				exit(0);
				break;
		}

		getchar();
	}
}

注意,switch-case 语句的每个 case 结束都应加上break;,且最后要加上default,同时,为了美观,在每个 case 语句调用函数前,都加上system("cls")清屏函数;在调用后,加上getchar ()语句等待用户操作。


  • 完整代码

链接: 完整代码

结束语

第一次写博客,方方面面的都有些不熟悉,脑子里有很多想法,但无从下手,拿了以前写的程序作主要内容,进行一次尝试。虽然内容啥的都很简单,程序也没有什么技术含量,但这是一次从0到1的尝试吧,以后也会继续努力。文中如有错误之处,还请指正,如遇大佬,还请多多指教!

Logo

快速构建 Web 应用程序

更多推荐