简介:这份《C语言经典编程实例100题 答案》文档面向C语言初学者与进阶学习者,用于通过经典题目巩固语法、提升编程实践能力,也可作为计算机相关课程的教学辅助材料。资源包为单个doc文件,压缩后约167KB,内容以文字讲解与代码实例为主,便于随时查阅与对照练习。文档围绕变量与数据类型、各类运算符、if与switch分支、循环与跳转语句、函数定义与参数传递、一维及多维数组、指针与数组和函数的关系、字符串复制连接比较搜索等核心知识点展开,每道题均配有答案与解析,帮助读者理解控制流程与模块化设计思路。已有107人学习下载,适合需要系统刷题、查漏补缺或准备课程作业与考试复习的读者参考使用。
1. 从一份“答案.doc”说起:C语言经典编程实例100题到底该怎么刷
很多人第一次拿到“C语言经典编程实例100题 答案.doc”这类资料,第一反应是打开文档,对着答案抄一遍,编译通过就关掉。结果面试被问到“冒泡排序为什么两层循环边界是 n-1-i”,或者“strcpy 为什么返回 char*”,脑子里一片空白。这份资料真正的价值不在答案本身,而在于它是一张覆盖 C 语言核心知识点的地图:指针、数组、字符串、结构体、文件读写、内存管理、递归、排序查找,几乎每一题都对应一个必须亲手写一遍才能长肌肉的语法点。
这份文档适合两类人:一类是刚学完 c语言基础、c语言基础知识入门阶段,需要大量短小题目把语法变成本能的新手;另一类是要应付计算机二级c语言、cspj 初赛,或者准备嵌入式、后端岗位笔试,需要快速把 c语言必背100代码过一遍的从业者。答案可以看,但正确姿势是:先自己写,编译报错,再对照答案找差异,最后把差异点记成笔记。下面按“读题—写码—验证—排错”的路径,把这份资料拆成能真正落地的刷题方案。
2. 拆解100题的知识分布与刷题顺序
2.1 先给题目分类,别从第1题顺着做
一份典型的 C 语言 100 题资料,题目顺序往往是随意的,顺着做会在简单题上浪费时间,又在中途突然撞上指针和链表。我一般会先按知识点把题目分成六类,再按依赖关系排序。
| 类别 | 典型题目 | 依赖前置 | 建议顺序 |
|---|---|---|---|
| 基础运算与分支 | 判断素数、水仙花数、成绩等级 | 无 | 1 |
| 循环与数组 | 冒泡排序c语言、数组逆序、二分查找 | 分支 | 2 |
| 字符串处理 | 字符串逆序c语言pta、strcpy用法、统计单词 | 数组 | 3 |
| 函数与递归 | 阶乘、斐波那契、汉诺塔 | 循环 | 4 |
| 指针与内存 | c语言 函数指针 指针函数、动态数组 | 数组+函数 | 5 |
| 结构体与文件 | c语言文件读写操作代码、学生成绩管理 | 指针 | 6 |
这样排的原因是:指针题里大量出现数组退化、字符串常量不可写等坑,如果数组和字符串没过关,指针题只会变成抄答案。链表题又依赖结构体和动态内存,放在最后最稳。
2.2 用最小命令跑通第一题,建立反馈闭环
不要一上来就配 IDE。先用最朴素的命令行把“判断素数”跑通,确认编译链路没问题。
# 新建工作目录,每题一个文件,方便回溯 mkdir -p c100 && cd c100 cat > p001_prime.c <<'EOF' #include <stdio.h> #include <math.h> /* 判断素数:只需试除到 sqrt(n),这是最容易被忽略的优化点 */ int is_prime(int n) { if (n < 2) return 0; for (int i = 2; i <= (int)sqrt(n); i++) { if (n % i == 0) return 0; } return 1; } int main(void) { for (int i = 1; i <= 100; i++) { if (is_prime(i)) printf("%d ", i); } printf("\n"); return 0; } EOF gcc -Wall -Wextra -O2 -o p001 p001_prime.c -lm ./p001逻辑说明:is_prime里先处理n < 2的边界,这是新手最常见的漏判;循环上界用sqrt(n)而不是n/2,把复杂度从 O(n) 降到 O(√n)。参数说明:gcc的-Wall -Wextra打开全部常见警告,-O2开优化,-lm链接数学库,因为用了sqrt。如果编译报undefined reference to sqrt,就是漏了-lm。
提示:每道题都用
-Wall -Wextra编译,把警告当错误看。很多“答案能跑但结果不对”的问题,编译器早就警告过了。
2.3 对照答案时只看三处差异
抄答案没有意义,但对照答案有。我一般只对比三个地方:边界条件怎么处理、循环变量从几开始到几结束、返回值类型和错误码怎么设计。以冒泡排序为例,答案里常见写法是:
void bubble_sort(int a[], int n) { for (int i = 0; i < n - 1; i++) { int swapped = 0; /* 提前退出标记,已有序时省一轮 */ for (int j = 0; j < n - 1 - i; j++) { if (a[j] > a[j + 1]) { int t = a[j]; a[j] = a[j + 1]; a[j + 1] = t; swapped = 1; } } if (!swapped) break; /* 本轮无交换,说明已经有序 */ } }参数说明:n是数组长度,内层上界n - 1 - i是因为每轮结束最大的元素已经沉到末尾,没必要再比。swapped标记是答案里经常省略、但实际工程里必须加的优化。如果面试官问“最好情况复杂度”,有这个标记才能答 O(n)。
3. 指针、字符串与内存这三类题怎么写出“答案之外”的理解
3.1 字符串题:strcpy 用法和字符串逆序的坑
字符串题是 100 题里错误率最高的一类,因为很多人分不清字符数组和字符串常量。先看strcpy的正确用法和它为什么返回char*。
#include <stdio.h> #include <string.h> /* 手写 strcpy,理解返回值设计:支持链式调用 */ char *my_strcpy(char *dst, const char *src) { char *ret = dst; /* 保存首地址,因为 dst 会被移动 */ while ((*dst++ = *src++) != '\0') ; return ret; } int main(void) { char buf[32]; my_strcpy(buf, "hello"); printf("%s\n", buf); /* 字符串逆序:原地交换,注意用 strlen 而不是 sizeof */ char s[] = "abcdef"; int len = strlen(s); for (int i = 0; i < len / 2; i++) { char t = s[i]; s[i] = s[len - 1 - i]; s[len - 1 - i] = t; } printf("%s\n", s); return 0; }逻辑说明:my_strcpy里*dst++ = *src++先赋值再自增,循环在遇到'\0'时结束,返回值ret让调用方能写strlen(my_strcpy(a, b))这种链式表达式。参数说明:src加const表示不修改源串,这是接口设计的基本素养。逆序题里必须用strlen(s)而不是sizeof(s),因为sizeof会把末尾的'\0'也算进去,导致交换错位。
注意:
char *p = "abc"; p[0] = 'x';是未定义行为,字符串常量在只读段。要修改就用char s[] = "abc";。这个坑在字符串逆序c语言pta 类题目里反复出现。
3.2 指针题:函数指针和指针函数的区别要落到代码上
c语言 函数指针 指针函数是热搜里高频出现的混淆点。一句话区分:指针函数是“返回指针的函数”,函数指针是“指向函数的指针”。看代码最清楚。
#include <stdio.h> /* 指针函数:返回 int* 的函数 */ int *find_max(int *a, int n) { int *max = &a[0]; for (int i = 1; i < n; i++) if (a[i] > *max) max = &a[i]; return max; /* 返回的是数组内部地址,安全 */ } /* 函数指针:指向“两个 int 返回 int”的函数 */ int add(int x, int y) { return x + y; } int sub(int x, int y) { return x - y; } int main(void) { int arr[] = {3, 9, 2, 7}; printf("max=%d\n", *find_max(arr, 4)); int (*op)(int, int) = add; /* 函数指针变量 op */ printf("add=%d\n", op(2, 3)); op = sub; printf("sub=%d\n", op(5, 1)); return 0; }逻辑说明:find_max返回数组元素地址,调用方解引用取值,注意不能返回局部数组的地址。int (*op)(int, int)的括号不能省,省了就变成“返回 int* 的函数声明”。参数说明:函数指针常用于回调,比如qsort的第四个参数就是int (*)(const void*, const void*)。
3.3 内存管理题:malloc 和 free 的配对检查
100 题里涉及动态内存的题目,答案往往只写malloc不写free,这在教学里可以,在工程里是灾难。我一般会加一个简单的封装来强制配对。
#include <stdio.h> #include <stdlib.h> /* 封装 malloc,失败直接退出,避免到处判空 */ void *xmalloc(size_t size) { void *p = malloc(size); if (!p) { fprintf(stderr, "out of memory\n"); exit(1); } return p; } int main(void) { int n = 10; int *a = (int *)xmalloc(n * sizeof(int)); for (int i = 0; i < n; i++) a[i] = i * i; for (int i = 0; i < n; i++) printf("%d ", a[i]); printf("\n"); free(a); /* 配对释放,置空防悬垂 */ a = NULL; return 0; }逻辑说明:xmalloc把判空集中到一处,业务代码更干净。参数说明:n * sizeof(int)要注意整数溢出,大数组场景应改成calloc(n, sizeof(int))或先检查n上限。free之后置NULL是防御性习惯,能避免重复释放和悬垂指针。
4. 文件读写与结构体综合题的落地写法
4.1 用结构体+文件做一份学生成绩管理
100 题的后半段通常有一道综合题:把学生信息存到文件再读出来。这道题把结构体、文件读写、字符串处理全串起来了,值得认真写一遍。
#include <stdio.h> #include <stdlib.h> #include <string.h> #define NAME_LEN 32 typedef struct { int id; char name[NAME_LEN]; int score; } Student; /* 写入:文本模式,一行一条记录 */ int save(const char *path, Student *s, int n) { FILE *fp = fopen(path, "w"); if (!fp) return -1; for (int i = 0; i < n; i++) fprintf(fp, "%d %s %d\n", s[i].id, s[i].name, s[i].score); fclose(fp); return 0; } /* 读取:fscanf 返回值判断是否读满三个字段 */ int load(const char *path, Student *s, int max) { FILE *fp = fopen(path, "r"); if (!fp) return -1; int n = 0; while (n < max && fscanf(fp, "%d %31s %d", &s[n].id, s[n].name, &s[n].score) == 3) { n++; } fclose(fp); return n; } int main(void) { Student in[3] = {{1, "alice", 90}, {2, "bob", 85}, {3, "carol", 78}}; save("score.txt", in, 3); Student out[10]; int n = load("score.txt", out, 10); for (int i = 0; i < n; i++) printf("%d %s %d\n", out[i].id, out[i].name, out[i].score); return 0; }逻辑说明:save用fprintf按行写,格式固定,方便人工查看。load用fscanf的返回值判断是否成功读到三个字段,返回 3 才计数,这样能正确处理文件末尾和格式错误。参数说明:%31s限制读入长度,防止name缓冲区溢出,这是c语言文件读写操作代码里最容易被忽略的安全点。max参数防止越界写入调用方数组。
4.2 文件读写常见错误对照表
| 现象 | 原因 | 修法 |
|---|---|---|
| fopen 返回 NULL | 路径不对或无权限 | 打印perror("fopen")看具体 errno |
| 读到乱码 | 文本/二进制模式混用 | Windows 下文本用"r",二进制用"rb" |
| 最后一行重复 | feof判断时机错误 | 用fscanf/fgets返回值判断,别用feof当循环条件 |
| 写入后读不到 | 没fclose或没fflush | 写完必须fclose,缓冲区才会落盘 |
提示:
while (!feof(fp))是经典错误写法,它会在最后一次多读一次。正确做法是while (fgets(buf, sizeof buf, fp))。
4.3 把综合题改成命令行小工具
刷完题后,把学生成绩管理改造成带参数的命令行工具,是检验是否真掌握的好办法。
# 编译 gcc -Wall -Wextra -O2 -o score score.c # 写入示例数据 ./score save score.txt # 读取并打印 ./score load score.txt逻辑说明:main里根据argv[1]分派save或load,这样一道题就变成了可复用的小工具。参数说明:argc至少为 3,否则打印用法并返回 1。这一步做完,文件读写、结构体、命令行参数三个知识点就串成了一条线。
5. 用调试器和静态检查把“答案对”变成“代码对”
5.1 用 gdb 定位数组越界和空指针
答案能跑出正确结果,不代表没有越界。用 gdb 跑一遍,能提前发现很多隐藏问题。
gcc -g -fsanitize=address -o p001 p001_prime.c -lm ./p001逻辑说明:-g保留调试符号,-fsanitize=address是 AddressSanitizer,能在运行时检测数组越界、使用已释放内存、栈溢出等问题。参数说明:ASan 会让程序变慢、内存占用变高,只用于调试,发布时去掉。如果输出里出现heap-buffer-overflow或stack-buffer-overflow,就按提示的行号回去改。
5.2 用 cppcheck 做静态检查
cppcheck --enable=warning,style --inconclusive p001_prime.c逻辑说明:cppcheck不编译代码,直接扫描源码,能发现未初始化变量、数组越界、内存泄漏等。参数说明:--enable=warning,style打开警告和风格检查,--inconclusive让它在不确定时也报告,避免漏报。对 100 题这种短文件,跑一遍只要几秒,性价比很高。
5.3 把易错点整理成自己的检查清单
刷完 100 题,真正沉淀下来的不是答案,而是一份检查清单。我一般会记这几条:数组下标是否从 0 开始、循环边界是否差一、字符串是否留了'\0'位置、malloc是否配对free、fopen是否判空、scanf的%s是否限宽、函数指针声明括号是否写对。每次写完新代码,对着清单扫一遍,比重新翻答案快得多。
5.4 用一道“字符串逆序”验证整条链路
最后拿字符串逆序c语言pta 这类题做一次端到端验证:自己写、编译带警告、ASan 跑一遍、cppcheck 扫一遍、对照答案只看边界处理。如果这四步都过了,这道题才算真正刷完。100 题按这个流程走下来,c语言基础知识和 c语言内存管理这两块基本就扎实了,剩下的只是熟练度问题。
本文还有配套的精品资源,点击获取