目录
前言
作者写本篇文章旨在检测自己的在C语言初阶阶段的学习成果,并把我在这个过程中遇到的困难的将解决方法和心得分享给大家。由于作者本人还是一个刚入门的菜鸟,不可避免的会出现一些错误和观点片面的地方,非常感谢读者指正!希望大家能一同进步,成为大牛,拿到好offer。
本篇是在学习完C语言初阶后的测试题目讲解,分为单选题和编程题两部分。将会采用画图的方式分析,是对测试的深度剖析,是为了能让读者和自己清楚对C语言掌握的程度。希望在这个过程中一同进步。
日志
- 2024.6.2号首发
1.选择题
1.1
下列程序执行后,输出的结果为()
#include <stdio.h>
int cnt = 0;
int fib(int n) {
cnt++;
if (n == 0)
return 1;
else if (n == 1)
return 2;
else
return fib(n - 1) + fib(n - 2);
}
void main()
{
fib(8);
printf("%d", cnt);
}
A. 41
B. 67
C. 109
D. 177
正确答案选B
接下来进行分析
分析函数内部细节
画图分析递归。但是这里有一个细节,那就是fib递归的返回值并不重要。因为题目问的是cnt的值是多少,而cnt的初始值是0,每进入一次fib函数就+1。也就是我们不必算递归的具体分析,只需要算在递归的过程中调用了多少次fib函数。
可以看到fib(8)递归一共会调用67次fib函数。最终进入fib函数67次,cnt加了67次。因此输出67
1.2
在上下文和头文件均正常情况下,以下程序的输出结果是()
#include <stdio.h>
int main()
{
int x = 1;
do
{
printf("%2d\n", x++);
} while (x--);
return 0;
}
A. 1
B. 无任何输出
C. 2
D. 陷入死循环
正确答案D
接下来进行分析
1.3
下面的代码段中,执行之后 i 和 j 的值是什么()
int main()
{
int i = 1;
int j;
j = i++;
return 0;
}
A. 1,1
B. 1,2
C. 2,1
D. 2,2
正确答案C
接下来进行分析
1.4
以下程序的k最终值是()
int main()
{
int i = 10;
int j = 20;
int k = 3;
k *= i + j;
return 0;
}
A. 90
B. 50
C. 70
D. 30
正确答案A
接下来进行分析
1.5
以下 C 程序的输出结果是()
#include <stdio.h>
#include <stdlib.h>
int a = 1;
void test(){
int a = 2;
a += 1;
}
int main(){
test();
printf("%d\n", a);
return 0;
}
A. 1
B. 2
C. 3
D. 4
正确答案A
接下来进行分析
分析函数内部细节
1.6
下列描述中正确的是()
A. 表示m>n为true或者m<n为true的表达式为m>n&&m<n
B. switch语句结构中必须有default语句
C. 如果至少有一个操作数为true,则包含“||”运算符的表达式true
D. if语句结构中必须有else语句
正确答案C
接下来进行分析
1.7
C 语言规定,在一个源程序中,main函数的位置()
A. 必须在最开始
B. 必须在系统调用的库函数的后面
C. 可以任意
D. 必须在最后
正确答案C
接下来进行分析
1.8
以下叙述不正确的是()
A. 一个C源程序可由一个或多个函数组成
B. 一个C源程序必须包含一个main函数
C. C程序的基本组成单位是函数
D. 在C程序中,注释说明只能位于一条语句的后面
正确答案D
接下来进行分析
注释能放在很多地方
所有的注释都不会作为有效代码执行。注释在执行的过程中会替换成空格
因为注释会替换成空格,所以不能随便放。
所以我们在注释的时候也要注意到该注释是否会导致程序无法运行。要想不出错,正常在本行后面或者上下行注释就好了。
1.9
以下叙述正确的是()
A. 在C程序中,main函数必须位于程序的最前面
B. C程序的每行中只能写一条语句
C. C语言本身没有输入输出语句
D. 在对一个C程序进行编译的过程中,可发现注释中的拼写错误
正确答案C
接下来进行分析
1.10
在上下文及头文件均正常的情况下,执行如下代码, c 的值是()
#include <stdio.h>
int main()
{
int a = 0, c = 0;
do {
--c;
a = a - 1;
} while (a > 0);
return 0;
}
A. 0
B. 1
C. -1
D. 死循环
正确答案C
接下来进行分析
1.11
假定 x 和 y 为 double 型,则执行 x=2; y=x+3/2; 后y的值为()
A. 3.500000
B. 3
C. 2.000000
D. 3.000000
正确答案D
接下来进行分析
1.12
以下for循环的执行次数是()
for(int x = 0, y = 0; (y = 123) && (x < 4); x++);
A. 是无限循环
B. 循环次数不定
C. 4次
D. 3次
正确答案C
接下来进行分析
1.13
若有定义语句:int year=1009,*p=&year;以下不能使变量 year 中的值增至 1010 的语句是()
A. *p+=1;
B. (*p)++;
C. ++(*p)
D. *p++
正确答案D
接下来进行分析
1.14
选择表达式 11|10 的结果(本题数值均为十进制)()
A. 11
B. 10
C. 8
D. 2
正确答案A
接下来进行分析
1.15
如果abc类型如下,则表达式 a*b+d-c 值的类型为()
char a; int b; float c; double d;
A. float
B. int
C. char
D. double
正确答案D
接下来进行分析
2编程题
2.1最小公倍数
题目描述:正整数 a 和正整数 b 的最小公倍数,是指能被 a 和 b 整除的最小的正整数。请你求 a 和 b 的最小公倍数。
比如输入5和7,5和7的最小公倍数是35,则需要返回35。
输入描述:输入两个正整数。
输出描述:输出最小公倍数。
示例
示例1
输入:5 7
输出:35
示例2
输入:4 6
输出:12
示例3
输入:6 12
输出:12
2.1.1解法1
a和b的公倍数就是能同时整除a和b的数,而我们要求的是就是最小公倍数。我们先假定a和b里的较大值就是最小公倍数。看一下是较大值能不能同时整除a和b,如果能就是最小公倍数,不能则再算。
这个时候发现较大值7不是最小公倍数,就给较大值不断加1,每次加1看是否能整除a和b。直到较大值加到都能整除a和b,这个值就是最小公倍数
就按这个思路写出代码
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int k = (a > b ? a : b);
while (1)
{
if (k % a == 0 && k % b == 0)
{
break;
}
k++;
}
printf("%d\n", k);
return 0;
}
这样就可以了。但是这个代码有缺陷,那就是当a和b的值过大的时候,k++要执行非常多次。这回使得代码的效率非常低。还有就是因为a和b的值过大,a*b会超出范围。所以我们可以看下解法2
2.1.2解法2
假设a和b的最小公倍数为k时,就有这么一个算法
根据这个思路就可以改进代码
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int i = 1;//一定要从0开始否则a*i就为0
while (a * i % b)
{
i++;
}
printf("%d\n", a * i);
return 0;
}
不过在某些IO型做题网站可能会出错,因为当a和b非常大时候,还是会超出范围。把a和b的类型改成long long类型就好
2.2倒置字符串
题目描述:将一句话的单词进行倒置,标点不倒置。比如 “I like beijing.”,经过处理后变为:“beijing. like I”。字符串长度不超过100。
输入描述:输入一个仅包含小写字母、空格、‘.’ 的字符串,长度不超过100。‘.’ 只出现在最后一个单词的末尾。
输出描述:依次输出倒置之后的字符串,以空格分割。
示例
输入:I like beijing.
输出:beijing. like I
说明:注意不是单纯的逆序字符串
2.2.1gets库函数和fgets库函数
题目要求我们处理字符串
我们现在就去写代码。但是写之前要明白scanf函数默认直接读空格前的,空格后的不读了。
而题目要求我们把空格后面的内容也读了,否则完成不了任务。要读取空格我们可以用gets函数,它连空格后的内容也能一起读进去
虽然gets确实能读空格后的内容,但是这里报错,说gets为定义。这不是头文件的原因,是因为编译器觉得gets不安全,不让我们用。本来gets是能用的,后来很多编译器都决定gets不安全,就不让用了。因此就用来fgets替代料gets
这个时候去读就没有什么问题了
因为fgets设置了个数参数,最多只能读num个,所以安全。gets没有指定读多少个,因此可能会把arr撑爆,因此不安全。
当然如果我们不知道这两个函数,scanf其实也能读空格后的内容,但有一定的格式。
2.2.2逆序
我们可以这么分析题目的要求
反着来也可以
我们就按照第一种方式来。那怎么逆序一个字符串?可以来看看
我们写一个reverse函数来交换字符串,且该字符串能复用
可以用strlen函数来求字符串长度
#include <stdio.h>
#include <string.h>
int main()
{
char arr[101];//长度不超过100,加上\0就是101
//scanf("%s", arr);
//gets(arr);
//fgets(arr, 100, stdin);
scanf("%[^\n]s", arr);
int len = strlen(arr);
reverse(arr, arr + len - 1);
printf("%s\n", arr);
return 0;
}
这个时候再去写reverse函数。先来设置一下参数
再来实现内部的具体细节
完成了字符串逆序之后,就来实现每个单词的逆序。
逆序每个单词要找到单词起始位置和末尾位置
写成代码也就就是这样
#include <stdio.h>
#include <string.h>
int main()
{
char arr[101];//长度不超过100,加上\0就是101
//scanf("%s", arr);
//gets(arr);
//fgets(arr, 100, stdin);
scanf("%[^\n]s", arr);
int len = strlen(arr);
//1.逆序字符串
reverse(arr, arr + len - 1);
//2.逆序每个单词
char* start = arr;
char* cur = arr;
while (*cur != ' ')//当cur指向空格时停下来
{
cur++;
}
printf("%s\n", arr);
return 0;
}
这个时候就可以用reverse去逆序单词。起始位置就是start,末尾位置则是cur-1。因为此时的cur指向空格,cur-1就往回找到空格末尾元素地址。把这两个地址传进去,调用reverse就行了
#include <stdio.h>
#include <string.h>
int main()
{
char arr[101];//长度不超过100,加上\0就是101
//scanf("%s", arr);
//gets(arr);
//fgets(arr, 100, stdin);
scanf("%[^\n]s", arr);
int len = strlen(arr);
//1.逆序字符串
reverse(arr, arr + len - 1);
//2.逆序每个单词
char* start = arr;
char* cur = arr;
while (*cur != ' ')//当cur指向空格时停下来
{
cur++;
}
reverse(start, cur - 1);
printf("%s\n", arr);
return 0;
}
逆序完第一个单词,还剩下其他的单词,因此我们还要继续逆序。
因此我们可以看到逆序每个单词是两层循环。外层决定处理多少个单词,而内层则具体去处理它。
外层循环停止的条件是逆序完所有的单词了,也就是当cur指向\0的时候,就不要再循环了。而内层循环的判断条件也要变一下了。因为当逆序到I的时候,cur指向的是\0,而不是空格了,如果仅仅是cur不是空格是没法停下来的。
所以内层循环停下来有两种情况。第一种是遇到空格,说明后面还有单词要逆序。第二种是遇到\0,后面没有单词要逆序了,也要停下来。即*cur既不是空格,也不是\0就需要cur向后找。而当cur是其中一种情况就要停下来,也就是cur不指向空格也不指向\0两种情况都要满足。
而且逆序完一个单词之后cur++也不是随便++的。
最后完整的代码
void reverse(char* left, char* right)
{
while (left < right)
{
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
#include <stdio.h>
#include <string.h>
int main()
{
char arr[101];//长度不超过100,加上\0就是101
//scanf("%s", arr);
//gets(arr);
//fgets(arr, 100, stdin);
scanf("%[^\n]s", arr);
int len = strlen(arr);
//1.逆序字符串
reverse(arr, arr + len - 1);
//2.逆序每个单词
char* start = arr;
char* cur = arr;
while (*cur != '\0')
{
while (*cur != ' ' && *cur != '\0')
{
cur++;
}
reverse(start, cur - 1);
start = cur + 1;
if (*cur == ' ')
{
cur++;
}
}
printf("%s\n", arr);
return 0;
}
来看一下效果
以上就是逆序整个字符串和单词的内容。当然也可以用这种办法。
这种办法也不容易实现,只不过说这种办法比较容易想到。
3总结
还没想好