基础编程能力测评(C语言)
前言
一些C语言编程题的思考求解过程
一、选择题
(1) 定义一个无符号字符型变量 a (unsigned char),给 a 赋值256,使用printf("%d", a)打印变量 a ,打印的结果为( )
A. 255 B. -128 C. 0 D. 256
char、unsigned char和signed char是三种不同的基本类型
只有char是专用于放字符的,后面的一般只当1字节的整数用。
反正对于unsigned char型变量可以当作是用来存放1个字节的整数变量,由于是无符号,范围0~255。
赋值256的话,会额外溢出一个数,就会回到0
因此此题选择C选项:0
(2) 0xef & 0xfe运行的结果是( )
A. 0xee B. 0xff C. 0xfe D. 0xef
这个考察的是二进制中运算符的使用方法,二进制中有许多运算符:
与运算:&
全1出1,有0得0
或运算:|
有1出1,全0出0
非运算:~
取反 ,1-0 0-1
异或运算:^
相同为0,不同为1
位运算操作符:<< 左移 >> 右移
- 注意区分有符号数和无符号数的左移,前者会改变正负号。
- 二进制下,左右移可以看作乘除2的n次方。
1110 1111 &
1111 1110
1110 1110
因此此题选择:A
(3) 定义一个数组array[] = {1, 2, 3, 4, 5}; 以下哪种数组访问方式的返回值不是1( )
A. array[1] B. 0[array] C. array[0] D. (array+1)[-1]
此题考察C语言数值的多种引用方式。
array[1]
这个比较简单,就是数组的下标,从0开始。
0[array]
这个我有点不清楚原理,但是通过测试,我可以得知,前面的数字可以指代下标。
依然从0开始,并且一旦超出数组下标边界,超出1个数,会输出-1,继续超出会输出0。
而加上负号则会在原来的输出加上负号
例如
array[]={100,2,30,4,5};
printf("%d",5[array]);//-1
printf("%d",4[array]);//5
printf("%d",6[array]);//0
printf("%d",700[array]);//0
printf("%d",-1[array]);//-2
printf("%d",-3[array]);//-30
对于 (array+1)[-1]
这个对array增加的数字会将数组开始位置前进几位,然后在以新的位置进行下标对应。
即
array[]={100,2,30,4,5};
printf("%d",(array+2)[-2]);//100
因此这道题选择:C
(4) 以下程序运行的结果为( )
#include "stdio.h"
#define b 2+a
int a = 0;
int main()
{
int a = 1;
a += b;
printf("%d\n", a);
return 0;
}
为了搞清楚宏定义后面不是数字,而是变量的话,会不会因为变量的值改变而改变。
我增加了打印语句,看看b的值是否会变化
#include "stdio.h"
#define b 2+a
int a = 0;
int main()
{
printf("%d\n", b);
int a = 1;
printf("%d\n", b);
a += b;
printf("%d\n", b);
printf("%d\n", a);
return 0;
}
输出:
2
3
6
4
所以宏定义后面是变量的话,会因为变量的值改变而改变。
因此这道题选择:D
(5) unsigned char i = 10; while(i–)在跳出循环时 i 的大小为( )
A. 0 B. 1 C. 255 D. 无法跳出循环
这道题主要考察while循环
while循环中里面的表达式如果是false就会跳出循环
即当 i-- < 0 时跳出循环,此时是先对i判断,再进行-1操作。
但是这个i又是我们之前提到的无符号字符变量,范围0~255,一直减1的话,好像会始终在0-255之间循环吧。
运行求证想法,结果 。。。。
结果只运行几次就跳出了。
因为while循环,里面表达式为0,也会跳出循环。。
。
好像是我傻了。
但是要记住,是先对i判断,再进行-1操作。
所以当i = 1后,会进入循环,并且减1,在进入循环后,跳出循环,并且再减1,因此,会到255
所以答案为:C
二、填空题
(1) 短整型占用的字节数为___。
第一时间去查了百度,严格来说不同的编译器上好像不同,不过按照普遍来说:
char :1个字节
char*(即指针变量): 2个字节
short int : 2个字节
int: 2个字节
unsigned int : 2个字节
float: 4个字节
double: 8个字节
long: 4个字节
long long: 8个字节
unsigned long: 4个字节
因此这道题就填:2
(2) 0x17对应的十进制数字为___。
考察进制转换,16进制0x17转换十进制
0x17 = 1*16 + 7 = 25
因此这道题填:25
(3) char b = -1; 在执行完 if(!b) {b = 1;} 后变量 b 的值为___。
这道题考察if语句,以及真假判断。
其中,非零为真,零为假。因此对于b = -1 ,b为真,用!取反后,为假,不进行if中的循环。
因此此题填:-1
(4) 以下程序运行的结果为___。
#include "stdio.h"
int Func1()
{
return 1;
}
int Func2()
{
return 2;
}
int main()
{
int(*pfc[])() = {Func1, Func2};
printf("%d\n", pfc[0]());
return 0;
}
这道题主要考察指针操作。
1.发现,这个是函数指针,
2.发现这个是一个指针数组,指向多个函数
失敬失敬,看来C语言指针学的实在太差。
int(*pfc[])() = {Func1, Func2};
该语句定义了一个指针数组,并且指向函数,那么后面的语句就能理解了,是指向Func1函数的指针,即函数Func1的值
因此答案为:1
额外的碎碎念
int (*p)();
定义一个函数指针p,p指向一个函数
int (*p[]);
定义一个指针数组p。
int (*p[])();
先定义一个指针数组,然后外层说明指针指向一个函数,返回值为int类型,输入空列表。
(5) 如下定义的联合体所占用的字节数为___。
union
{
char a;
short b;
}u;
额这又涉及到我的知识盲区了,短暂的知识补充
因此
char a --1字节
short b --2字节
因此答案为:2字节
结构体的大小:
以字节最大的单位 为地址对齐标准 ,如 结构体中 最大为 double,8个字节,那么 比它小的单位就要凑齐 8的整数倍 字节大小。
例如
struct stu
{
double c;
int s;
char k;
}a;
double --8
int --4
char --1
所以以8字节对齐,总字节大小为8的倍数
8+4+1=13,而8字节对齐后,为16。
联合体(union):
联合体的每一项元素起始地址都一样,都跟联合体 union 的地址偏移量为0;
此结构的容量要容纳最大的一个元素,而且要字节对齐其他元素的大小
union U
{
char s[9];
int n;
double d;
};
char s[9]; --9个字节
int n; --4个字节
double d; --8个字节
union U中最大的是 9 个字节的 s[9],但 9 不能被 4 和 8 同时整除,而16可以,且16比9大。所以 联合体所占空间 为16 个字节
三、改错题
在智能车比赛中,采用摄像头循迹的小车常用二维数组保存采集到的图像,后续的图像处理也经常会访问二维数组。
在写图像处理程序时,我们经常会遇到千奇百怪的问题,因此调试代码、寻找代码错误的能力十分重要。
我们准备了一段运行时发生了问题的代码,请你阅读代码,尝试写出程序的功能,同时找出其中的错误,加以改正。
#include "stdio.h"
#define m 4
#define n 3
char dir[] = {-1, 0, 1, 0, -1};
int main()
{
char r = 0, c = 0; //r:行 | c:列
char x, y, k;
char finish = 0, count = 0;
unsigned char a[m][n] = {{0, 1, 0},
{0, 1, 2},
{0, 1, 0},
{0, 0, 0}};
while (1)
{
a[r][c] = 3;
count++;
for (k = 0; k < 4; k++)
{
x = r + dir[k],y = c + dir[k + 1];
if (x >= 0 && x < n && y >= 0 && y < m) //防止越界
{
if (a[x][y] == 2)//找到目标
{
finish = 1; break;
}
if (a[x][y] == 0)//继续前进
{
r = x, c = y;
}
}
}
if (finish)
{
break;
}
}
printf("%d", count);//输出结果
return 0;
}
修改如下
#include "stdio.h"
// 此处定义总图像的大小,四行三列
#define m 4
#define n 3
//这里定义一个数组,通过给二维数组的参数增加特定值值
//用于给予基准元素一个寻找目标的一个寻找路线。
char dir[] = {-1, 0, 1, 0, -1};
int main()
{
char r = 0, c = 0; //r:行 | c:列
char x, y, k;
//finish是寻找目标是否成功的一个判断指标。
// count是确定了几次基准点的次数,即数组中赋值为3的个数。
char finish = 0, count = 0;
unsigned char a[m][n] = {{0, 1, 0},
{0, 1, 2},
{0, 1, 0},
{0, 0, 0}};
//进入死循环,直到找到目标为止
while (1)
{
a[r][c] = 3;//确定第一个基准点位
count++; //对应的值增加1
//进入for循环,通过对变量k的依次递增,从dir数组中依次提取元素
//(1)这个语句配合dir数组理解,可以看作是以基准点位为基准,依次从上-右-下-左这四个方位的元素进行查询,即
//做顺时针四个方位的查询。并且通过第一个if语句(2)来防止访问元素时产生越界。
//代码的错误出现在这,x代表行,则需要满足0~3,y代表列,则范围需要满足0~2。将mn相互调换即可
// 接下来,对元素的具体查询如下,若查询元素值为2,则表示找到该元素,将finish变量赋值为1,并跳出第一个循环,即for循环 (3)(4)(5)
//若查询元素值为0,则将基准点下标改为该元素的下标,并以此基准点继续未完成的方位查询。 (6)(7)
//完成一次完整的四个方位查询后,即完成一个for循环后,首先检查finish的值,是否完成目标的寻找,若finish=1,进一步跳出while循环,输出结果。
//否则,以新基准点为基准,赋值为3,并将对应的count+1,进入for循环,继续进行四个方位的点位查询。
for (k = 0; k < 4; k++)
{
x = r + dir[k],y = c + dir[k + 1]; //(1)
if (x >= 0 && x < m && y >= 0 && y < n) //防止越界 (2)
{
if (a[x][y] == 2)//找到目标 (3)
{
finish = 1; //(4)
break; //(5)
}
if (a[x][y] == 0)//继续前进 (6)
{
r = x, c = y; (7)
}
}
}
if (finish)
{
break;
}
}
printf("%d", count);//输出结果
return 0;
}
四、编程题
归一化在电磁循迹小车上应用广泛。当场地更换时,电磁循迹获取的电磁读值(可以简单理解为磁场大小)可能会出现上下浮动,使得我们调整好的小车无法试应新的场地、发挥出自己的最佳状态。
我们引入数值归一化,是希望能营造相同的调参环境,即使电磁读值有所变化,但归一化后的 数值仍能与之前场地相同。
请你用C语言设计一个函数,这个函数能够传入无符号短整型变量 n 。变量 n 的取值范围在0~4095之间(包括0和4095)。
你需要让函数实现传入数值的归一化,即用0~100表示传入数字的大小。
函数运行的结果用无符号字符型变量保存并返回。例:
输入:n = 1000
返回:24
这里的话,主要考察了归一化,出于编程的思维,对于输入范围以及归一化后的数值范围要有一定的修改便捷性。
其中比较困难的就是对于除法运算要设置浮点运算,不然整型运算默认会变成0。
所以代码如下:
1. #include "stdio.h"
2.
3. #define in_max 4095
4. #define in_min 0
5. #define out_max 100
6. #define out_min 0
7.
8. unsigned char guiyi(unsigned short int n)
9. {
10. float temp;
11. unsigned char output;
12.
13. temp = n;
14. output = ((temp - in_min)/(in_max - in_min))
15. * (out_max - out_min) + out_min;
16.
17. return output;
18. }
19.
20. int main()
21. {
22. unsigned char out;
23.
24. out = guiyi(1000);
25. printf("输出:%u", out);
26.
27. return 0;
28. }
五、情景题
在编程题中,我们实现了传入数值的归一化,现在有如下场景:
在赛道的某处,电磁循迹小车读取到的电磁值为2047,归一化后得到的数值为50。经过一段时间调试,
我们把小车调整到了最佳状态,但突然遇到设备故障,能发生磁场的信号发生器无法正常工作,需要更
换。更换信号源后我们发现,原本单圈成绩能跑进一分钟的小车现在却需要一分半的时间才能完成单
圈。经过测量我们得知,小车处于赛道同一位置时读取到的电磁读值从2047变为了1876,归一化的数值
变为了45。已知:小车是依赖归一化后的电磁值循迹的,问:你有什么办法让小车重新跑进一分钟吗?
告诉我们你的想法,写出你想法的依据。
具体的回答:
小车依靠通过检测磁场信息并转化信号输出。当更换场地的发生磁场的信号发生器中的信号
源后,小车处于赛道同一位置时读取到的电磁读值从2047变为了1876,归一化的数值
变为了45。
而之前对于电磁值为2047,归一化后得到的数值为50,正好为归一化范围的中值。
我的想法是,既然之前状态是最佳状态,而且通过检测数据我们可以看出,电磁场的强度相比之前削弱了。
那么我在问题四中的代码中就先设定好了宏定义。
对此,我们需要将
1. #define in_max 4095 修改成 #define in_max 1876*2
因为同一位置之前的归一化刚好是中值,因此新的电磁信号范围也应以1876为中值,因此就以1876*2 = 3752为传入数值范围的最大值即可。
总结
通过这次学习,对C语言的回顾效果很好,希望后续这种学习模式可以坚持下去。