一、选择判断部分
第一题:
如下代码是否存在风险,并说明原因和修改方案
#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;
}
题解:我们定义两个变量为time1和time2,time1 = 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