前言
我是小白,所以源码不是我写的捏,也写不来,我是笨蛋😭。
源码所属的博客在这里。链接:源代码
希望作者可以允许我引用。
不过便于我的理解,我把源码简单封装了一下(记录一下在封装过程里我遇到的小问题和疑问),也是为了之后再 添加功能 和 改进数据的存储与调用 做准备。
使用的工具:vs2019 x86
封装后的结构:
main.cpp代码如下:
#include "main.h"
#include "cale.h"
#include "index.h"
#include "MainMenuHandler.h"
#include "search.h"
#include "StuOperations.h"
int g = 0; //全局
int h = 5;
student s[5] = {
{
"小明", "00001", "boy", 80,90,80,90},
{
"小王", "00002", "boy", 60,80,90,50},
{
"小红", "00003", "girl", 85,65,75,90},
{
"小陈", "00004", "boy", 55,65,75,80},
{
"小青", "00005", "girl", 90,95,98,99}
};
User user[3] = {
{
"Lucky", "001"},
{
"Happy", "100"},
{
"Find", "999"}
};
//主菜单函数声明
void login(); //登入函数
void index(); //主界面函数
void input_record(); //输入学生成绩处理 1
void show_record(); //显示学生成绩处理 2
void search_record(); //查询学生成绩处理 3
void change_record(); //更新学生成绩处理 4
void detele_record(); //删除学生信息 5
void calc_record(); //计算成绩处理 6
void save_record(); //保存处理 7
void exit_record(); //退出 8
//查询子函数声明
void search_by_num(); //学号查询
void search_by_name(); //姓名查询
void search_by_sex(); //性别查询
void search_by_exit(); //返回上一级
//计算子函数声明
void calc_sum(); //计算总成绩
void calc_ave(); //计算平均成绩
void calc_eixt(); //返回上一级
//自定义函数
void print_table();//查询表格
void print_table_sum();//总成绩表格
void print_table_ave();//平均分表格
void read(); //将文件中的数据读入到结构体数组中
int main(void)
{
login();
index();
return 0;
}
问题一 :全局变量的处理
(1)变量int g ,h的处理
处理:
这个很好解决,在main.pp中声明定义int g = 0; int h = 5; 然后在main.h里添加 extern int g, h;即可。
因为:
在C语言中,extern关键字用于声明一个变量或者函数是在其他文件中定义的,或者是在当前文件的后面部分定义的。当你在一个源文件中使用extern声明一个变量或函数时,编译器会假设这个变量或函数在另一个地方有完整的定义,并且链接器会在链接阶段找到这个定义。
疑问:
编译器是如何知道extern后面的变量是定义在哪里的呢?我也没有声明位置啊?如果我在其他.cpp文件里同样声明int g = 1; int h = 6;且在调用文件里同样包含其头文件会如何呢?
解答:
当你在一个源文件中使用 extern 声明一个变量时,编译器并不会分配内存给这个变量,而是会在链接阶段查找这个变量的定义。变量的定义应该出现在另一个源文件中,通常是同一个项目中的另一个 .cpp 文件或一个共享库中。
当编译器遇到 extern 声明时,它只是记录下来,说:“这个变量或函数是在别的地方定义的。” 然后在链接阶段,链接器会从其他目标文件或库中找到这个变量或函数的实际定义,然后把它们连接到一起,形成最终可执行文件。
如果你在多个源文件中声明了相同的全局变量或函数,但只在一个源文件中实际定义了它(也就是提供了它的实现),这不会有问题,因为 extern 声明只是告诉编译器变量的存在,而实际的定义只会被包含一次。但是,如果在两个或更多的 .cpp 文件中都定义了同一个全局变量(例如,两个文件中都有 int g = 1;),那么在链接时就会出现错误,因为链接器会发现这个变量有多重定义。这是不允许的,因为一个符号只能有一个定义。为了避免这种错误,你应该确保变量的定义只出现在一个源文件中,而在其他需要使用该变量的源文件中使用 extern 声明。
(2)如何处理结构体及其变量的定义?
1.结构体定义在哪?
2.结构体变量的定义?
3.定义后其他源.cpp文件如何正确引用该结构体的数组变量?
4.这样的全局变量的定义有问题吗?用结构体数组储存数据有什么问题?(毕竟这只是个课题作业)
1.结构体定义在哪?
1.1 和int g,h;的定义过程一样吗?
首先:
我们的目标肯定是所用到函数都能调用到结构体Student、User的数组变量s[ ]和user[ ]。那么结构体的定义和全局变量int g,j;的定义是一样的吗?答案 是不一样的,结构体不好定义在main.cpp中,同时使用 extern 关键字通常与全局变量或函数的声明有关,而不是结构体的定义。
因此:
通常,结构体的定义和声明应该放在头文件(.h 文件)中,这样所有需要使用该结构体的源文件都可以通过包含这个头文件来访问它,就不需要重复定义。这里我就把结构体定义在main.h中。(其实写在main.cpp里也行,可以利用结构体指针)
#ifndef __MAIN_H
#define __MAIN_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define _CRT_SECURE_NO_WARNINGS
#define max 100
extern int g, h;
// 定义学生结构体
struct student
{
char name[max]; //姓名
char num[max]; //学号
char sex[max]; //性别
int chinese; //语文成绩
int mathematic; //数学成绩
int english; //英语成绩
int compuer; //计算机成绩
};
// 定义用户结构体
struct User //存储用户
{
char username[20];
char password[20];
};
// 声明全局数组
extern student s[5];
extern User user[3];
#endif //__MAIN_H
2.结构体变量的定义?
是的,刚刚定义完结构体,接着就是结构体的变量s[ ]、user[ ]的定义。
结构体只是一个自定义的复杂变量类型,它不是变量。
2.1 结构体变量的定义和结构体struct student和struct User的定义过程一样吗?
不一样,如果和原代码一样,变量和结构体一起定义在main.h文件里就会报错,如下图:
如何解释? 为什么会报错?我明明只在main.h中定义了s[ ]、user[ ],并没有在其他地方定义啊?为什么会报错多重定义?
解答:
在原始 main.h 文件中,直接定义了全局数组 s 和 user,如下所示:
struct student
{
// ...
}s[5] = {
/* 初始化 */ };
struct user
{
// ...
}user[3] = {
/* 初始化 */ };
由于这个头文件被多个源文件包含,每当一个源文件包含 main.h,编译器就会看到这些数组的定义。结果是,对于项目中的每个源文件,s 和 user 都会被重新定义一次,导致链接器在最后链接所有对象文件时遇到多重定义的错误。
如何解决?
为了解决这个问题,我们需要确保每个全局变量或数组只被定义一次,同时在需要使用它们的地方声明它们。这就是为什么我们要修改 main.h 并将数组的定义移到一个源文件中。
步骤 1: 修改 main.h
打开 main.h 文件并移除数组的定义,只留下结构体的定义和数组的声明:
#ifndef __MAIN_H
#define __MAIN_H
// ... (其他包含和宏定义)
// 定义学生结构体
typedef struct {
// 成员变量
} Student;
// 定义用户结构体
typedef struct {
// 成员变量
} User;
// 声明全局数组
extern Student s[5];
extern User user[3];
#endif // __MAIN_H
这里的关键是使用 extern 关键字来声明数组 s 和 user。extern 告诉编译器,这些数组将在其他地方定义,而不是在这里。
步骤 2: 定义数组
接下来,选择一个源文件(通常是 main.c 或 main.cpp),在那里定义数组 s 和 user:
#include "main.h"
// 定义学生数组
Student s[5] = {
{
"小明", "00001", "boy", 80,90,80,90},
// ... 其他初始化
};
// 定义用户数组
User user[3] = {
{
"Lucky", "001"},
// ... 其他初始化
};
步骤 3: 结束并验证
3.定义后其他源.cpp文件如何正确引用该结构体的数组变量?
直接引用头文件即可,#include “main.h”。原函数不变。main.h如下:
#ifndef __MAIN_H
#define __MAIN_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define _CRT_SECURE_NO_WARNINGS
#define max 100
extern int g, h;
// 定义学生结构体
struct student
{
char name[max]; //姓名
char num[max]; //学号
char sex[max]; //性别
int chinese; //语文成绩
int mathematic; //数学成绩
int english; //英语成绩
int compuer; //计算机成绩
};
// 定义用户结构体
struct User //存储用户
{
char username[20];
char password[20];
};
// 声明全局数组
extern student s[5];
extern User user[3];
#endif //__MAIN_H
4.这样的全局变量的定义有问题吗?用结构体数组储存数据有什么问题?(毕竟这只是个课题作业)
让我们从头开始吧!
4.1 全局变量int g,h;
这里使用编译器的查询功能可以快速定位,输入g+空格(标准书写代码的好处)找到