嵌入式软件C语言面试常见问题及答案解析(二)
继续来总结分享那个面试中遇到的题目,文中给出的答案或者代码并不是唯一或者绝对的,不同的情况下有可能不适用。
本文中题目列表
- 1. 现有如下代码,请问在32位编译器上输出结果是什么?
- 2. 现有如下代码,输入x=9999,求函数返回值。
- 3. 写一个函数实现求斐波那契数列前n项的和(递归算法得一半分)。
- 4. 分析如下代码,请写出函数最终的输出结果。
- 5. 分析如下代码,改正其中的错误。
- 6. 编程实现:把十进制数(long 型)分别以二进制和十六进制形式输出,不能使用 printf 系列函数。
- 7. 下面的代码的目的是把一个字符串倒序,如“abcd”倒序后变为“dcba”,请找出所有错误。
- 8. 有如下代码,请问输出结果是什么?
- 9. 写一段程序,找出数组中第k大小的数,输出数所在的位置。
- 10. 请简述以下两个for循环的优缺点。
- 11.请根据如下如题目写出解题的方案:实现一个摇号系统。
- 12. strcpy函数相关问题。
- 13. 设有`int a,b,c`,请写函数实现`c=a+b`,同时避免溢出问题。
1. 现有如下代码,请问在32位编译器上输出结果是什么?
typedef struct AA
{
int b1:5;
int b2:2;
} AA;
void main()
{
AA aa;
char cc[100];
strcpy(cc, "0123456789abcdefghijklmnopqrstuvwxyz");
memcpy(&aa, cc, sizeof(AA));
printf("%d, %d\n", aa.b1, aa.b2);
}
输出结果是:-16, 1
解析如下:
成员
b1
占一个int类型中的5bit
,成员b2
占一个int类型中的2bit
,因为在32位机器上int类型占用4字节,也就是32bit,所以b1
和b2
在一个int类型中,故sizeof(AA)
的大小为4
;经过
strcpy
和memcpy
后,变量aa
的4个字节所存放的值是字符0, 1, 2, 3
的ASCII码,用二进制00110000
,00110001
,00110010
,00110011
;最后输出的是这4个字节的前5bit和之后的2bit,分别为:
10000
,01
,而且b1和b2为int类型,最高位表示为符号位,所以b1输出为-16,b2为1。
2. 现有如下代码,输入x=9999,求函数返回值。
int func(int x)
{
int countx = 0;
while (x)
{
countx++;
x = x & (x - 1);
}
return countx;
}
函数返回结果是:8
第一次在面试中拿到这个题的时候说实在的懵了好久,程序中就只知道语句
x & (x - 1);
是可以判断x
是否为2的指数次幂。但是组合到一起的时候着实不清楚到底要干嘛。当时只记得是通过小参数带入算了几次,后来给出的答案当时是不对的,面试官看答案不对所以问我解决的思路,简单说说思路,反正后来面试官不置可否。当然现在肯定是知道了这是统计9999的二进制数值中有多少个1的函数,当然也有直接让写程序的题。
整体思路就是将目标数据拆分成2的n次幂和系数的加和形式,然后计算系数含有1的个数然后加和即可。比如:
- 9999 = 9×1024 + 1×512 + 1×256 + 7×2 + 1×1
- 7650 = 7×1024 + 1×256 + 3×64 + 1×32 + 1×2
将10进制转换为二进制表示,那么 9 用二进制表示为 1001,7 用二进制表示为 0111,3用二进制表示为 0011,1 用二进制表示为 0001,所以,所有系数的含1量加起来就是:
- 9999 : 2 + 1 + 1 + 3 + 1 = 8
- 7650 : 3 + 1 + 2 + 1 + 1 = 8
可以用随便一个数来验证一下:
7690 = 7×1024 + 1×512 + 1×8 + 1×2,即 3 + 1 + 1 + 1 = 6
7690 用二进制表示为 : 0001 1110 0000 1010,可见其二进制中含有 6 个 1。
3. 写一个函数实现求斐波那契数列前n项的和(递归算法得一半分)。
两种简单的实现代码如下所示。
/* 法一:递归 */
long long fb(uint32_t n)
{
if (n <= 0)
return 0;
if (n <= 1)
return 1;
return fb(n - 1) + fb(n - 2);
}
/* 法二:循环计算 */
long long fb(uint32_t n)
{
uint32_t rlt[] = {
0, 1};
if (n < 2)
return rlt[n];
long long fb1 = 0, fb2 = 1, fbn = 0;
for (uint32_t i = 2; i <= n; ++i)
{
fbn = fb1 + fb2;
fb1 = fb2;
fb2 = fbn;
}
return fbn;
}
这个题目其实有点意思,在题干中已经说明了用递归可以,但是要扣分,像这种情况下其实如果除了递归之外不知道怎么实现的话那就用递归,但是如果你有其他办法,那最好就不要用递归了,因为首先扣分是其次,面试的时候可能会给比较挑剔的面试官一些不好的映像(不会?明知山有虎偏向虎山行?)。再者如果被问道递归算法效率低,且容易栈溢出等(当时本人被面试官就问我为什么会限制用递归?)。对于这种题,面试官临场发挥的空间比较大,所以要随机应变。在嵌入式里面,像 效率、堆栈溢出、内存管理 等都是比较重视的问题。
4. 分析如下代码,请写出函数最终的输出结果。
struct bit
{
int a : 3;
int b : 2;
int c : 3;
};
void main()
{
struct bit s;
char *c = (char *)&s;
printf("%d\n", sizeof(struct bit));
*c = 0x99;
printf("%d, %d, %d\n", s.a, s.b, s.c);
int a = -1;
printf("%x\n", a);
}
在32位机器上输出结果是: 4 1 -1 -4 ffffffff
像这种求占用空间大小问题在题目中没有说明当前操作机器是多少位的,说真的不清楚是人家故意不说作为考核的,还是出题的人也没有想到直接默认的。反正作为面试者能明确表示说在32位机器上肯定是一个明智的选择。
因为 0x99 在内存中表示为 1001 1001,a占用3bit = 001, b占2bit = 11, c占3bit = 100。
所以用sizeof获取占用空间的时候,就是一个int的类型大小空间,在32位系统上为4;
因为c为有符号数, c = 100, 最高1为表示c为负数,负数在计算机用补码表示,所以c = -4;同理
b = -1;
当c为有符合数时, c = 100,即 c = 4,同理 b = 3
5. 分析如下代码,改正其中的错误。
int main(void)
{
int **p;
int arr[100];
p = &arr;
return 0;
}
这个题目实在搜索的时候看见的,觉得有意思就总结在这儿,这里面其实就一个容易忽略的常识的问题:数组名是常量不是变量,与一般的变量名不一样,不能对其取地址。 题中的问题处在
p = &arr;
,如果想要通过二级指针去访问次数组,那么可以修改为下面的代码。
int main(void)
{
int **p, *q;
int arr[100];
q = arr; // 一级指针变量指向数组
p = &q; // 二级指针变量指向一级指针
return 0;
}
6. 编程实现:把十进制数(long 型)分别以二进制和十六进制形式输出,不能使用 printf 系列函数。
题目中已经表明是long型数据,那么在32为系统上其占用是4字节,所以直接按照32为操作系统给出实例代码,当然在面试的时候最好也能表明是默认32位系统的会更好。
还有在win环境上,不管是32位还是64位,long型都是4字节,long long是8字节,但是在64位linux环境上,long型和long long型都是8个字节。切记!
#define TYPE_LONG_BITS (4U * 8)
#define TYPE_LONG_BYTE 4U
/* 每1bit转换为二进制的1位数字 */
char *tenToBin(unsigned int data)
{
/* NOTE 为了简单说明,直接默认long型占用4字节 */
char *tp_bin = calloc(1, TYPE_LONG_BITS + 1); // ‘\0’位置
tp_bin[32] = '\0'; // 字符串以'\0'为结束,不能丢
for (int i = 0; i < 32; ++i)
{
tp_bin[31 - i] = (data >> i & 0x01U) + 0x30U;
}
return tp_bin;
}
/* 每4bit转换为十六进制的1位数字 */
char *