C语言刷题日记(附详解)(1)

一、选择判断部分

第一题:

如下代码是否存在风险,并说明原因和修改方案

#include<stdio.h>
int main()
{
	char* str = "hello world";
	*str = 'a';
	return 0;
}

思路提示:这种形式的字符串存储在什么区域呢?是否真的有写入权限?

答案:运行会报错!!!

因为这种形式的字符串是存储在常量区的,而对于储存在常量区的数据只能进行读取,并不能进行写入,所以此时直接对str所指向的值进行修改是不行的。当然,如果我们先将字符串存放在数组形式中,就可以对数据进行修改啦:

#include<stdio.h>
int main()
{
	char str[] = "hello world";
	char* pstr = str;
	*str = 'a';
	return 0;
}

第二题:

若定义:int a[2][3]={1,3,5,7,9,11},以下描述正确的是(      )

A:*(a + 1)为元素7的地址

B:(a[1] + 1)的值是5

C:**(a + 1) + 2值是11

D:a[0]和a不同

答案:A

题解:在二维数组中,*(a + 1)代表的是第二列首元素的地址,而第二列首元素对应的值为7,则A是正确的。而(a[1] + 1)所代表的是第二列的第二个元素的地址,对应的值应该为9的地址(需要再解引用一次才是9),故B是错误的。**(a + 1) + 2中**(a + 1)所代表的是第二列首元素,此值为7,+2得9,并不是11,所以C是错误的。a所代表的是首元素地址,也就是数组首地址,而a[0]代表的是第一列的首元素地址,同样也是数组首地址,故a和a[0]其实是相同的。

第三题:

请问下列代码的输出是多少 (      )

#include<stdio.h>
int main()
{
	int m[] = { 1,2,3,4,5,6,7,8,9,0 };
	int(*p)[4] = (int(*)[4])m;
	printf("%d", p[1][2]);
	return 0;
}

思路提示:int(*p)[n]代表的是什么?而int* p[n]代表的又是什么?p[1][2]中p这个二维数组是对一维数组m以什么形式的拆分所得到的呢?

答案:输出结果是7。

题解:int∗ p[n] 表示的是大小为n的,用来存储指针变量的指针数组
           int(∗p)[n] 表示的是一个指向有n个元素的数组的
数组指针

而对于(int(*)[4])m, 我们可以联想一下:(int)m 就是将m强制类型转换为int型,而此时(int(*)[4])m也与(int)m的格式相似,而(int(*)[4])的格式代表了一个以4位元素组成一组的数组指针,此时将m强制类型转换为它,那么m现在就应该是{{1,2,3,4},{5,6,7,8},{9,0, , }};而p[1][2]所指向的就是第二列的第三个元素也就是7

第四题:

说出以下代码的错误之处,并说明原因:

char* getmemory(void)
{
	char p[] = "hello world";
	return p;
}
void test()
{
	char* str = NULL;
	str = getmemory();
	printf("%s", str);
}
int main()
{
	test();
	return 0;
}

思路提示:在getmemory函数中定义的p值在什么区域?是全局变量?还是局部变量?还是静态变量?

答案:运行不报错,但也不会输出hello world,而是随机输出乱码。

题解:因为当我们将str置空之后,将str传入getmemory函数中,虽然getmemory函数中期望的定义并返回的p为hello world,但p是一个局部变量,当退出函数时,p会自动销毁,而此时我们再打印str时,会发现str不再是NULL了,但也不会是hello world,而是随机的乱码。

第五题:

请问下列代码的输出是什么(      )

int main() 
{
    char* str = "hello.com";
    int i;
    str = "hello world!";
    int len = strlen(str);
    printf("%d \n", len);
    printf("%c \n", *(str + 4));
    printf("%s \n", (str + 6));
    return 0;
}

答案

12
o
world!

题解:因为将str重新赋值后长度发生了改变,此时长度为12,*(str + 4)对应的是str字符数组中第五个字符,也就是o,而打印字符串形式(str + 6)代表的就是跳过str前六个字符再进行打印,也就是world!。

二、编程题部分

第一题:统计字母,空格,字符数!

输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数。

输入样例:

aaabbb11223344   @@@@

输出样例:

数字个数为:8
字母个数为:6
空格个数为:3
字符个数为:4

思路提示:想要做到分别统计出一段字符中的英文字母,空格,数字以及其他字符个数,我们可以定义出四个计数器,分别来统计数字,字母,空格,字符的个数。然后我们可以定义一个整型变量sz用来存放这段字符的长度通过一个for循环将这段字符的每一个字符遍历一边,并且对每一个字符进行判断,使相应的计数器自增,最后按格式输出它们的个数。

答案

int main()
{
	char arr[100];
	fgets(arr, 100, stdin);
	int sz = strlen(arr) - 1;
	int shunum = 0;
	int zinum = 0;
	int kongnum = 0;
	int funum = 0;
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		if (arr[i] >= '0' && arr[i] <= '9')
			shunum++;
		else if ((arr[i] >= 'a' && arr[i] <= 'z') || (arr[i] >= 'A' && arr[i] <= 'Z'))
			zinum++;
		else if (arr[i] == ' ')
			kongnum++;
		else
			funum++;
	}
	printf("数字个数为:%d\n", shunum);
	printf("字母个数为:%d\n", zinum);
	printf("空格个数为:%d\n", kongnum);
	printf("字符个数为:%d\n", funum);
	return 0;
}

需要注意的是:在对定义的字符串赋值时,我们使用的是fgets函数而并非scanf("%s"),这是因为使用scanf的形式进行赋值,在遇到空格时会终止赋值,所以无法判断空格的个数。而fgets函数在进行赋值时,大部分情况下不会被终止只有遇到换行符号"\n",或者已经达到输入大小的限度才会终止

由图我们可以知道,fgets函数的原型,以及fgets传参的参数类型。

char* fgets(char* str, int num, FILE* stream)

第一个参数char* str代表的是:容器的地址

第二个参数int num代表的是:容器的大小

第三个参数FILE* stream代表的是:从哪里开始读取

在题中我们使用的格式为fgets(arr, 100, stdin);这段代码就代表,我们需要为地址为arr的容器进行赋值,并且容器最大为100,从0(键盘)开始读取。(stdin代表输入流,表示从0(键盘)开始记录)

第二题:然后是几点?

我们使用四个数字来代表时间,比如1209代表12点零9分。而此题中,你的代码需要计算之前的时间加上经过的时间后的到的现在的时间

读入两个数字,一个数字代表之前的时间,另一个数字代表经过的时间,而最后输出现在的时间。比如我们输入:1130 160;   输出:1410;

思路提示:如果直接将时间用一个数来表示,那么将本是100进1的小时位改成60进1的小时位未免过于繁琐,我们是否可以尝试一下将时间分成两个数字分别代表小时和分钟呢?这样或许会方便很多哦~

答案

int main()
{
	int time;
	int minute;
	scanf("%d %d", &time, &minute);
	int time1 = time / 100;//用来表示小时
	int time2 = time % 100;//用来表示分钟
	if (minute > 0)//用来判断经过时间>0
	{
		while (minute > 60)
		{
			time1++;
			minute -= 60;
		}
	}
	else if (minute < 0)//用来判断经过时间<0
	{
		while (minute < 0)
		{
			time1--;
			minute += 60;
		}
	}
	time2 += minute;
	if (time2 >= 60)//将>60的分钟进位
	{
		time1++;
		time2 -= 60;
	}
	if (time1 > 24)
		time1 -= 24;
	if(time2<10)//补充分钟为个位时,中间缺少的0
		printf("%d0%d", time1, time2);
	else
		printf("%d%d", time1, time2);
	return 0;
}

题解我们定义两个变量为time1time2time1 = time / 100;代表只取两个高位数,也就是小时而time2 = time % 100;代表只取两个低位数,也就是分钟。之后我们使用if选择语句和while循环语句,将流逝的时间minute分别加在小时位和分钟位上当minute>60时,不断地将minute自减60,并且同时为小时位+1同时也要注意,此题中输入流逝时间为负数的情况(难道这就是传说中的反方向的钟?!)同样的当minute<0时,不断地将minute自增60,并且同时小时位-1。最后将剩余的时间加在分钟位上,再进行一次time2是否>=60的判断,然后此题就解决啦~怎么样,只要想到将小时和分钟分开算,其实还是很简单的呢。

第三题:阶乘计算升级版

本题要求实现一个打印非负整数的阶乘的函数

要求其中N是用户传入的参数,其值不超过1000。如果N是非负整数,则该函数必须在一行中打印出N!的值,否则打印“Invalid input”

整体代码:

#include <stdio.h>

void Print_Factorial(const int N)
{
    /* 你需要编写并实现此代码 */


}

int main()
{
    int N;

    scanf("%d", &N);
    Print_Factorial(N);
    return 0;
}

思路提示:平时我们使用的阶乘计算方法很简单,比如我们此时要求5!,只需要定义一个整形变量初始化值为1,for(i=1;i<=5;i++),使此变量在for循环中与i分别相乘,就能求出5!

int main()//阶乘计算初阶版
{
	int a;
	scanf("%d", &a);
	int i = 0;
	int sum = 1;
	for (i = 1; i <= a; i++)
	{
		sum *= i;
	}
	printf("%d", sum);
	return 0;
}

但这是之前的初阶版,对于比较小的数字求阶乘还勉强能做到,但如果要求的阶乘位数很高呢,高到我们用long long型变量也接收不了,那该怎么办呢?给大家一个提示:我们可以定义一个很大的数组,用数组中的元素分别放置阶乘结果的每一位数字,这样无论结果位数多大,我们也能轻松自如的接收啦~

(在此大家可能觉得,就算知道了用数组接收每一位数字的思路,但想要实现能够计算1000!的函数还是太难太繁琐了,确实这是一个较难的题(也可能是我菜(ㄒoㄒ)),那让我们先做一个稍微比较好理解的题来练练手吧~)

练手题:大数加法

利用数组的形式存储每一位数字并进行运算。

运行效果:

大数加法的核心思想与阶乘计算升级版的思想是一致的,我们需要使用数组来接收每一位数字并运算。因为是大数加法,所以输入的数字也可能非常大,所以输入的数字也需要使用数组来存储

因为我们需要使每一位数字存储在数组相应的位置,并且这一位数字必须是个位数,所以我们可以将其转换成字符形式这样输入运算数时,自动就将每一位运算数当作字符存储在相应位置了~~~那么有了思路,具体来操作一下:我们定义三个char型数组,char a[10001]用来接收大数运算数(存入时为字符类型),char arr1[10001]和char arr2[10001]用来接收两个大数运算数(接收时将每一位都转换成数字类型)

char a[10001] = { 0 };//用于存储运算数(字符形式)
char arr1[10001] = { 0 };//用于接收运算数(接收需转换成数字形式)
char arr2[10001] = { 0 };//用于接收运算数(接收需转换成数字形式)
printf("输入两个运算数:\n");
scanf("%s", a);
for (i = strlen(a) - 1; i >= 0; i--)
{
	arr1[tmp++] = a[i] - '0';//将字符转换为数字
}
scanf("%s", a);
for (tmp = 0,i = strlen(a) - 1; i >= 0; i--)
{
	arr2[tmp++] = a[i] - '0';//将字符转换为数字
}

解决了大数运算数的传输与接收,那么再让我们来思考一下,大数运算数如何通过数组的形式进行每一位的运算呢

让我们来举一个例子,并且逐步分析,来看一下运算的过程

比如此时我们输入的arr1为97,输入的arr2为75,那么两者运算过程就会是:

 个 十 百
 7  9  0
 5  7  0
 (第一次运算)
 tmp = arr1[i] + arr2[i] + up; (tmp = 7 + 5 + 0 = 12)
 arr1[i] = tmp % 10; (arr1[i] = 12 % 10 = 2)
 up = tmp / 10;  (up = 12 / 10 = 1)
 (第二次运算)
 tmp = arr1[i] + arr2[i] + up; (tmp = 9 + 7 + 1 = 17)
 arr1[i] = tmp % 10; (arr1[i] = 17 % 10 = 7)
 up = tmp / 10;  (up = 17 / 10 = 1)
 (第三次运算)
 tmp = arr1[i] + arr2[i] + up; (tmp = 0 + 0 + 1 = 1)
 arr1[i] = tmp % 10; (arr1[i] = 1 % 10 = 1)
 up = tmp / 10;  (up = 1 / 10 = 0)
 (结束运算)
 结果为:
 arr1[0] = 2 arr1[1] = 7 arr1[2] = 1;(97 + 75 = 172)

由此,我们就能够直观的看出运算过程到底是何种方式~我们可以定义两个整型变量tmp和up来辅助运算,tmp代表此次两个位数相加的和up代表两个位数相加后是否需要进1而arr1[i]用来存储此次运算后得到的此位数字(只需要取10的余数即可)。将这个思路转化为运算公式的形式就会是这样:

for (i = 0; i < 10000/*运算的最大位数*/; i++)
{
	tmp = arr1[i] + arr2[i] + up;//两数相加运算
	arr1[i] = tmp % 10;
	up = tmp / 10;
}

怎么样?转化为运算公式之后,是不是感觉一下简便了很多呢~接下来就只剩下最后的打印工作了此处我们需要注意的是,之前我们对arr1和arr2进行存储时,使用的是逆序的存储,这样的目的是使低位数在前,高位数在后,这样我们就能做到使用for循环运算时先计算低位数,再计算高位数相应的,得出的结果也理所当然的是逆序,所以打印时我们需要从后往前打印,才能得出真正的结果~

答案

int main()
{
	char a[10001] = { 0 };//用于存储运算数(字符形式)
	char arr1[10001] = { 0 };//用于接收运算数(接收需转换成数字形式)
	char arr2[10001] = { 0 };//用于接收运算数(接收需转换成数字形式)
	int tmp = 0;
	int up = 0;
	int i = 0;
	printf("输入两个运算数:\n");
	scanf("%s", a);
	for (i = strlen(a) - 1; i >= 0; i--)
	{
		arr1[tmp++] = a[i] - '0';//将字符转换为数字
	}
	scanf("%s", a);
	for (tmp = 0,i = strlen(a) - 1; i >= 0; i--)
	{
		arr2[tmp++] = a[i] - '0';//将字符转换为数字
	}
	printf("运算结果为:\n");
	for (i = 0; i < 10000; i++)
	{
		tmp = arr1[i] + arr2[i] + up;//两数相加运算
		arr1[i] = tmp % 10;
		up = tmp / 10;
	}
	for (i = 10000; i >= 0; i--)
	{
		if(arr1[i]!=0)
			for(i;i>=0;i--)//逆序打印出真正结果
		printf("%d", arr1[i]);
	}
	return 0;
}

那么学会了大数相加,让我们收回思路,继续解决阶乘计算升级版吧~

还是同样的思路,我们需要定义一个足够大的char型数组用来后续存放i的阶乘,然后先分别对<0求阶乘和对0,对1求阶乘的情况进行解决:

#include <stdio.h>
void Print_Factorial(const int N)
{
    char a[5000] = { 1 };//因为是相乘而不是相加,如果初始化为0则无法进行相乘
    int tmp = 0;         //所以我们将第一个元素初始化为1,使其能够正常运算
    int i = 0;           //并且其他的元素仍为0,使用进位来增大其余元素
    int j = 0;
    int up = 0;
    if (N < 0)
        printf("Invalid input");
    else if (N == 0 || N == 1)
        printf("1");
    else
    {
        ...
    }

而else中的内容,就是求阶乘的运算了~

同样的,让我们举个例子,逐步分析一下应该如何运算:

 从 i = 2 开始 i<=5 i++ 分别求2! 3! 4! 5!
 (计算2!)//2 0 0
 tmp = a[j] * i + up; (tmp = 1 * 2 + 0 = 2)
 a[j] = tmp % 10; (a[j] = 2 % 10 = 2)
 up = tmp / 10; (up = 2 / 10 = 0)
 (计算3!)//6 0 0
 tmp = a[j] * i + up; (tmp = 2 * 3 + 0 = 6)
 a[j] = tmp % 10; (a[j] = 6 % 10 = 6)
 up = tmp / 10; (up = 6 / 10 = 0)
 (计算4!)//4 2 0(逆序)
 tmp = a[j] * i + up; (tmp = 6 * 4 + 0 = 24)
 a[j] = tmp % 10; (a[j] = 24 % 10 = 4)
 up = tmp / 10; (up = 24 / 10 = 2)
 tmp = a[j] * i + up; (tmp = 0 * 4 + 2 = 2)
 a[j] = tmp % 10; (a[j] = 2 % 10 = 2)
 up = tmp / 10; (up = 2 / 10 = 0)
 (计算5!)//0 2 1(逆序)
 tmp = a[j] * i + up; (tmp = 24 * 5 + 0 = 120)
 a[j] = tmp % 10; (a[j] = 120 % 10 = 0)
 up = tmp / 10; (up = 120 / 10 = 12)
 tmp = a[j] * i + up; (tmp = 0 * 5 + 12 = 12)
 a[j] = tmp % 10; (a[j] = 12 % 10 = 2)
 up = tmp / 10; (up = 12 / 10 = 1)
 tmp = a[j] * i + up; (tmp = 0 * 5 + 1 = 1)
 a[j] = tmp % 10; (a[j] = 1 % 10 = 1)
 up = tmp / 10; (up = 1 / 10 = 0)

(整理这个真的要把我这个小菜鸡累毁了!!!各位看到这里如果不介意的话,点点收藏点点赞吧 T A T)

统计成公式就是:

for (i = 2; i <= N; i++)
{
    for (up = 0, j = 0; j <= 5000; j++)
    {
        tmp = a[j] * i + up;
        a[j] = tmp % 10;
        up = tmp / 10;
    }
}

最后我们只需要将运算出来的数组给逆序打印出来就好啦~

答案

#include <stdio.h>
void Print_Factorial(const int N)
{
    char a[5000] = { 1 };
    int tmp = 0;
    int i = 0;
    int j = 0;
    int up = 0;
    if (N < 0)
        printf("Invalid input");
    else if (N == 0 || N == 1)
        printf("1");
    else
    {
        for (i = 2; i <= N; i++)
        {
            for (up = 0, j = 0; j <= 5000; j++)
            {
                tmp = a[j] * i + up;
                a[j] = tmp % 10;
                up = tmp / 10;
            }
        }
        for (i = 5000; i >= 0; i--)
        {
            if (a[i] != 0)
                for (i; i >= 0; i--)
                    printf("%d", a[i]);
        }
    }
}
int main()
{
    int N;
    scanf("%d", &N);
    Print_Factorial(N);
    return 0;
}

怎么样,是不是看起来比大数运算还要更加简便一些呢?但其实这段乘法的公式是真的难!!

最后我们算出1000的阶乘为......好可怕的数字......不愧是计算机,这都能算出来~

那么今天的刷题日记就给大家分享到这啦~如果有哪里说的不对的或者讲解的不清楚的,还请大家多多在评论区指出意见哦~我也会多多吸取教训,多多学习的~那么我们下一篇再见啦ヾ(•ω•`)o

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值