找工作笔试面试题目集(嵌入式软件篇)----持续更新

这里将自己遇到的,做错的题目进行一个记录和整理。

1.C语言函数中,strcpy和memcpy的区别是什么?

答:strcpy和memcpy都是将存储器的东西复制到另一个存储器。但是这两个的区别在于:

  • strcpy常用于复制字符串,而memcpy可以复制任何内容。
  • strcpy在复制字符串的时候,不需要指定长度,它会一直复制直到字符串的结束符“\0”,连同结束符共同复制后结束复制,因此如果复制的对象不是个字符串,那么就会一直复制下去,因此不能用于复制其他内容。memcpy在复制的时候,除了需要指定起始地址之外,还需要指定复制的长度。

2.static的作用?

答:static关键字用于在变量定义时修饰变量。其作用主要有以下几点:

  • 设置变量的存储域,函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
  • 限制变量的作用域,在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
  • 限制函数的作用域,在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;

具体来说,其表现为:

  • 当static修饰局部变量时(该局部变量被称为静态局部变量),其存储位置发生改变。通常情况下,局部变量存储在栈区,但是静态局部变量存储在静态区(关于存储分区的作用,将在后面进行详解)。实际上局部变量修饰的全局变量,也存储在静态区。由于静态局部变量的存储位置唯一确定,因此带来的效果是,该变量只会被初始化一次,其值在下次调用时仍维持上次的值,且该变量的生命周期被延长,直到程序结束才销毁。
  • 当static修饰全局变量时,原来可以通过extern来使用没有包含声明的全局变量,但是对于静态全局变量,只有包含了该静态变量的声明,才能够使用该全局变量。
  • 当static修饰函数时,只有包含了该静态函数的声明,才能够使用该函数。

3.如何在外部访问static变量?

答:对于static变量,虽然其作用于范围确定了,一般来说外部是不能访问的,但是其存储空间是唯一确定的,因此如果能够得到该变量在存储器中的地址,那么我们就可以通过取地址的方式得到该变量的数值。

4.存储器中都有哪些存储区,其意义是什么?

答:代码区、常量区、静态区(全局区)、堆区、栈区。各种区的意义是:

  • 代码区(Code):
    顾名思义,代码区所存放的数据是程序代码,为只读性的存储,且掉电不丢失。以STM32为例,其代码区所存放的物理介质为Flash,其起始地址一般为0x0800 0000。

  • 常量区(RO-data):
    常量区存储的数据为所定义的常量和常量字符串,也为只读性存储,且掉电不丢失。以STM32为例,其常量区所存放的物理介质为Flash,其起始地址一般在代码区之后。

  • 静态区(RA-data & ZI-data):
    静态区存储的数据为静态变量和全局变量,这些变量在程序开始的时候存在,其生命周期直到本次程序结束,为可读写性的存储,掉电丢失。以STM32为例,其静态区所存放的物理介质为RAM,其起始地址一般为0x2000 0000。此外,静态区中已经初始化的静态变量和全局变量,存放在静态区中的一部分,称为RA-data,没有被初始化的静态变量和全局变量,系统默认初始化为0存放在静态区中的另一部分,称为ZI-data。

  • 堆区(heap):
    堆区是内存中定义的一块存储区域,其目的是为程序员提供一个可以自行分配使用的内存空间,堆区中的内存池由程序员通过malloc函数申请,使用完成后要由程序员通过free函数进行释放,没有释放的内存将一直被占用直到程序结束。在STM32中可以在如 Heap_Size EQU 0x00000200中,通过设置后面的地址空间大小来设置堆区的大小,此外堆区也是RAM中的一部分,因此也会掉电丢失。

  • 栈区(stack)
    栈区同样是内存中定义的一块存储区域,其目的是为程序提供一个灵活的内存空间以存放临时数据,因此栈区是由编译器自动分配和释放 ,用于存放函数的参数值、局部变量的值、中断现场等临时数据,当操作完成后,栈中的数据就会被释放。在STM32中可以在如 Stack_Size EQU 0x00000400中,通过设置后面的地址空间大小来设置栈区的大小,此外栈区也是RAM中的一部分,因此也会掉电丢失。栈区与堆区的区别在于,堆区是程序员自行分配和释放,较为灵活,但是对处理器的占用较大以及安全性较差;而栈区是由处理器本身自行分配和释放,程序员无法控制,因此灵活性差,但是速度快,且安全,不易出现内存没有被回收的情况。
    (堆和栈的区别可以用如下的比喻来看出:使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大)

5.进程间的通讯方式是什么?

答:由于进程在运行的时候,相互之间可能存在着一定的关系,例如不同的进程都要使用相同的外设,那么这两个进程之间的运行就是互斥的;或者有些进程的运行存在着先后次序等关系。如何保证进程之间的正常工作,就需要进程之间通过通讯的方式传递信息。
通信的基本原理就是,通过一个多个进程都可以访问的载体,来在进程之间传递数据。根据该载体的结构不同,具体方法有:

  • 匿名管道(pipe)以及命名管道(named pipe):
    在linux系统中,对于有亲属关系的进程之间可以通过匿名管道通讯。对于无亲属关系进程之间,可以通过命名管道通信。
  • 信号量与信号量集:
    信号量是一种标志式的数据结构,其所传递的信息往往是某种状态,例如某些共享设备的被占用情况等。信号量集就是实现了多个信号量组合的数据结构。
  • 消息与消息队列:
    消息是一种指针式的数据结构,其所传递的信息往往是某些数据,例如像其他进程传递一些参数或字符串等。消息队列就是由多个消息组成的链表结构,可以传递更多的数据。
  • 共享内存:
    多个进程也可以共同访问一块内存空间,这种通信方式比较块,但是由于这种通信方式不具备同步和互斥机制,往往需要使用信号量或锁来配合使用。
  • 套接字(socket):
    对于网络上不同进程之间的通信,常采用套接字的方法来实现。套接字所表示的端口号,就是用于数据通讯的介质,进程之间通过将数据传输到socket所表示的端口中,由发送设备将数据发送到接收设备的socket端口中,接收设备的进程再从接收设备的socket端口中得到数据,即完成一次通信。

6.什么是死锁?

答:死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。举例来说,线程1使用了资源A,请求使用资源B,而线程2使用了资源B,请求使用资源A,这样这两个进程就由于始终无法得到所需要的资源从而永远的阻塞运行。产生死锁的四个条件是:

  1. 互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
  2. 请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
  3. 不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
  4. 环路等待条件:在发生死锁时,必然存在一个进程–资源的环形链。

因此解决死锁的方法是:

  1. 资源一次性分配:一次性分配所有资源,这样就不会再有请求了:(破坏请求条件)
  2. 只要有一个资源得不到分配,也不给这个进程分配其他的资源:(破坏保持条件)
  3. 可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)
  4. 资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)
  5. 超时放弃资源:当请求超时后,线程放弃执行,从而释放自己所占用的资源。
  6. 对线程状态进行监控,发现死锁后通过剥夺资源或撤销线程等方式解除死锁。

但是上述解决死锁的方法基本上都是以损害系统性能和正常运行秩序的方式来实现的。因此我们在进行程序设计时,需要注意尽量避免死锁的产生。

7.const和define的区别?

答:对于define,其作用阶段是在编译阶段,在编译程序时,被define关键词修饰的词语会先被替换,然后才进行编译。在程序运行阶段,已经没有define参与运行了。
而const也常用于修饰常量,其修饰的常量时只读的,不可更改。与define不同的是,const所修饰的就是一个变量,其在程序运行时也发挥作用。
const与define相比,其优点在于:

  1. const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
  2. 有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。
  3. const可节省空间,避免不必要的内存分配,提高效率,而define在赋值的时候,可能会对同一个常量多次赋值,增加了内存。

8.对于一个频繁使用的短小函数如何实现比较好?

答:对于C语言来说,采用宏函数的方法来实现。
对于C++来说,采用内联函数inline来实现。
其思想都是以代码存储空间为代价,减少由于函数调用而产生的时间上、内存上的开支。
内联函数相较于宏函数来说,宏定义一般不进行参数类型检查,内联函数会进行类型检查,更加安全。且宏是由预处理器对宏进行替代,编译器接触到的已经是替代后的代码,而内联函数是通过编译器控制的,由编译器控制将内联函数的代码插入到响应的语句段中。

9.局部变量和全局变量的区别?

答:建议从变量的生命期、作用域、存储域等方面来比较:

  1. 生命期来说,全局变量的生命期是从程序开始到程序结束,随程序的销毁而销毁;局部变量的生命期只有函数内部,随着函数的结束,局部变量被销毁。
  2. 作用域来说,全局变量的作用域为整个程序,局部变量的作用域只有当前函数。
  3. 存储域来说,全局变量存储在全局区(静态区),而局部变量存储在栈区。

10.函数的参数存放在哪里?

答:函数的参数存放在栈区。

你可能感兴趣的:(理论基础)