嵌入式开发C语言面试题详解
嵌入式开发C语言面试题详解
随着医疗电子、智能家居、物流管理和电力控制等方面的不断发展,嵌入式系统在物联网应用中发挥着越来越重要的作用。为了帮助大家更好地准备嵌入式开发相关的面试,本文整理了一些经典的C语言面试题及其答案解析,涵盖了预处理指令、宏定义、指针、位操作、中断处理、动态内存分配等多个方面的知识点。
预处理指令
1. 用预处理指令#define
声明一个常数,用以表明1年中有多少秒(忽略闰年问题)
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
这个示例展示了以下几个关键点:
#define
语法的基本知识(例如:不能以分号结束,括号的使用等)- 预处理器会为你计算常数表达式的值,因此直接写出计算过程比直接写出结果更清晰且没有代价
- 意识到这个表达式会使一个16位机的整型数溢出,因此需要使用长整型符号
L
告诉编译器这个常数是长整型数 - 如果你在你的表达式中用到
UL
(表示无符号长整型),那么你有了一个好的起点
2. 写一个“标准”宏MIN
,这个宏输入两个参数并返回较小的一个
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
这个示例用于测试以下知识点:
#define
在宏中应用的基本知识- 三重条件操作符的知识
- 在宏中小心地把参数用括号括起来
- 宏的副作用,例如:当你写下面的代码时会发生什么事?
least = MIN(*p++, b);
3. 预处理器标识#error
的目的是什么?
如果你不知道答案,请查看相关参考文献。这个问题有助于区分一个普通的程序员和一个对C语言有深入理解的程序员。
死循环(Infinite loops)
4. 嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?
这个问题有多个解决方案:
首选方案:
while(1) { }
另一个常用方案:
for(;;) { }
使用
goto
的方案:Loop: ... goto Loop;
数据声明(Data declarations)
5. 用变量a
给出下面的定义
a) 一个整型数
b) 一个指向整型数的指针
c) 一个指向指针的指针,它指向的指针是指向一个整型数
d) 一个有10个整型数的数组
e) 一个有10个指针的数组,该指针是指向一个整型数的
f) 一个指向有10个整型数数组的指针
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数
答案是:
a) int a;
// An integer
b) int *a;
// A pointer to an integer
c) int **a;
// A pointer to a pointer to an integer
d) int a[10];
// An array of 10 integers
e) int *a[10];
// An array of 10 pointers to integers
f) int (*a)[10];
// A pointer to an array of 10 integers
g) int (*a)(int);
// A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int);
// An array of ten pointers to functions that take an integer argument and return an integer
Static
6. 关键字static
的作用是什么?
在C语言中,关键字static
有三个主要作用:
- 在函数体中,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
- 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
- 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
Const
7. 关键字const
是什么含意?
const
意味着“只读”。例如:
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
Volatile
8. 关键字volatile
有什么含意?并给出三个不同的例子。
一个定义为volatile
的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile
变量的几个例子:
- 并行设备的硬件寄存器(如:状态寄存器)
- 一个中断服务子程序中会访问到的非自动变量
- 多线程应用中被几个任务共享的变量
位操作(Bit manipulation)
9. 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a
,写两段代码,第一个设置a
的bit 3,第二个清除a
的bit 3。在以上两个操作中,要保持其它位不变。
最佳的解决方案如下:
#define BIT3 (0x1<<3)
static int a;
void set_bit3(void)
{
a |= BIT3;
}
void clear_bit3(void)
{
a &= ~BIT3;
}
中断(Interrupts)
11. 中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字__interrupt
。下面的代码就使用了__interrupt
关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。
__interrupt double compute_area (double radius)
{
double area = PI * radius * radius;
printf(" Area = %f", area);
return area;
}
这个函数有太多的错误了:
- ISR不能返回一个值
- ISR不能传递参数
- 在许多的处理器/编译器中,浮点一般都是不可重入的
printf()
经常有重入和性能上的问题
代码例子(Code examples)
12. 下面的代码输出是什么,为什么?
void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts("> 6") : puts("<= 6");
}
这个问题测试你是否懂得C语言中的整数自动转换原则。答案是输出是“>6”。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。
13. 评价下面的代码片断:
unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1's complement of zero */
对于一个int型不是16位的处理器来说,上面的代码是不正确的。应编写如下:
unsigned int compzero = ~0;
动态内存分配(Dynamic memory allocation)
14. 尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(heap)中动态分配内存的过程的。那么嵌入式系统中,动态分配内存可能发生的问题是什么?
这里,我期望应试者能提到内存碎片,碎片收集的问题,变量的持行时间等等。
15. Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如,思考一下下面的例子:
#define dPS struct s *
typedef struct s * tPS;
以上两种情况的意图都是要定义dPS
和tPS
作为一个指向结构s
指针。哪种方法更好呢?(如果有的话)为什么?
这是一个非常微妙的问题,任何人答对这个问题(正当的原因)是应当被恭喜的。答案是:typedef
更好。
晦涩的语法
16. C语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么?
int a = 5, b = 7, c;
c = a+++b;
这个问题将做为这个测验的一个愉快的结尾。上面的例子是完全合乎语法的。根据最处理原则,编译器应当能处理尽可能所有合法的用法。因此,上面的代码被处理成:
c = a++ + b;
因此, 这段代码持行后a = 6, b = 7, c = 12
。
其他示例
1. What will print out?
main()
{
char *p1="name";
char *p2;
p2=(char*)malloc(20);
memset (p2, 0, 20);
while(*p2++ = *p1++);
printf("%s\n",p2);
}
Answer: empty string.
2. What will be printed as the result of the operation below:
main()
{
int x=20,y=35;
x=y++ + x++;
y= ++y + ++x;
printf("%d%d\n",x,y);
}
Answer : 5794
3. What will be printed as the result of the operation below:
main()
{
int x=5;
printf("%d,%d,%d\n",x,x<<2,x>>2);
}
Answer: 5,20,1
4. What will be printed as the result of the operation below:
#define swap(a,b) a=a+b;b=a-b;a=a-b;
void main()
{
int x=5, y=10;
swap (x,y);
printf("%d %d\n",x,y);
swap2(x,y);
printf("%d %d\n",x,y);
}
int swap2(int a, int b)
{
int temp;
temp=a;
b=a;
a=temp;
return 0;
}
Answer: 10, 5
10, 5
5. What will be printed as the result of the operation below:
main()
{
char *ptr = "Cisco Systems";
*ptr++;
printf("%s\n",ptr);
ptr++;
printf("%s\n",ptr);
}
Answer:
Cisco Systems
isco systems
6. What will be printed as the result of the operation below:
main()
{
char s1[]="Cisco";
char s2[]= "systems";
printf("%s\n",s1);
}
Answer: Cisco
7. What will be printed as the result of the operation below:
main()
{
char *p1;
char *p2;
p1=(char *)malloc(25);
p2=(char *)malloc(25);
strcpy(p1,"Cisco");
strcpy(p2,"systems");
strcat(p1,p2);
printf("%s\n",p1);
}
Answer: Ciscosystems
8. The following variable is available in file1.c, who can access it?:
static int average;
Answer: all the functions in the file1.c can access the variable.
9. WHat will be the result of the following code?
#define TRUE 0
// some code
while(TRUE)
{
// some code
}
Answer: This will not go into the loop as TRUE is defined as 0.
10. What will be printed as the result of the operation below:
int x;
int modifyvalue()
{
return(x+=10);
}
int changevalue(int x)
{
return(x+=1);
}
void main()
{
int x=10;
x++;
changevalue(x);
x++;
modifyvalue();
printf("First output:%d\n",x);
x++;
changevalue(x);
printf("Second output:%d\n",x);
modifyvalue();
printf("Third output:%d\n",x);
}
Answer: 12 , 13 , 13
11. What will be printed as the result of the operation below:
main()
{
int x=10, y=15;
x = x++;
y = ++y;
printf("%d %d\n",x,y);
}
Answer: 11, 16
12. What will be printed as the result of the operation below:
main()
{
int a=0;
if(a==0)
printf("Cisco Systems\n");
printf("Cisco Systems\n");
}
Answer: Two lines with “Cisco Systems” will be printed.