嵌入式开发工程师面试题 - 2024/11/24
原文嵌入式开发工程师面试题 - 2024/11/24 转载请注明来源
1.若有以下定义语句double a[8],*p=a;int i=5;对数组元素错误的引用是?
A *a
B a[5]
C *(p+1)
D p[8]
解析:
在 C 或 C++ 语言中,对于数组和指针的操作需要严格遵守语法规则。
我们逐一分析选项来确定哪个是对数组元素错误的引用:
A. *a
-
这是正确的。
a
是数组名,它在大多数表达式中等价于指向数组第一个元素的指针。因此,*a
等价于a[0]
,表示数组的第一个元素。
B. a[5]
-
这也是正确的。
a[5]
直接引用数组的第六个元素(数组索引从 0 开始)。
C. *(p+1)
-
这同样是正确的。
p
是一个指向double
类型的指针,指向数组a
的第一个元素。p+1
将指针移动到数组的第二个元素,*(p+1)
解引用这个指针,得到数组的第二个元素,即a[1]
。
D. p[8]
-
这是错误的。在 C/C++ 中,
p[i]
是指针p
偏移i
个元素后的值的简写,等价于*(p+i)
。因此,p[8]
等价于*(p+8)
,这意味着尝试访问p
指向的位置之后的第九个double
元素。由于p
指向的是一个只有 8 个元素的数组,p[8]
尝试访问数组之外的内存,这是越界的,因此是错误的。
综上所述,错误的引用是 D. p[8]。
2.在上下文及头文件均正常的情况下,设:
enum color { red, yellow = 2, blue, white, black }r = white;
,执行 printf("%d", r) ;后的输出结果是?
解析:
在C语言中,enum
类型用于定义一组命名的整数常量。当定义enum
时,可以给枚举成员显式赋值,也可以不赋值。如果未显式赋值,则枚举成员的值将默认为前一个成员的值加1(第一个未赋值的成员默认为0)。
考虑以下enum
定义:c
复制代码
enum color { red, yellow = 2, blue, white, black } r = white; |
这里,enum color
定义了五个成员:
-
red
:未显式赋值,且是第一个成员,因此默认为0。 -
yellow
:显式赋值为2。 -
blue
:未显式赋值,因此继承yellow
的值加1,即3。 -
white
:未显式赋值,因此继承blue
的值加1,即4。 -
black
:未显式赋值,因此继承white
的值加1,即5。
同时,定义了一个enum color
类型的变量r
,并将其初始化为white
。
由于white
的值是4,因此执行printf("%d", r);
后,输出结果是4。
总结:执行printf("%d", r);
后的输出结果是4
。
3.对于条件表达式(k)?(i++):(i--)来说,其中的表达式k等价于()
A k==0
B k==1
C k!=0
D k!=1
解析:
在C语言中,条件表达式(也称为三元运算符)的语法是:
复制代码
condition ? expression1 : expression2; |
这里,condition
是一个任意表达式,它会被评估为真(非零)或假(零)。如果 condition
为真(即非零),则整个条件表达式的结果是 expression1
的值;如果 condition
为假(即零),则结果是 expression2
的值。
现在考虑给定的条件表达式:
代码
(k) ? (i++) : (i--); |
在这个表达式中,k
是条件部分。根据条件表达式的规则,k
会被评估:
-
如果
k
的值非零(即真),则执行i++
。 -
如果
k
的值为零(即假),则执行i--
。
这里的关键是理解 k
如何被评估。在C语言中,任何非零值都被视为真,而零被视为假。因此,当 k
被用作条件时,它实际上是在检查 k
是否不等于零。
现在来看选项:
A. k==0
:这是检查 k
是否等于零,与条件表达式的逻辑不符。
B. k==1
:这是检查 k
是否等于1,这同样不是条件表达式所检查的。
C. k!=0
:这是检查 k
是否不等于零,这与条件表达式中 k
的评估方式相符。
D. k!=1
:这是检查 k
是否不等于1,这也不是条件表达式所检查的。
因此,对于条件表达式 (k) ? (i++) : (i--)
来说,其中的表达式 k
等价于 k!=0
。
正确答案是 C。
4.设有以下函数void fun(int n,char *s)(......),则下面对函数指针的定义和赋值均是正确的:()
A void (*pf)(int,char); pf=&fun;
B void (*pf)(int n,char *s); pf=fun;
C void *pf(); *pf=fun;
D void *pf(); pf=fun;
解析:
在C语言中,定义一个指向函数的指针需要指定函数的返回类型、函数名(在指针定义中省略)以及函数的参数类型。对于给定的函数:
复制代码
void fun(int n, char *s); |
我们需要定义一个函数指针,该指针能够指向这样一个函数:返回类型为void
,接受一个int
类型的参数和一个char *
类型的参数。
现在,我们逐一分析选项:
A. void (*pf)(int,char); pf=&fun;
-
这个选项中的函数指针定义是错误的。它应该指定第二个参数为
char *
类型,而不是char
类型。此外,虽然取函数地址是常见的做法,但在这个上下文中,直接赋值pf = fun;
也是合法的,因为函数名在大多数表达式中会被解释为指向该函数的指针。
B. void (*pf)(int n,char *s); pf=fun;
-
这个选项是正确的。函数指针
pf
被正确地定义为指向一个接受int
和char *
参数并返回void
的函数。然后,pf
被赋值为fun
,这是合法的,因为fun
是一个符合该签名的函数。
C. void *pf(); *pf=fun;
-
这个选项是错误的。首先,
pf
被定义为一个指向返回void
且不接受任何参数的函数的指针。这与fun
的签名不匹配。其次,*pf=fun;
是尝试对函数指针进行解引用并赋值,这是不合法的。
D. void *pf(); pf=fun;
-
这个选项同样是错误的。与选项C类似,
pf
的定义与fun
的签名不匹配。此外,尝试将函数名fun
直接赋值给pf
也是不正确的,因为类型不匹配。
综上所述,只有选项B正确地定义了函数指针,并将其赋值为指向给定函数的指针。
正确答案是B:
5.阅读下面代码:
class B
{
public:
virtual void Fun(){}
};
class D: public B
{
public:
void Fun(){}
};
D dd;
B* pb = ⅆ
D* pd = ⅆ
pb->Fun();
pd->Fun();
上述例程调用的Fun函数顺序为()
A B::Fun, D::Fun
B B::Fun, B::Fun
C D::Fun, D::Fun
D D::Fun, B::Fun
解析:
在C++中,虚函数(virtual function)机制允许派生类(derived class)重写(override)基类(base class)中的函数。当通过基类指针或引用调用虚函数时,实际调用的函数是派生类中重写的那个版本,而不是基类中的版本。这种动态绑定(dynamic binding)或称为运行时多态性(runtime polymorphism)是面向对象编程中的一个核心概念。
现在,我们来分析提供的代码:c复制代
-
class B
定义了一个虚函数Fun()
。 -
class D
继承自B
并重写了Fun()
函数。 -
创建了一个
D
类型的对象dd
。 -
B* pb = ⅆ
创建了一个指向dd
的B
类型的指针pb
。由于B
中的Fun()
是虚函数,通过pb
调用Fun()
时,将发生动态绑定,实际调用的是D
类中的Fun()
。 -
D* pd = ⅆ
创建了一个指向dd
的D
类型的指针pd
。通过pd
调用Fun()
时,直接调用的是D
类中的Fun()
,因为这里没有涉及多态性(即没有通过基类指针调用派生类函数)。
现在,我们来看函数调用的顺序:
-
pb->Fun();
:由于pb
是B
类型的指针,但指向D
类型的对象,且Fun()
是虚函数,因此这里调用的是D::Fun()
。 -
pd->Fun();
:pd
是D
类型的指针,直接指向D
类型的对象,因此这里调用的也是D::Fun()
。
综上所述,调用的 Fun
函数顺序为 D::Fun, D::Fun
。
正确答案是 C。
未完待续,持续更新!
以防后边找不到可以点赞收藏下!