1. C语言编程实战:从基础算法到结构体应用
今天我想分享几个在哈工大C语言课程中遇到的经典编程题目,这些题目涵盖了排序算法、条件判断、字符串处理、字符转换和结构体应用等核心知识点。每个题目都配有完整代码和详细解析,特别适合正在学习C语言的同学参考。
2. 奇偶排序算法实现
2.1 问题分析与算法选择
这个题目要求我们对10个整数进行排序,输出时奇数在前偶数在后,且整体保持升序排列。我选择使用冒泡排序算法,因为:
- 冒泡排序实现简单,适合小规模数据(10个元素)
- 排序完成后可以方便地分离奇偶数
- 时间复杂度O(n²)对于n=10完全可以接受
注意:虽然冒泡排序不是最高效的算法,但在数据量小且需要简单实现时,它仍然是个不错的选择。在实际工程中,如果数据量大,应该考虑更高效的排序算法如快速排序。
2.2 代码实现细节
c复制#include <stdio.h>
int main()
{
int a[10], result[10];
printf("Input 10 numbers:");
// 输入处理
for(int i = 0; i < 10; i++){
scanf("%d", &a[i]);
}
// 冒泡排序核心逻辑
for(int i = 0; i < 9; i++){
for(int j = 0; j < 10 - i - 1; j++){
if(a[j] > a[j + 1]){
int tmp = a[j];
a[j] = a[j + 1];
a[j + 1] = tmp;
}
}
}
// 奇偶数分离技巧
int front = 0;
for(int i = 0; i < 10; i++){
if(a[i] % 2 == 1) result[front++] = a[i];
}
for(int i = 0; i < 10; i++){
if(a[i] % 2 == 0) result[front++] = a[i];
}
// 格式化输出
printf("Output: %d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
result[0], result[1], result[2], result[3], result[4],
result[5], result[6], result[7], result[8], result[9]);
return 0;
}
2.3 常见问题与优化
- 边界条件处理:确保数组索引不越界,特别是在冒泡排序的内层循环中
- 奇偶判断:使用%运算符时,注意负数的处理(本题目假设输入都是正整数)
- 优化建议:可以尝试一次性完成排序和奇偶分离,减少一次数组遍历
3. 整数属性判断
3.1 条件逻辑设计
这个题目需要判断一个整数的正负性和奇偶性,涉及多重条件判断。关键点在于:
- 首先检查是否为0(特殊情况)
- 然后判断正负
- 最后判断奇偶性
c复制#include <stdio.h>
#include <stdlib.h>
int main()
{
int m;
printf("Input m:");
scanf("%d", &m);
if(m == 0){
printf("%d is zero. It is an even\n", m);
} else if(m < 0){
// 负数处理
if(abs(m) % 2 == 1){
printf("%d is a negative odd\n", m);
} else {
printf("%d is a negative even\n", m);
}
} else {
// 正数处理
if(m % 2 == 1){
printf("%d is a positive odd\n", m);
} else {
printf("%d is a positive even\n", m);
}
}
return 0;
}
3.2 注意事项
- 使用abs()函数处理负数的奇偶判断,避免直接对负数取模可能带来的问题
- 条件判断的顺序很重要,必须先检查0的情况
- 输出格式要严格符合题目要求
4. 字符串逆序实现
4.1 双指针算法
字符串逆序是经典的编程题目,这里使用双指针法实现:
- 一个指针从头部开始,一个从尾部开始
- 交换两个指针所指的字符
- 向中间移动指针,直到相遇
c复制#include <stdio.h>
#include <string.h>
void Inverse(char str[]);
int main()
{
char str[80];
printf("Input a string:\n");
gets(str); // 注意:gets()不安全,实际开发中应使用fgets()
Inverse(str);
printf("Inversed results:\n");
puts(str);
return 0;
}
void Inverse(char str[]){
int len = strlen(str);
char temp;
for(int i = 0; i < len / 2; i++){
temp = str[i];
str[i] = str[len - 1 - i];
str[len - 1 - i] = temp;
}
}
4.2 安全注意事项
- 缓冲区溢出风险:gets()函数不安全,可能造成缓冲区溢出
- 实际开发替代方案:应该使用fgets(str, sizeof(str), stdin)
- 空字符处理:确保字符串以'\0'结尾
5. 大小写字母转换
5.1 ASCII码操作
小写字母转大写字母利用了ASCII码的特性:
- 'a'到'z'的ASCII码是97到122
- 'A'到'Z'的ASCII码是65到90
- 大小写字母相差32
c复制#include <stdio.h>
int main()
{
char c;
printf("输入一个小写字符\n");
c = getchar();
// 转换方法1:使用差值计算
c = c - 'a' + 'A';
// 转换方法2:直接减32
// c = c - 32;
printf("转换以后的字符为:%c,%d\n", c, c);
return 0;
}
5.2 输入验证
- 应该检查输入是否确实是小写字母
- 非字母字符的处理
- 考虑EOF等特殊情况
6. 结构体应用:学生成绩查询
6.1 结构体设计
使用结构体来组织学生信息是最佳选择,因为:
- 学号和成绩是逻辑上相关的数据
- 结构体提供了良好的数据封装
- 便于扩展其他学生属性
c复制#include <stdio.h>
struct student{
long xh; // 学号
int score; // 成绩
};
int main()
{
int n = 0, flag = 0;
long ID;
struct student S[40];
while(1){
printf("Input student's ID and score:");
scanf("%ld %d", &S[n].xh, &S[n].score);
if(S[n].xh < 0 || S[n].score < 0){
break;
}
n++;
}
printf("Total students are %d\n", n);
printf("Input the searching ID:");
scanf("%ld", &ID);
for(int i = 0; i < n; i++){
if(S[i].xh == ID){
flag = 1;
printf("score = %d\n", S[i].score);
}
}
if(flag == 0){
printf("Not found!");
}
return 0;
}
6.2 搜索优化
- 如果数据量大,可以考虑先排序再使用二分查找
- 可以使用哈希表来提高查找效率
- 考虑学号唯一性验证
7. 编程技巧总结
在实际编写这些程序时,我总结了以下几点经验:
- 测试边界条件:特别是对于排序和搜索算法,要测试空输入、单个元素等特殊情况
- 代码复用:将常用功能封装成函数,如排序算法可以单独提取出来
- 防御性编程:检查输入有效性,防止非法输入导致程序崩溃
- 性能考量:虽然这些题目数据量小,但要养成考虑算法复杂度的习惯
对于初学者来说,理解这些基础算法的实现原理非常重要。建议自己手动实现一遍,而不是直接复制代码。遇到问题时,可以使用调试工具逐步跟踪程序执行过程,观察变量值的变化。