c语言项目驱动学习--实例化--001-V1基础知识

发布时间:2026/6/26 18:14:31
c语言项目驱动学习--实例化--001-V1基础知识 C语言核心知识点总结与示例一、核心知识点提炼 必须掌握⭐⭐⭐1. 结构体的定义与使用// 定义结构体 struct Student { int id; char name[20]; float score; }; // 使用结构体 struct Student s1 {1001, 张三, 95.5}; printf(姓名%s成绩%.1f\n, s1.name, s1.score); // 结构体指针 struct Student* p s1; p-score 98.0; // 等价于 (*p).score 98.0关键点结构体是数据容器组合不同类型.访问成员-通过指针访问成员结构体可以整体赋值2. 结构体数组struct Student class[30]; // 30个学生 // 初始化 struct Student class[3] { {1001, 张三, 85.5}, {1002, 李四, 92.0}, {1003, 王五, 78.5} }; // 遍历 for(int i 0; i 3; i) { printf(%d\t%s\t%.1f\n, class[i].id, class[i].name, class[i].score); }关键点数组的每个元素是结构体通过下标访问结构体再用.访问成员适合管理同类对象集合如图书、学生3. 指针与函数参数// ❌ 错误值传递无法修改原数据 void badUpdate(int x) { x 100; // 只修改了副本 } // ✅ 正确指针传递修改原数据 void goodUpdate(int* x) { *x 100; // 修改原变量 } // ✅ 结构体指针传递高效 void updateStudent(struct Student* p, float newScore) { p-score newScore; // 直接修改原结构体 } // 调用 int a 10; goodUpdate(a); // a变成100 updateStudent(s1, 99.0);关键点值传递复制数据指针传递共享数据传递结构体用指针避免复制高效大结构体传指针可节省内存4. 字符串操作#include string.h char str1[20] Hello; char str2[20]; // 字符串赋值不能用 strcpy(str2, str1); // str2 Hello // 字符串比较不能用 if(strcmp(str1, str2) 0) { printf(相等\n); } // 字符串拼接 strcat(str1, World); // str1 Hello World // 获取长度 int len strlen(str1); // len 11关键点字符串是字符数组需要#include string.h不能用赋值不能用比较必须确保目标数组空间足够 重要掌握⭐⭐5. 函数的声明与定义// 声明告诉编译器函数存在 int add(int a, int b); void printMsg(const char* msg); // 定义实现函数 int add(int a, int b) { return a b; } void printMsg(const char* msg) { printf(%s\n, msg); } // const修饰保护参数不被修改 void showStudent(const struct Student* p) { // p-score 100; // ❌ 编译错误不能修改 printf(%s\n, p-name); // ✅ 可以读取 }关键点声明放在主函数之前定义可以放在后面const保护指针指向的数据不被修改函数名见名知意如addBook,deleteBook6. 数组删除操作int arr[10] {1,2,3,4,5,6,7,8,9,10}; int size 10; // 删除索引为3的元素值为4 int index 3; for(int i index; i size - 1; i) { arr[i] arr[i1]; // 元素前移 } size--; // 逻辑大小减1 // 结果1,2,3,5,6,7,8,9,10关键点删除后必须维护逻辑大小变量用后一个元素覆盖前一个物理数组大小不变逻辑大小变化 了解即可⭐7. void* 通用指针void* ptr; // 可以指向任何类型 int a 100; char c A; ptr a; // 指向int ptr c; // 指向char // 使用时需要强制转换 int* p (int*)ptr; // 告诉编译器这是int*二、项目注意事项 ⚠️ 常见错误与避坑指南1. scanf 输入字符串注意事项// ❌ 错误无法输入带空格的字符串 scanf(%s, name); // 输入Data Structures只得到Data // ✅ 解决方案1fgets推荐 fgets(name, MAX_NAME, stdin); // 会包含换行符需要手动去除 name[strcspn(name, \n)] \0; // ✅ 解决方案2正则表达式 scanf(%[^\n], name); // 读取到换行符为止2. 数组越界问题// ❌ 严重错误数组越界 char name[10]; strcpy(name, This is a very long name); // 溢出 // ✅ 安全做法 strncpy(name, src, sizeof(name) - 1); name[sizeof(name) - 1] \0; // 确保结尾3. 指针判空// ❌ 错误使用前未检查 struct Book* b findBookById(id); b-stock 10; // 如果b是NULL程序崩溃 // ✅ 正确使用前检查 struct Book* b findBookById(id); if(b NULL) { printf(未找到图书\n); return; } b-stock 10;4. 删除操作注意事项// ⚠️ 删除前必须检查 void deleteBook(int id) { int index findBookIndexById(id); if(index -1) { printf(图书不存在\n); return; } // ⚠️ 检查业务规则 if(library[index].borrowed 0) { printf(有借出不能删除\n); return; } // 执行删除 for(int i index; i bookCount - 1; i) { library[i] library[i 1]; } bookCount--; }5. 全局变量管理// ⚠️ 全局变量的风险任何函数都能修改 int bookCount 0; // 全局变量 // ✅ 建议提供专门的修改函数 int getBookCount() { return bookCount; } void setBookCount(int count) { bookCount count; }三、完整示例学生成绩管理系统综合运用所有知识点#include stdio.h #include string.h #define MAX_STUDENTS 50 #define MAX_NAME 20 // 1. 定义结构体 struct Student { int id; char name[MAX_NAME]; float score; }; // 2. 全局数据 struct Student students[MAX_STUDENTS]; int studentCount 0; int nextId 1001; // 3. 函数声明 void addStudent(const char* name, float score); struct Student* findStudent(int id); void updateScore(int id, float newScore); void deleteStudent(int id); void listAll(); void showMenu(); // 4. 主函数 int main() { // 测试数据 addStudent(张三, 85.5); addStudent(李四, 92.0); addStudent(王五, 78.5); int choice; while(1) { showMenu(); scanf(%d, choice); if(choice 1) { char name[MAX_NAME]; float score; printf(姓名); scanf(%s, name); printf(成绩); scanf(%f, score); addStudent(name, score); } else if(choice 2) { int id; float newScore; printf(学号); scanf(%d, id); printf(新成绩); scanf(%f, newScore); updateScore(id, newScore); } else if(choice 3) { int id; printf(学号); scanf(%d, id); deleteStudent(id); } else if(choice 4) { listAll(); } else if(choice 5) { printf(再见\n); break; } } return 0; } // 5. 函数实现 void showMenu() { printf(\n 学生成绩管理系统 \n); printf(学生总数%d\n, studentCount); printf(1. 添加学生\n); printf(2. 修改成绩\n); printf(3. 删除学生\n); printf(4. 显示所有\n); printf(5. 退出\n); printf(请选择); } void addStudent(const char* name, float score) { if(studentCount MAX_STUDENTS) { printf(❌ 学生已满\n); return; } struct Student* s students[studentCount]; s-id nextId; strcpy(s-name, name); s-score score; studentCount; printf(✅ 添加成功学号%d\n, s-id); } struct Student* findStudent(int id) { for(int i 0; i studentCount; i) { if(students[i].id id) { return students[i]; } } return NULL; } void updateScore(int id, float newScore) { struct Student* s findStudent(id); if(s NULL) { printf(❌ 未找到学号 %d\n, id); return; } if(newScore 0 || newScore 100) { printf(❌ 成绩必须在0-100之间\n); return; } s-score newScore; printf(✅ 成绩已更新为 %.1f\n, newScore); } void deleteStudent(int id) { int index -1; for(int i 0; i studentCount; i) { if(students[i].id id) { index i; break; } } if(index -1) { printf(❌ 未找到学号 %d\n, id); return; } // 删除操作前移覆盖 for(int i index; i studentCount - 1; i) { students[i] students[i 1]; } studentCount--; printf(✅ 删除成功\n); } void listAll() { if(studentCount 0) { printf( 暂无学生\n); return; } printf(\n学号\t姓名\t成绩\n); printf(\n); for(int i 0; i studentCount; i) { struct Student* s students[i]; printf(%d\t%s\t%.1f\n, s-id, s-name, s-score); } printf(\n); printf(共计 %d 名学生\n, studentCount); }四、知识图谱速查表知识点关键语法常见错误记忆口诀结构体struct Book { };忘记分号数据打包成类型结构体数组struct Book books[100];数组越界连续存储如书架指针访问p-member混淆.和-指针用箭头字符串复制strcpy(dest, src)空间不足不能直接赋值字符串比较strcmp(s1, s2) 0用比较比较用函数指针传参void f(int* p)忘加想修改用指针数组删除覆盖前移忘记更新数量后往前覆盖错误检查if(ptr NULL)使用前不检查用前先判空五、建议理解内存布局结构体在内存中如何存储学会调试使用GDB查看指针和结构体内容