作为一名长期奋战在编程一线的开发者,我深知C语言作为计算机科学的基石语言,其重要性不言而喻。今天我将分享五个来自哈工大C语言课程的编程练习,这些题目涵盖了从基础语法到算法设计的多个层面,非常适合用来检验和提升编程能力。每个题目我都会详细解析实现思路,并分享一些实际编码中的经验技巧。
计算一个整数各位数字之和是最基础的编程练习之一,但它能很好地训练我们对循环和模运算的理解。先来看题目要求:
c复制#include <stdio.h>
int compute(int n);
int main()
{
int n,sum;
printf("please input a number:");
scanf("%d",&n);
sum=compute(n);
printf("the result is: %d",sum);
return 0;
}
int compute(int n){
int sum=0;
while(n){
sum+=n%10;
n/=10;
}
return sum;
}
这个实现有几个值得注意的技术点:
while(n)等价于while(n != 0),当n被不断除以10最终变为0时循环结束n%10获取n的个位数,n/=10相当于去掉已经处理的个位数提示:在实际工程中,建议对输入参数进行校验,比如处理INT_MIN的情况,避免未定义行为。
这道题目要求计算只允许一次买卖时的最大股票收益,是动态规划算法的经典入门题。先看实现代码:
c复制#include <stdio.h>
int maxProfit(int prices[],int n);
int main()
{
int n,arr[128];
printf("Please input the array size\n");
scanf("%d",&n);
for(int i=0;i<n;i++){
printf("Please input the %d-th number\n",i);
scanf("%d",&arr[i]);
}
int max=maxProfit(arr,n);
printf("the maxProfit is %d\n",max);
return 0;
}
int maxProfit(int prices[],int n){
int max=prices[0],min=prices[0];
for(int i=1;i<n;i++){
if(max<prices[i]){
max=prices[i];
}
if(min>prices[i]){
min=prices[i];
}
}
return max-min;
}
这个实现存在一个逻辑错误:它简单地计算全局最大值和最小值的差,而没有考虑时间顺序(必须先买后卖)。正确的动态规划解法应该是:
c复制int maxProfit(int prices[], int n) {
if(n <= 1) return 0;
int minPrice = prices[0];
int maxProfit = 0;
for(int i = 1; i < n; i++) {
if(prices[i] < minPrice) {
minPrice = prices[i];
} else if(prices[i] - minPrice > maxProfit) {
maxProfit = prices[i] - minPrice;
}
}
return maxProfit;
}
这个优化后的版本:
这个练习要求我们按ISBN对图书信息进行排序,涉及结构体操作和字符串比较:
c复制#include <stdio.h>
#include <string.h>
struct book{
char name_of_book[100]; //书名
char author[25]; //作者
char ISBN[15];
float price;
};
void order(struct book arr[]);
int main()
{
struct book arr[10];
printf("please input name_of_book author ISBN price:\n");
for(int i=0;i<4;i++){
scanf("%s %s %s %f",arr[i].name_of_book,arr[i].author,arr[i].ISBN,&arr[i].price);
}
order(arr);
for(int i=0;i<4;i++){
printf("%s,%s,%s,%.2f\n",arr[i].name_of_book,arr[i].author,arr[i].ISBN,arr[i].price);
}
return 0;
}
void order(struct book arr[]){
for(int i=0;i<4;i++){
for(int j=i;j<4;j++){
if(strcmp(arr[i].ISBN,arr[j].ISBN)>0){
struct book tmp;
strcpy(tmp.name_of_book,arr[i].name_of_book);
strcpy(tmp.author,arr[i].author);
strcpy(tmp.ISBN,arr[i].ISBN);
tmp.price=arr[i].price;
strcpy(arr[i].name_of_book,arr[j].name_of_book);
strcpy(arr[i].author,arr[j].author);
strcpy(arr[i].ISBN,arr[j].ISBN);
arr[i].price=arr[j].price;
strcpy(arr[j].name_of_book,tmp.name_of_book);
strcpy(arr[j].author,tmp.author);
strcpy(arr[j].ISBN,tmp.ISBN);
arr[j].price=tmp.price;
}
}
}
}
这里有几点值得注意:
结构体交换:直接交换结构体变量比逐个成员交换更高效:
c复制struct book tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
排序算法选择:当前使用的是选择排序,时间复杂度O(n²)。对于大量数据,建议使用qsort:
c复制int compare(const void *a, const void *b) {
return strcmp(((struct book*)a)->ISBN, ((struct book*)b)->ISBN);
}
qsort(arr, n, sizeof(struct book), compare);
输入安全:实际应用中应该限制输入的字符串长度,避免缓冲区溢出
这个数学问题要求计算购买橙子的日均花费:
c复制#include <stdio.h>
int main()
{
int day=1,orange=2,sum=2;
while(orange*2<=100){
day++;
orange*=2;
sum+=orange;
}
printf("%.2f\n",sum*0.8/day);
return 0;
}
这个问题的关键在于理解题目描述的购买模式:
算法分析:
这个实现简洁高效,时间复杂度O(log n)。在实际应用中,我们可能还需要考虑:
最后一个练习实现字符串中的"is"替换为"be":
c复制#include <stdio.h>
int Convert(char b[],char a[]);
int main()
{
char str[1000],a[3]={'i','s'};
gets(str);
int found=Convert(str,a);
if(found){
printf("%d found!\n%s\n",found,str);
}else{
printf("not found!\n");
}
return 0;
}
int Convert(char b[],char a[]){
int i=0,count=0;
while(b[i]!='\0'){
if(b[i]==a[0]&&b[i+1]==a[1]){
b[i]='b';
b[i+1]='e';
count++;
}
i++;
}
return count;
}
这个实现有几个需要注意的地方:
gets()安全问题:gets()函数不检查缓冲区大小,可能导致溢出。实际应用应该使用fgets():
c复制fgets(str, sizeof(str), stdin);
替换逻辑:当前实现会修改原始字符串,如果需要保留原字符串,应该创建副本
边界检查:在检查b[i+1]前应该确保不是字符串结尾
改进建议:
通过这五个练习,我总结了一些C语言编程的实用经验:
初始化:结构体变量最好初始化后再使用,避免未定义行为
c复制struct book b = {0}; // 全部成员初始化为0
传参:大型结构体应该通过指针传递,避免拷贝开销
c复制void print_book(const struct book *b);
柔性数组成员:对于变长数据可以使用柔性数组
c复制struct str {
int len;
char s[];
};
缓冲区安全:始终使用长度受限的函数
c复制char buf[100];
snprintf(buf, sizeof(buf), "format", ...);
字符串比较:strcmp返回0表示相等,注意逻辑判断
c复制if(strcmp(s1, s2) == 0) // 正确
if(!strcmp(s1, s2)) // 正确但可能不直观
字符分类:使用ctype.h中的函数判断字符类型
c复制if(isalpha(c)) // 比直接比较范围更可移植
c复制#include <assert.h>
assert(ptr != NULL);
在实际编程练习中,经常会遇到一些典型问题,下面是我总结的一些常见问题及解决方法:
原因:
解决方法:
检测工具:
预防措施:
分析工具:
优化方法:
常见问题:
解决方案:
良好的编程风格能显著提高代码质量和可维护性:
student_countMAX_SIZEcalculate_average通过这五个编程练习的详细解析,我们不仅复习了C语言的基础知识,还探讨了算法优化、调试技巧和编程风格等进阶话题。在实际开发中,这些基础技能的扎实掌握往往能起到事半功倍的效果。建议读者可以尝试扩展这些练习,比如增加错误处理、提高算法效率或者添加更多功能,以此来进一步提升编程能力。