嵌入式软件工程师面试题——2025校招社招通用(C/C++)(四十四)

说明:

  • 面试群,群号: 228447240
  • 面试题来源于网络书籍,公司题目以及博主原创或修改(题目大部分来源于各种公司);
  • 文中很多题目,或许大家直接编译器写完,1分钟就出结果了。但在这里博主希望每一个题目,大家都要经过认真思考,答案不重要,重要的是通过题目理解所考知识点,好应对题目更多的变化;
  • 博主与大家一起学习,一起刷题,共同进步;
  • 写文不易,麻烦给个三连!!!

前面1-15已经是C/C++,但是由于前面写的比较混乱,把八股文和题目混在了一起,所以从这一篇开始重新整理重新写,前面1-15也就可以选看了,希望多多支持!

1.const的用法(定义和用途)(必考)

答案:

const主要用来修饰变量、函数形参和类成员函数:

1 )用 const 修饰常量:定义时就初始化,以后不能更改。
2 )用 const 修饰形参: func(const int a){}; 该形参在函数里不能改变
3 )用 const 修饰类成员函数:该函数对成员变量只能进行只读操作,就是 const 类成员函数是不能修改成员变量的数值的。
const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
参考一个大佬的回答 :
我只要一听到被面试者说: "const 意味着常数 " ,我就知道我正在和一个业余者打交道。去年 Dan Saks 已经在他的文章里完全概括了const 的所有用法,因此 ESP( 译者: Embedded Systems Programming) 的每一位读者应该非常熟悉const 能做什么和不能做什么 . 如果你从没有读到那篇文章,只要能说出 const 意味着" 只读 " 就可以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。如果应试者能正确回答这个问题,我将问他一个附加的问题:下面的声明都是什么意思?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
前两个的作用是一样, a 是一个常整型数。
第三个意味着 a 是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。
第四个意思 a 是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。
最后一个意味着 a 是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。

2.volatile作用和用法

答案:

一个定义为 volatile 的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量在内存中的值,而不是使用保存在寄存器里的备份(虽然读写寄存器比读写内存快)。
回答不出这个问题的人是不会被雇佣的。这是区分 C 程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的家伙们经常同硬件、中断、RTOS 等等打交道,所有这些都要求用到 volatile 变量。不懂得 volatile的内容将会带来灾难。
以下几种情况都会用到 volatile
1 、并行设备的硬件寄存器(如:状态寄存器)
2 、一个中断服务子程序中会访问到的非自动变量
3 、多线程应用中被几个任务共享的变量

3.经典的sizeof(struct)sizeof(union)内存对齐

答案:

内存对齐作用:
1. 平台原因 ( 移植原因 ) :不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2. 性能原因:数据结构 ( 尤其是栈 ) 应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

结构体struct内存对齐的3大规则:

1. 对于结构体的各个成员,第一个成员的偏移量是 0 ,排列在后面的成员其当前偏移量必须是当前成员类型的整数倍;
2. 结构体内所有数据成员各自内存对齐后,结构体本身还要进行一次内存对齐,保证整个结构体占用内存大小是结构体内最大数据成员的最小整数倍;
3. 如程序中有 #pragma pack(n) 预编译指令,则所有成员对齐以 n 字节为准 ( 即偏移量是 n 的整数倍 ) ,不再考虑当前类型以及最大结构体内类型。

联合体union内存对齐的2大规则:

1. 找到占用字节最多的成员;
2.union 的字节数必须是占用字节最多的成员的字节的倍数,而且需要能够容纳其他的成员
typedef union {
long i;
int k[5];
char c;
}D
要计算 union 的大小 , 首先要找到占用字节最多的成员 , 本例中是 long, 占用 8 个字节 ,int k[5] 中都是 int 类型 ,仍然是占用4 个字节的,然后 union 的字节数必须是占用字节最多的成员的字节的倍数 , 而且需要能够容纳其他的成员, 为了要容纳 k(20 个字节 ), 就必须要保证是 8 的倍数的同时还要大于 20 个字节 , 所以是 24 个字节。

引申:位域(大疆笔试题)
C 语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为 位段 或称“ 位域 ”( bit field) 。利用位段能够用较少的位数存储数据。一个位段必须存储在同一存储单元中,不能跨两个单元。如果第一个单元空间不能容纳下一个位段,则该空间不用,而从下一个单元起存放该位段。
1. 位段声明和结构体类似
2. 位段的成员必须是 int unsigned int signed int
3. 位段的成员名后边有一个冒号和一个数字
typedef struct_data{
char m:3;
char n:5;
short s;
union{
int a;
char b;
};
int h;
}_attribute_((packed)) data_t;
答案 12

4.inline函数

答案:
C 语言中,如果一些函数被频繁调用,不断地有函数入栈,即函数栈,会造成栈空间或栈内存的大量消耗。为了解决这个问题,特别的引入了inline 修饰符,表示为内联函数。
大多数的机器上,调用函数都要做很多工作:调用前要先保存寄存器,并在返回时恢复,复制实参,程序还必须转向一个新位置执行C++ 中支持内联函数,其目的是为了提高函数的执行效率,用关键字 inline放在函数定义( 注意是定义而非声明 ) 的前面即可将函数指定为内联函数,内联函数通常就是将它在程序中的每个调用点上“ 内联地 展开。
内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。

5.使用32位编译情况下,给出判断所使用机器大小端的方法

答案:
联合体方法判断方法:利用 union 结构体的从低地址开始存,且同一时间内只有一个成员占有内存的特性。大端储存符合阅读习惯。联合体占用内存是最大的那个,和结构体不一样。
#include
int main(){
union w
{
int a;
char b;
}c;
c.a = 1;
if(c.b == 1)
printf("小端存储\n");
else
printf("大端存储\n");
return 0;
}
指针方法:  通过将 int 强制类型转换成 char 单字节, p 指向 a 的起始字节(低字节)
#include 
int main ()
{
    int a = 1;
    char *p = (char *)&a;
    if(*p == 1)
    {
        printf("小端存储\n");
    }
    else
    {
        printf("大端存储\n");
    }
    return 0;
}

6.main执行之前和之后执行的代码可能是什么?

答案:
main 函数执行之前 ,主要就是初始化系统相关资源:
  • 设置栈指针
  • 初始化静态static变量和global全局变量,即.data段的内容
  • 将未初始化部分的全局变量赋初值:数值型shortintlong等为0boolFALSE,指针为NULL等等,即.bss段的内容
  • 全局对象初始化,在main之前调用构造函数,这是可能会执行前的一些代码
  • main函数的参数argcargv等传递给main函数,然后才真正运行main函数
  • __attribute__((constructor))
main 函数执行之后
  • 全局对象的析构函数会在main函数之后执行;
  • 可以用 atexit 注册一个函数,它会在main 之后执行;
  • __attribute__((destructor))

7.在传递函数参数时,什么时候该使用指针,什么时候该使用引用呢?

答案:
需要返回函数内局部变量的内存的时候用指针。使用指针传参需要开辟内存,用完要记得释放指针,不然会内存泄漏。而返回局部变量的引用是没有意义的。
对栈空间大小比较敏感(比如递归)的时候使用引用。使用引用传递不需要创建临时变量,开销要更小。
类对象作为参数传递的时候使用引用,这是 C++ 类对象传递的标准方式。

8.堆和栈的区别

答案:
栈空间默认是4M, 堆区一般是 1G - 4G。
嵌入式软件工程师面试题——2025校招社招通用(C/C++)(四十四)_第1张图片

9.你觉得堆快一点还是栈快一点?

答案:
毫无疑问是栈快一点。
因为操作系统会在底层对栈提供支持,会分配专门的寄存器存放栈的地址,栈的入栈出栈操作也十分简单,并且有专门的指令执行,所以栈的效率比较高也比较快。
而堆的操作是由 C/C++ 函数库提供的,在分配堆内存的时候需要一定的算法寻找合适大小的内存。并且获取堆的内容需要两次访问,第一次访问指针,第二次根据指针保存的地址访问内存,因此堆比较慢。

10.free回收的内存是立即返还给操作系统吗?

答案:
不是的,被 free 回收的内存会首先被 ptmalloc 使用双链表保存起来,当用户下一次申请内存的时候,会尝试从这些内存中寻找合适的返回。这样就避免了频繁的系统调用,占用过多的系统资源。同时ptmalloc也会尝试对小块内存进行合并,避免过多的内存碎片。

11.变量声明和定义区别?

答案:
声明仅仅是把变量的声明的位置及类型提供给编译器,并不分配内存空间;定义要在定义的地方为其分配存储空间。
相同变量可以在多处声明(外部变量 extern ),但只能在一处定义。

12.C++C语言的区别

答案:
  • C++newdelete是对内存分配的运算符,取代了C中的mallocfree
  • 标准C++中的字符串类取代了标准C函数库头文件中的字符数组处理函数(C中没有字符串类型)。
  • C++中用来做控制态输入输出的iostream类库替代了标准C中的stdio函数库。
  • C++中的try/catch/throw异常处理机制取代了标准C中的setjmp()longjmp()函数。
  • C++中,允许有相同的函数名,不过它们的参数类型不能完全相同,这样这些函数就可以相互区别开来。而这在C语言中是不允许的。也就是C++可以重载,C语言不允许。
  • C++语言中,允许变量定义语句在程序中的任何地方,只要在是使用它之前就可以;而C语言中,必须要在函数开头部分。而且C++允许重复定义变量,C语言也是做不到这一点的
  • C++中,除了值和指针之外,新增了引用。引用型变量是其他变量的一个别名,我们可以认为他们只是名字不相同,其他都是相同的。
  • C++相对与C增加了一些关键字,如:boolusingdynamic_castnamespace等等

你可能感兴趣的:(嵌入式面试题,c语言,c++,面试,面试题,嵌入式)