2021年嵌入式面试题汇总(最新经典)

写在前面:秋招嵌入式开发方向,经过了很多场的笔试与面试,在准备的过程中看了非常多的资料,现在把他们整理一下,有的资料看过了觉得不错就保存下来了,如果有不对的地方,欢迎批评指正,侵权联删!(慢慢更新过程中........)

-------------------------------------------------------------------------------------------------------------------

目录

1、c语言内存模型

2、C语言中的变量定义在什么地方

3、C语言代码如何运行的、关于栈的相关

4、指针函数与函数指针的区分

5、Static关键字的作用

6、const作用

7、进程与线程的区别

8、链表与数组的区别

9、#define宏定义与typedef的区别


1、c语言内存模型

2、C语言中的变量定义在什么地方

内存中供用户使用的存储空间分为三部分:1、程序区,2、静态存储区3、动态存储区。

内存中局部变量定义在栈区,全局变量和静态变量放在静态数据区。

还不理解可参考:动态存储区、静态存储区、堆和栈的区别_qian_enjoy-CSDN博客

3、C语言代码如何运行的、关于栈的相关

   1、预处理--编译--汇编--链接

      (1)预处理(Preprocessing):用于将所有的#include头文件以及宏定义替换成其真正的内容;

      (2)编译(Compilation):将经过预处理之后的程序转换成特定汇编代码的过程;

      (3)汇编(Assemble):将上一步的汇编代码转换成机器码,产生的文件叫做目标文件(.o);

      (4)链接(Linking):链接过程将多个目标文件以及所需的库文件(.so等)链接成最终的可执行文件(.exe)。

   2、程序定义的“局部变量”会放在计算机内存中(栈),栈在数据结构是最基本的操作,只有两个操作,一个是push入栈,另一个是pop出栈。在c的栈中,是从栈顶向下开始存入变量的。

4、指针函数与函数指针的区分

指针函数

简单来说,就是一个返回指针的函数。本质是一个函数,函数返回的类型是一个指针。

普通函数的声明:

 int sort (int a,int b);

指针函数的声明:

 int *sort(int a,int b);

函数指针

本质是一个指针变量,该指针指向这个函数。总结来说,函数指针就是指向函数的指针。

函数指针的声明:

 int  (*sort)(int a,int b);

函数指针的赋值有两种方法:

 sort=&a;
 sort=a;

取地址运算符&不是必需的,因为一个函数标识符就表示了它的地址,如果是函数调用,还必须包含一个圆括号括起来的参数表。

两者的不同

1、定义不同

指针函数本质上是一个函数,其返回值时指针。

函数指针本质上式一个指针,其指向一个函数。

2、写法不同

 指针函数:int *sort(int a,int b);
 函数指针:int (*sort)(int a,int b);

5、Static关键字的作用

1、作用于变量:(参考内存分部,静态存储区与动态存储区,看前面的3和4)

用Static声明局部变量------局部变量是指在代码块内定义的变量,只在代码块内起作用,其缺省的存储方式是自动变量或说是动态存储的,即指令执行到变量时才会给变量分配存储单元,调出代码块时释放内存单元。用Static关键字声明后,是变量成为静态的局部变量,即编译时就为变量分配存储单元,直到程序结束时才释放。这样,使得该局部变量有记忆功能,可以记忆上次的数据,不过由于仍是局部变量,因而只能在代码块内部使用(作用域不变)。

用Static声明全局变量-----全局变量指在所有代码块{}之外定义的变量,它缺省为静态变量,编译时分配内存,程序结束时释放内存单元。同时全局变量作用域很广,整个文件都有效甚至别的文件也能引用它。为了限制某些全局变量的作用域,使其只在本文件中有效,而不能被其他文件引用,可以用static关键字对其作出声明。

总结:用static声明局部变量,使其变为静态存储方式(静态数据区),作用域不变;用static声明外部变量,其本身就是静态变量,这只会改变其连接方式,使其只在本文件内部有效,而其他文件不可连接或引用该变量。

2、作用于函数

使用static用于函数定义时,对函数的连接方式产生影响,使得函数只在本文件内部有效,对其他文件是不可见的。这样的函数又叫作静态函数。使用静态函数的好处是,不用担心与其他文件的同名函数产生干扰,另外也是对函数本身的一种保护机制。

  如果想要其他文件可以引用本地函数,则要在函数定义时使用关键字extern,表示该函数是外部函数,可供其他文件调用。另外在要引用别的文件中定义的外部函数的文件中,使用extern声明要用的外部函数即可。

6、const作用

1、定义常量

(1)const(修饰的变量不可变)

修饰变量,以下两种定义形式在本质上是一样的。它的含义是:const修饰的类型为TYPE的变量value是不可变的,readonly。

TYPE const ValueName = value;

const TYPE ValueName = value;

(2)将const改为外部连接,作用于扩大至全局,编译时会分配内存,并且可以不进行初始化,仅仅作为声明,编译器认为在程序其他地方进行了定义. (修饰的外部链接可以不用定义直接声明使用)

extend const int ValueName = value;

2、指针使用const()

(1)指针本身是常量不可变

char * const pContent;

const (char*) pContent;

(2)指针所指向的内容是常量不可变

const char *pContent;

char const *pContent;

(3)两者都不可变

const char* const pContent;

(4)还有其中区别方法,沿着号划一条线:如果const位于的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于*的右侧,const就是修饰指针本身,即指针本身是常量。

7、进程与线程的区别

根本区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位

在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。

所处环境:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)

内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。

包含关系:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。

总结一下:进程:1、操作系统会分配地址空间 2、一个进程当中可以有多个线程

线程:1、没有独立的地址空间 2、一个进程里的多个线程可以共享该进程的所有资源 3、线程有自己的栈、堆和局部变量 4、线程是不能独立执行的

补充:

1、同步和互斥的区别

当有多个线程的时候,经常需要去同步这些线程以访问同一个数据或资源。例如,假设有一个程序,其中一个线程用于把文件读到内存,而另一个线程用于统计文件中的字符数。当然,在把整个文件调入内存之前,统计它的计数是没有意义的。但是,由于每个操作都有自己的线程,操作系统会把两个线程当作是互不相干的任务分别执行,这样就可能在没有把整个文件装入内存时统计字数。为解决此问题,你必须使两个线程同步工作。

所谓同步,是指散步在不同进程之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。如果用对资源的访问来定义的话,同步是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。

所谓互斥,是指散布在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行。如果用对资源的访问来定义的话,互斥某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

2、进程间通信的方式

(1)管道(pipe)及有名管道(named pipe):管道可用于具有亲缘关系的父子进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。

(2)信号(signal):信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一致的。

(3)消息队列(message queue):消息队列是消息的链接表,它克服了上两种通信方式中信号量有限的缺点,具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息。

(4)共享内存(shared memory):可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。

(5)信号量(semaphore):主要作为进程之间及同一种进程的不同线程之间得同步和互斥手段。

(6)套接字(socket):这是一种更为一般得进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛。

8、链表与数组的区别

链表是一种上一个元素的引用指向下一个元素的存储结构,链表通过指针来连接元素与元素;

链表分为:单向链表、双向链表、循环链表

链表是链式的存储机构;数组是顺序的存储机构

链表通过指针来连接元素与元素,数组则是把所有元素按次序依次存储。

总结:

1)数组便于查询和修改,但是不方便新增和删除

2)链表适合新增和删除,但是不适合查询,根据业务情况使用合适的数据结构和算法是在大数据量和高并发时必须要考虑的问题

9、#define宏定义与typedef的区别

# define宏定义是字符替换,typedef是定义类型,是声明一种新的类型,等同自带的基本类型。

  • #define是宏,处理的时候位于编译前阶段,宏处理器基本上对你的C/C++程序不会有任何的感知。它只处理宏的语法。而编译阶段的“程序”得到的是宏处理完的结果。

  • typedef是编译阶段的一部分。它的意义是单一的。

宏定义只是简单的字符串代换,是在预处理完成的,而typedef是在编译时处理的,它不是作简单的代换,而是对类型说明符进行重新命令。被命名的标识符具有类型定义说明的功能。

 
 
 #include<stdio.h>
 #include<iostream>
 #define PIN1 char*
 typedef char* PIN2;
 ​
 int main() {
     //使用#define相当于:char* x, y;就是说x是char指针类型,而y是char类型
     PIN1 x, y;
     //使用typedef相当于:char* x, char* y;就是说x是char指针类型,而y也是char指针类型
     PIN2 a, b;
     printf("By #define :%d  %d\n\n", sizeof(x), sizeof(y));
     printf("By typedef :%d  %d\n\n", sizeof(a), sizeof(b));
     system("pause");
     return 0;
}

猜你喜欢

转载自blog.csdn.net/CUI9015/article/details/120666803
今日推荐