C语言经典例题精讲(精选)(上)

第一题(附加)

声明一个指向含有10个元素的数组的指针,其中每个元素是一个函数指针,该函数的返回值是int,参数是int*,正确的是( )

A.(int *p[10])(int*)

B.int [10]*p(int *)

C.int (*(*p)[10])(int *)

D.int ((int *)[10])*p

评析:本题作为阿里巴巴的一道笔试题,它的解答在网上众说纷纭,几乎没有说到点上。

解析:根据题意可知,声明一个指向数组的指针,所以这个指针肯定是数组指针之类的

数组的十个元素都是函数指针,函数指针所指向的函数返回值都是int,参数都是int*。读到这里

我们想一想,有什么数组居然可以装变量,装函数指针,显然没有这样的数组,数组内装的都是数值,所以这个数组装的是函数指针所指向的函数的地址,所以这个数组是什么?是一级函数指针数组,  它指向这十个函数的地址,题目要求就转变为声明一个指向一级函数指针数组的指针,那就是

二级函数数组指针,根据原函数返回值都是int,参数都是int*,我们可以直接写出答案

int (*(*p)[10]) (int *)       

第二题

打印9*9乘法口诀表
#include <stdio.h>
int main()
{
 int i = 0;
 int j = 0;
 for (i = 1; i <= 9; i++)
 {
 for (j = 1; j <= i; j++)
 {
 printf("%d*%d=%2d ", i, j, i * j);
 }
 printf("\n");
 }
 return 0;
}

第三题

打印100~200之间的素数
#include <stdio.h>
#include <math.h>
int main()
{
 int i = 0;
 for (i = 101; i <= 200; i+=2)//因为偶数不是素数,直接跳过
 {
 int j = 0;
 int flag = 1;//定义变量记录当前数是否为素数,假设是素数,变量值为1
 for (j = 2; j <=sqrt(i); j++) //遍历2~当前数的根号
 {
 if (i % j == 0)//存在⼀个数可以整除当前数
 {
 flag = 0;//不是素数
 break;
 }
 }
 if (flag == 1)//是素数就打印
 printf("%d ", i);
 }
 return 0;
}

解析:这是本题最优算法的解答,sqrt(i)是对i的开根号,关于j为什么就取到√i,我在这里稍作解释,√i * √i =i,如果i是合数,就一定至少存在一对除1和它本身的正因数,这两个正因数其中有一个必然小于或等于√i,另一个必然大于或等于√i。只要找到第一个正因数就直接判定是合数。

第四题

输⼊三个整数a,b,c,判断由a,b,c作为三条边组成的三角形,如果不能组成三角形则输出:非三角形;如果是三角形,再继续判断,如果是等边三角形,则输出:等边三角形;如果是等腰三角形,则输出:等腰三角形;否则输出普通三角形。
#include <stdio.h>
int main() 
{
 int a = 0;
 int b = 0;
 int c = 0;
 scanf("%d %d %d", &a, &b, &c);
 if (a + b > c && a + c > b && b + c > a) 
 {
 if (a == b && b == c) 
 {
 printf("等边三⻆形\n");
 }
 else if (a == b || a == c || b == c) 
 {
 printf("等腰三⻆形\n");
 } 
 else
 {
 printf("普通三⻆形\n");
 }
 } 
 else
 {
 printf("⾮三⻆形\n");
 }
 return 0;
}

第五题

输⼊2个整数m和n,计算m和n的最大公约数
#include <stdio.h>
int main()
{
 int m = 0;
 int n = 0;
 scanf("%d %d", &m, &n);
 //辗转相除法
 int k = 0;
 while (k = m % n)//当n不能整除m,即k≠0,更新两个最值重复步骤计算n与m%n的最⼤公约数
 {
 m = n;
 n = k;
 }
 printf("%d\n", n);
 return 0;
}
解析:辗转相除法也称为欧几里得算法,是⼀种⽤来求两个正整数最大公约数的方法。它基于⼀个简单的数 学原理:如果 a 和 b 是两个正整数,且 a>b ,则a和b的最大公约数等于 b 和 a%b ( a 除以 b  所得的余数)的最大公约数。直到余数等于0为止,最后的除数就是两个数的最大公约数。

第六题

输入2个整数m和n,计算m和n的最小公倍数

#include <stdio.h>
int main()
{
 int m = 0;
 int n = 0;
 //输⼊
 scanf("%d %d", &m, &n);//18 24
 int k = 0;
 int mul = m*n;
 while (k = m % n)//辗转相除法求得最⼤公约数
 {
 m = n;
 n = k;
 }
 printf("%d\n", mul/n);
 return 0;
}

解析:最小公倍数可以由两数乘积除以两数的最大公约数求得

第七题

计算 1/1 - 1/2 + 1/3 - 1/4 + 1/5 - … + 1/99 - 1/100 的值
#include<stdio.h>
int main()
{
	float i,sum=0;
	for(i=1;i<=100;i+=2)
	{		
	sum+=1/i-1/(i+1);			
	}
	printf("%f",sum);	
	return 0;
} 

第八题

输入10个整数,使用冒泡排序对数组内容进行升序排序
#include <stdio.h>
int main()
{
 int arr[10] = { 0 };
 int sz = sizeof(arr) / sizeof(arr[0]);
 //输⼊数据
 int i = 0;
 for (i = 0; i < sz; i++)
 {
 scanf("%d", &arr[i]);
 }
 //冒泡排序
 for (i = 0; i < sz-1; i++)
 {
 int j = 0;
 int flag = 1;//假设待排序的数据已经有序
 //对未排序元素进⾏冒泡排序
 for(j=0; j < sz-1-i; j++)
 {
 //当前相邻元素顺序错误,进⾏交换
 if (arr[j] > arr[j + 1])
 {
 flag = 0;//只要交换说明正在排序的数据不是有序的
 int tmp = arr[j];
 arr[j] = arr[j + 1];
 arr[j + 1] = tmp;
 }
 }
 //当前序列已经是升序状态,结束循环
 if (flag == 1)
 break;
 }
 //输出
 for (i = 0; i < sz; i++)
 {
 printf("%d ", arr[i]);
 }
 return 0;
}

第九题

某地发生了一件盗窃案,警察通过排查确定盗窃者必为4个嫌疑人的一个。
以下为4个嫌疑人的供词:
A说:不是我。
B说:是C。
C说:是D。
D说:C在胡说
已知3个人说了真话,1个人说的是假话。
现在请根据这些信息,写⼀个程序来确定到底谁是盗窃者。
#include<stdio.h>
int main()
{
 int thieves = 0;
 //分别假设盗窃者是a,b,c,d,看谁是盗窃者时满⾜3个⼈说了真话,⼀个⼈说了假话
 for (thieves = 'A'; thieves <= 'D'; thieves++)
 {
 //判断当前嫌疑⼈作为盗窃者是否成⽴
 if ((thieves != 'A') + (thieves == 'C') + (thieves == 'D') + (thieves !='D')==3)
 printf("盗窃者是:%c", thieves);
 }
 return 0;
}

解析:本题题型新颖,利用字符串和条件筛选即可写出程序

第十题

写⼀个代码打印1~100000之间的所有的自幂数
#include <stdio.h>
#include <math.h>
int main()
{
 int i = 0;
 for (i = 1; i <= 100000; i++)
 {
 //判断i是否是⾃幂数
 //1. 计算i的位数n
 int n = 1;
 int tmp = i;
 while (tmp/10)
 {
 n++;
 tmp /= 10;
 }
 //2. 计算i的每⼀位的n次⽅之和
 tmp = i;
 int sum = 0;
 while (tmp)
 {
 sum += pow(tmp % 10, n);
 tmp /= 10;
 }
 //3. 输出
 if (sum == i)
 printf("%d ", i);
 }
 return 0;
}

解析:一个n位自然数等于自身各个数位上数字的n次幂之和,则称此数为自幂数

第十一题

输入一个整数n,打印对应2*n-1行的菱形图案
比如,输入7,输出如下图案,图案总共13行

#include <stdio.h>
int main()
{
 int n = 0;
 //输⼊
 scanf("%d", &n);
 //打印
 //打印上半部分的n⾏
 int i = 0;
 for (i = 0; i < n; i++)
 {
 //打印空格
 int j = 0;
 for (j = 0; j < n-1-i; j++)
 {
 printf(" ");
 }
 //打印*
 for (j = 0; j < 2*i+1; j++)
 {
 printf("*");
 }
 printf("\n");
 }
 //打印下半部分的n-1⾏
 for (i = 0; i < n; i++)
 {
 //打印空格
 int j = 0;
 for (j = 0; j <=i; j++)
 {
 printf(" ");
 }
 //打印*
 for (j = 0; j < 2*(n-1-i)-1; j++)
 {
 printf("*");
 }
 printf("\n");
 }
 return 0;
}

解析:根据n找出各行空格和*数量的规律

第十二题

汽水已知1瓶汽水1元,2个空瓶可以换一瓶汽水,输入整数n(n>=0),表示n元钱,计算可以喝多少瓶汽水
#include <stdio.h>
int main()
{
 int n = 0;
 int total = 0;//表⽰总共能喝多少汽⽔
 int empty = 0;//表⽰⼿⾥的空瓶数
 scanf("%d", &n);
 //买n瓶汽⽔,并且喝完后剩余n个空瓶
 total += n;
 empty += n;
 
 //重复⽤空瓶⼦购买汽⽔直⾄空瓶不够2个
 while (empty >= 2)
 {
 total += empty / 2;
 empty = empty / 2 + empty % 2;
 }
 printf("%d\n", total);
 return 0;
}

解析: 第一次循环

            total += empty / 2  计算了 第一次喝的n瓶+第二次喝的n/2瓶

            empty = empty / 2 + empty % 2 计算第三次喝的瓶数就需要计算第二次剩余的瓶数 

           第二次循环      

            total += empty / 2  计算了  第一次喝的n瓶+第二次喝的n/2瓶 + 第三次喝的瓶数

            empty = empty / 2 + empty % 2 计算第四次喝的瓶数就需要计算第三次剩余的瓶数

            ..........

           直到剩余的空瓶数小于2,也就是1和0,换不了汽水了,循环结束        

第十三题

写⼀个函数Swap,可以交换两个整数的内容。
#include <stdio.h>
//传⼊两个整型指针参数,进⾏交换
void Swap(int* pa, int* pb)
{
 int tmp = *pa;
 *pa = *pb;
 *pb = tmp;
}
int main()
{
 int a = 0;
 int b = 0;
 //输⼊
 scanf("%d %d", &a, &b);
 //将需要交换值的两个整数变量的地址作为参数传递给函数,进⾏交换
 Swap(&a, &b);
 //输出
 printf("%d %d\n", a, b);
 return 0;
}

解析:由于函数只能返回一个值,所以无法使用return实现两个数的交换

          除了利用指针进行交换,这里还可以定义两个全局变量进行交换,也可以使用数组    

因为数组的本质也是指针

第十四题     

输入2个整数m和n,写⼀个函数average,求2个整数的平均值,考虑整数过大溢出的问题    

#include <stdio.h>
//⽅法1:这种⽅式数字太⼤是会溢出
int average(int x, int y)
{
 return (x + y) / 2;
}
//⽅法2:改进
int average(int x, int y)
{
 return x + (y-x) / 2;
}
int main()
{
 int m = 0;
 int n = 0;
 //输⼊
 scanf("%d %d", &m, &n);
 //求平均值
 int avg = average(m, n);
 //输出
 printf("%d\n", avg);
 return 0;
}

  解析:本题主要强调了求平均的保险求法  x + (y-x) / 2,这样就不会溢出,从而防止截断错误
 

第十五题

写⼀个函数Strlen,可以求字符串长度
#include <stdio.h>
int Strlen(const char* str)
{
 //定义变量计数
 int count = 0;
 //利⽤指针遍历字符串
 while (*str)
 {
 //计数加⼀
 count++;
 
 //字符串指针后移,遍历下⼀个字符
 str++;
 }
 return count;
}
int main()
{
 char arr[31] = { 0 };
 //输⼊字符串
 scanf("%[^\n]s", arr);
 //获取字符串⻓度
 int len = Strlen(arr);
 printf("%d\n", len);
 return 0;
}

解析:其中^表示"非",[^\n]表示读入换行字符就结束读入。这个是scanf的正则用法,我们都知道scanf不能接收空格符,一接受到空格就结束读入,所以不能像gets()等函数一样接受一行字符串,但是使用%[^\n]就可以读取一行,直到碰到’\n’才结束读入。

本题是对strlen的模拟实现,strlen本质就是指针读取,读取字符串首元素地址,没遇到\0时,地址加一,计数加一。遇到\0停止,并返回数量大小。

法二:利用递归求

#include <stdio.h>
//使⽤递归的⽅式解决
int Strlen(const char* str)
{
 //递归结束条件
 if (*str == '\0')
 return 0;
 else
 //返回1加后⾯⼦字符串的和
 return 1 + Strlen(str + 1);
}
int main()
{
 char arr[31] = { 0 };
 //输⼊字符串
 scanf("%[^\n]s", arr);
 //获取字符串⻓度
 int len = Strlen(arr);
 printf("%d\n", len);
 return 0;
}

第十六题

输入⼀个字符串,写⼀个函数将⼀个字符串的内容逆序过来
#include <stdio.h>
#include <string.h>
void reverse(char* str)
{
 //利⽤库函数求得字符串⻓度
 int len = strlen(str);
 //定义两个字符串指针分别指向字符串⾸位和末位
 char* left = str;
 char* right = str + len - 1;
 //当左指针在右指针左边时,进⾏交换
 while (left < right)
 {
 char tmp = *left;
 *left = *right;
 *right = tmp;
 
 //两个指针相向移动⼀位
 left++;
 right--;
 }
}
int main()
{
 char arr[31] = { 0 };
 //输⼊字符串
 scanf("%[^\n]s", arr);
 //对字符串进⾏逆序
 reverse(arr);
 printf("%s\n", arr);
 return 0;
}

解析:利用指针进行交换从而实现倒序

第十七题

输入⼀个整数m,求这个整数m的每⼀位之和,并打印
#include <stdio.h>
int digit_sum(int m)
{
 //定义变量记录每⼀位的和
 int s = 0;
 //当前数还不为0,获取其个位数加到s中
 while (m)
 {
 s += m % 10;
 //删除其个位数
 m /= 10;
 }
 //返回每⼀位的和
 return s;
}
int main()
{
 int m = 0;
 //输⼊整数
 scanf("%d", &m);
 //获取每⼀位的和
 int ret = digit_sum(m);
 printf("%d\n", ret);
 return 0;
}

解析: m%10 :取m的最后一位

            m/10   :去除m的最后一位

第十八题

输入⼀个字符串,判断这个字符串是否是回文字符串
#include <stdio.h>
#include <string.h>
int is_palindrome_string(char arr[])
{
 //获取字符串⻓度
 int n = strlen(arr);
 //定义两个指针分别指向字符串的⾸位和末位
 char* left = arr;
 char* right = arr + n - 1;
 
 //当两个指针相对位置未发⽣变化时对其进⾏判断
 while (left < right)
 {
 //两字符不相等,返回0,表⽰字符串不是回⽂串
 if (*left != *right)
 return 0;
 //两指针相向移动⼀位
 left++;
 right--;
 }
 //返回1,表⽰字符串是回⽂串
 return 1;
}
int main()
{
 char arr[31]; 
 //输⼊
 scanf("%[^\n]s", arr);
 //判断字符串是否为回⽂串
 int ret = is_palindrome_string(arr);
 
 if (ret == 1)
 printf("Yes\n");
 else
 printf("No\n");
 return 0;
}

第十九题

写⼀个函数my_strcpy,实现拷贝字符串的功能
#include <stdio.h>
void my_strcpy(char* dest, const char* src)
{
 // 拷⻉源字符串的内容到⽬标字符串中
 while (*src != '\0') 
{
 *dest = *src;
 dest++;
 src++;
 }
 // 拷⻉源字符串的结束标志
 *dest = '\0';
}
int main()
{
 char arr1[] = "hello bite";
 char arr2[20] = { 0 };
 
 //将字符串arr1中的内容拷⻉到字符串arr2中
  my_strcpy(arr2, arr1);
 //打印字符串arr2
 printf("%s\n", arr2);
 return 0;
}

第二十题

输入两个升序排列的序列,将两个序列合并为一个有序序列并输出。
输入包含三行,第一行包含两个正整数n, m,用空格分隔。n表示第二行第⼀个升序序列中数字的个 数,m表示第三行第二个升序序列中数字的个数。其中 1<=n<=30, 1<=m<=30
第二行包含 n 个整数,用空格分隔。
第三行包含 m 个整数,用空格分隔。
输出为一行,输出长度为n+m的升序序列,即长度为n的升序序列和长度为m的升序序列中的元素重 新进行升序序列排列合并。
#include <stdio.h>
int main() {
 int n = 0;
 int m = 0;
 //输⼊//可以使用变长数组
 scanf("%d %d", &n, &m);
 int arr1[30] = {0};
 int arr2[30] = {0};
 int arr3[60] = {0};
 //输⼊arr1
 int i = 0;
 for (i = 0; i < n; i++)
 {
 scanf("%d", &arr1[i]);
 }
 //输⼊arr2
 for (i = 0; i < m; i++)
 {
 scanf("%d", &arr2[i]);
 }
 //合并arr1和arr2到arr3数组中
 i = 0; //i遍历arr1
 int j = 0;//j遍历arr2
 int k = 0;//k遍历arr3
 
 //合并两个数组的元素⾄arr3数组中
 while (i < n && j < m)
 {
 //⽐较两个指针指向的元素⼤⼩
 //arr1数组当前元素较⼩,将此值放⼊arr3数组并令i和k后移
 if (arr1[i] < arr2[j])
 {
 arr3[k] = arr1[i];
 i++;
 k++;
 }
 //arr2数组当前元素较⼩,将此值放⼊arr3数组并令j和k后移
 else
 {
 arr3[k] = arr2[j];
 j++;
 k++;
 }
 }
 
 //若指针i越界,则arr1数组所有元素已经放⼊arr3数组
 //arr2数组中有剩余元素,将它们依次放⼊arr3数组
 if (i == n)
 {
 for (; j < m; j++)
 {
 arr3[k] = arr2[j];
 k++;
 }
 }
 
 //若指针j越界,则arr2数组所有元素已经放⼊arr3数组
 //arr1数组中有剩余元素,将它们依次放⼊arr3数组
 if (j == m)
 {
 for (; i < n; i++)
 {
 arr3[k] = arr1[i];
 k++;
 }
 }
 //打印
 for (k = 0; k < m + n; k++)
 {
 printf("%d ", arr3[k]);
 }
 return 0;
}

解析:

定义一个长度为 n+m 的新数组 arr3 ,将两个有序数组合并到 arr3 数组中,使得 arr3 数组有
序。
1. 定义一个长度为 n+m 的新数组 arr3 ,以及两个指针 i 和 j ,分别指向 arr1 和 arr2 的开头;
2. 定义一个计数器 k ,初始化为 0;
3. 比较 arr1[i] 和 arr2[j] ,将较小的数存入 arr3 中,并将对应指针向后移动一位;
4. 将计数器 k 加 1;
5. 重复步骤 3 和 4 直到某个指针越界;
6. 将另⼀个指针指向的数组的剩余所有元素依次存入 arr3 中,存入同时将计数器 k 的值自增 1;
7. 至此, arr3 数组中的元素为 arr1 数组和 arr2 数组合并并排序后的结果,按序输出前 k 个元素即可。

第二十一题

实现一个函数,可以左旋字符串中的k个字符。

例如:

ABCD左旋一个字符得到BCDA

ABCD左旋两个字符得到CDAB

方法一

void leftRound(char * src, int time)
{
	int i, j, tmp;
  int len = strlen(src);
  time %= len; //长度为5的情况下,旋转6、11、16...次相当于1次,7、12、17...次相当于2次,以此类推。
	for (i = 0; i < time; i++) //执行k次的单次平移
	{
		tmp = src[0];
		for(j = 0; j < len - 1; j++) //单次平移
		{
			src[j] = src[j + 1];
		}
		src[j] = tmp;
	}
}

方法二

void leftRound(char * src, int time)
{
	int len = strlen(src);
	int pos = time % len; //断开位置的下标
	char tmp[256] = { 0 }; //更准确的话可以选择malloc len + 1个字节的空间来做这个tmp
	
	strcpy(tmp, src + pos); //先将后面的全部拷过来
	strncat(tmp, src, pos); //然后将前面几个接上
	strcpy(src, tmp); //最后拷回去
}

方法三

void reverse_part(char *str, int start, int end) //将字符串从start到end这一段逆序
{
	int i, j;
	char tmp;

	for (i = start, j = end; i < j; i++, j--)
	{
		tmp = str[i];
		str[i] = str[j];
		str[j] = tmp;
	}
}

void leftRound(char * src, int time)
{
	int len = strlen(src);
	int pos = time % len;
	reverse_part(src, 0, pos - 1); //逆序前段
	reverse_part(src, pos, len - 1); //逆序后段
	reverse_part(src, 0, len - 1); //整体逆序
}

解析:还有一种方法使用双向链表,会在下一篇博客讲解。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你好,赵志伟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值