1. 练习题的价值与使用场景
对于C语言学习者来说,练习题是巩固基础、提升编程思维的重要工具。这组21-30编号的练习题,特别适合已经掌握基础语法但需要加强实践能力的学习者。我在大学任教期间,经常布置类似难度的题目给大二学生作为课后作业。
这类练习题通常覆盖以下几个核心知识点:
- 基础语法巩固(循环、条件判断)
- 数组和指针的灵活运用
- 简单算法的实现
- 基础输入输出处理
提示:建议每做完一道题后,尝试用不同方法实现,比如把for循环改成while循环,或者把数组操作改成指针操作,这样能获得更多练习价值。
2. 题目21-25详细解析
2.1 第21题:数字逆序输出
这道题要求输入一个整数,输出其逆序数。比如输入1234,输出4321。看似简单,但有几个关键点需要注意:
c复制#include <stdio.h>
int main() {
int num, reversed = 0;
printf("请输入一个整数:");
scanf("%d", &num);
while(num != 0) {
reversed = reversed * 10 + num % 10;
num /= 10;
}
printf("逆序数为:%d\n", reversed);
return 0;
}
常见问题:
- 负数处理:如果输入负数,上述代码会输出负的逆序数(如-123 → -321)
- 末尾是0的情况:输入1200会输出21而不是0021
- 溢出问题:大数逆序后可能超出int范围
2.2 第22题:素数判断
判断一个数是否为素数是经典题目,但优化算法很关键。初学者常犯的错误是检查所有小于n的数:
c复制int isPrime(int n) {
if(n <= 1) return 0;
for(int i = 2; i < n; i++) {
if(n % i == 0) return 0;
}
return 1;
}
优化版本只需检查到√n:
c复制#include <math.h>
int isPrime(int n) {
if(n <= 1) return 0;
int limit = sqrt(n);
for(int i = 2; i <= limit; i++) {
if(n % i == 0) return 0;
}
return 1;
}
2.3 第23题:斐波那契数列
斐波那契数列有多种实现方式,递归虽然直观但效率低:
c复制int fib(int n) {
if(n <= 1) return n;
return fib(n-1) + fib(n-2);
}
迭代法更高效:
c复制int fib(int n) {
int a = 0, b = 1, c;
if(n == 0) return a;
for(int i = 2; i <= n; i++) {
c = a + b;
a = b;
b = c;
}
return b;
}
2.4 第24题:字符串反转
不使用库函数实现字符串反转:
c复制void reverseString(char* str) {
int len = 0;
while(str[len] != '\0') len++;
for(int i = 0; i < len/2; i++) {
char temp = str[i];
str[i] = str[len-1-i];
str[len-1-i] = temp;
}
}
2.5 第25题:冒泡排序
经典排序算法实现:
c复制void bubbleSort(int arr[], int n) {
for(int i = 0; i < n-1; i++) {
for(int j = 0; j < n-i-1; j++) {
if(arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
3. 题目26-30进阶解析
3.1 第26题:二分查找
二分查找的前提是数组已排序:
c复制int binarySearch(int arr[], int left, int right, int target) {
while(left <= right) {
int mid = left + (right - left)/2;
if(arr[mid] == target) return mid;
if(arr[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}
3.2 第27题:最大公约数
欧几里得算法实现:
c复制int gcd(int a, int b) {
while(b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
3.3 第28题:矩阵转置
二维数组操作:
c复制void transpose(int mat[][N], int trans[][M]) {
for(int i = 0; i < M; i++) {
for(int j = 0; j < N; j++) {
trans[j][i] = mat[i][j];
}
}
}
3.4 第29题:链表反转
数据结构基础操作:
c复制struct Node {
int data;
struct Node* next;
};
struct Node* reverseList(struct Node* head) {
struct Node *prev = NULL, *current = head, *next = NULL;
while(current != NULL) {
next = current->next;
current->next = prev;
prev = current;
current = next;
}
return prev;
}
3.5 第30题:文件内容统计
文件操作练习:
c复制#include <stdio.h>
int main() {
FILE *file;
char ch;
int chars = 0, words = 0, lines = 0;
file = fopen("sample.txt", "r");
if(file == NULL) {
printf("无法打开文件\n");
return 1;
}
while((ch = fgetc(file)) != EOF) {
chars++;
if(ch == '\n') lines++;
if(ch == ' ' || ch == '\n' || ch == '\t') words++;
}
fclose(file);
printf("字符数:%d\n单词数:%d\n行数:%d\n", chars, words, lines);
return 0;
}
4. 常见错误与调试技巧
4.1 内存管理问题
初学者常见的内存错误:
- 数组越界访问
- 使用未初始化的指针
- 内存泄漏
- 野指针
调试建议:
- 使用valgrind工具检测内存问题
- 在Linux下使用gdb调试
- 添加打印语句跟踪程序执行
4.2 指针使用陷阱
指针常见错误示例:
c复制int *p;
*p = 10; // 错误:指针未初始化
char *str = "hello";
str[0] = 'H'; // 错误:尝试修改字符串常量
4.3 输入输出问题
scanf常见问题:
c复制int a;
char b;
scanf("%d", &a);
scanf("%c", &b); // 会读取前一个输入的回车
解决方案:
c复制scanf("%d", &a);
getchar(); // 消耗回车
scanf("%c", &b);
5. 进阶练习建议
完成基础题目后,可以尝试以下变种:
- 把迭代实现改为递归实现
- 增加错误处理逻辑
- 考虑边界条件和大数情况
- 添加性能测试和优化
- 编写单元测试验证正确性
例如,斐波那契数列可以尝试:
- 使用记忆化优化递归
- 实现O(logn)复杂度的矩阵解法
- 计算大数斐波那契(使用数组存储)
对于排序算法,可以:
- 添加比较次数和交换次数的统计
- 实现可视化展示排序过程
- 测试不同数据规模下的性能
6. 学习资源推荐
- 《C Primer Plus》- 全面系统的C语言教材
- 《C和指针》- 深入讲解指针概念
- 《算法导论》- 算法理论基础
- LeetCode和牛客网 - 在线编程练习平台
- GitHub上的开源项目 - 学习优秀代码风格
练习时建议:
- 先自己尝试实现,不要直接看答案
- 写伪代码理清思路
- 测试各种边界条件
- 记录解题时间和遇到的问题
