资讯详情 嵌入式面试题概要
📅 2026/10/29 10:46:18
C语言面试1.在32位系统上以下类型的大小为多少int:4char:1double:8float:4short:2char[5]:5double*:432 位平台指针统一占 4B数组大小 元素个数 × 单个元素大小数组名不是指针。2.c语言的优势****加粗样式低级内存操作支持系统级编程不支持面向对象操作具有良好的可移植性3.0无符号字符是0到255a1后为256在编译器中显示为04.func函数8主函数100主函数计算的是整个数组占用的总字节数func形参会把数组变成指针相当于char*str指针占8个字节5.如何避免头文件重复引用使用宏定义在头文件开头使用#ifndef、#define末尾用#endif包裹头文件内容#pragma once是现代编译器支持的简化方案6.进制转换八进制转十进制按权展开权值为8或者先八转二三位一组二转十六四位一组十进制转十六进制除16取余(从下往上取余)7.描述define与typedef的区别与对应的用法1.#define是预处理文本替换typedef是类型别名2.typedef不能用于定义常量#define可以8.strlen与sizeof的区别sizeof运算符编译期计算得到整个空间占用字节数包括数组末尾\0参数可以是类型数组变量。char s[]“abc”;sizeof(s)4;strlen库函数运行时遍历字符串统计\0之前有效字符个数不包含末尾\0参数必须是以\0结尾的char指针。strlen(s)3;sizeof求内存大小strlen求字符串有效长度。sizeof 包含\0strlen 不计入\0。strlen 只对字符串有效遇到\0停止。9.请详细阐述 static 关键字 在C语言中作用于 局部变量、全局变量、函数 时的区别。并回答在多文件编译中如果在 main.c 中使用 extern 声明一个 static 全局变量链接阶段会发生什么static局部变量改变存储区域延长生命周期作用域不变(函数内static全局变量生命周期不变修改链接属性限制只能本源文件访问static函数只修改链接函数函数只能本源文件调用结果链接报错undefined reference to num(未定义引用10.在裸机ARM Cortex-M单片机环境中有Flash和RAM分别定义11.描述C语言的编译流程每个步骤分别做了什么事?1.预处理展开头文件宏替换条件编译删除注释2.编译:将c文件编译为汇编文件3.汇编将汇编文件转化成目标文件(可执行的二进制文件4.链接:将目标文件和库文件链接成最终的可执行文件12.描述什么叫内存泄漏什么叫内存溢出内存泄漏malloc申请完内存后没有free释放内存溢出向一块内存空间写入的数据超过了该内存块本身的容量。13.写出 const 分别修饰以下含义的定义代码指向的内容不可修改但指针本身可以修改的指针。常量指针const intp指针本身不可修改指针指向的值可以修改的指针。指针常量intconst p指向常量的常量指针。const int* const p一个函数参数为指向常量整数的指针返回值为常量整数指针const intfunc(const intp){return p;}14.struct A {char c1;int i;char c2;};struct B {char c1;char c2;int i;};计算 sizeof(struct A) 和 sizeof(struct B)。struct A:12字节struct B:8字节如果使用 #pragma pack(1)大小分别是多少对齐规则按 1 字节对齐不再填充任何字节成员紧密排列结构体总大小就是所有成员大小之和。struct A :1416字节struct B :1146字节为什么在嵌入式通信中经常需要 #pragmapack(1)使用#pragma pack(1),是为了取消结构体对齐填充,让结构体内存布局和通信协议帧一致,避免收发数据解析错位,同时减少报文占用字节15.写出以下变量的定义一个指针 p1指向一个包含10个 int 元素的数组。int (*p1)[10]一个数组 arr包含10个元素每个元素是一个指向返回值为 int参数为 void 的函数指针。int (*arr[10])(void)写出给 arr 第一个元素赋值 func 函数的语句。arr[0]func//(int func (void));16.#include stdio.hint main() {unsigned int a 10;int b -20;if (a b 0) {printf(“ 0\n”);} else {printf(“ 0\n”);}printf(“%u\n”, a b);return 0;}上述代码输出什么为什么大于0,有符号 int 和无符号 unsigned int 运算时int 会隐式转换为 unsigned int如何修改 if 条件逻辑才能使其符合数学上的 10 (-20) 0 的直观判断if判断中在a前加上(int)17.请用两种不同的方法写一个函数 int is_little_endian(void)返回1为小端0为大端。方法一使用 union。int_little_endian(void){union{int ch;char num;}un;un.num1;return un.ch;}int main(int argc, char const *argv[]){int retis_little_endian();if(ret1){printf(“小端序”);}else{printf(“大端序”);}return 0;}方法二使用 char * 指针强制转换 int。int is_little_endian(void){int ch1;char *p(char *)ch;return *p;}int main(int argc, char const *argv[]){int retis_little_endian();if(ret1){printf(“小端序”);}else{printf(“大端序”);}return 0;}*18.给定一个简单的状态机场景状态有 INIT, RUNNING, STOPPED。请定义一个函数指针数组 void (state_func[])(void)并用它代替 switch-case 实现状态跳转。说明这种表驱动法的优势。函数名自己取。typedef enum{INIT,RUNNING,STOPPED,STATE_MAX}StateLevel;void state_init(void){printf(“进入INIT模式”);}void state_stopped(void){printf(“进入stopped模式”);}void state_running(void){printf(“进入RUNNING模式”);}void (*state_func[])(void){state_init,state_stopped,state_running};void state_switch(StateLevel states){if(statesSTATE_MAX){state_funcstates;}}int main(){state_switch(INIT);state_switch(RUNNING);state_switch(STOPPED);return 0;}**优势:**代码简洁消除冗长的switch-case新增状态只需要添加函数、扩充数组不用修改分支判断。可读性强状态和处理函数一一对应逻辑直观。维护方便状态集中管理便于增删状态。执行效率高直接数组下标跳转不需要逐个 case 比较。19.位操作置位与清零定义一个宏 BIT_MASK(bit) 用于生成对应位的掩码。然后编写两个内联函数set_bit(int *reg, int n) //传递整数reg将第n位置1clear_bit(int *reg, int n) //传递整数reg将第n位置0不允许使用循环和 - 号仅用位运算。#define BIT_MASK(bit) 1u(bit))static inline set_bit(int *reg, int n){*reg|BIT_MASK(n);(或置1}static inline clear_bit(int *reg, int n){*reg~BIT_MASK(n);与取反置0}20.volatile关键字的作用。请列举三个必须使用 volatile 的场景保证可见性禁止指令重排不保证原子性场景1.多线程共享布尔标志位2.访问硬件外设寄存器3.DCL双重检查单例**21.请用C语言实现 char *my_strcpy(chardest, const charsrc)要求数返回 dest 指针。必须处理 dest 与 src 内存重叠的情况吗如果需要怎么写如果不需要为什么1.char *my_strcpy(char *dest, const char *src){char *startdest;保存dest起始地址while(*dest*src){}return start;}2.char *my_strcpy(char *dest, const charsrc){charpdest;(用p做遍历指针while(*p*src){}return dest;数据结构面试1.链表逆序代码编写struct ListNode{int val;struct ListNodenext;};struct ListNodereverselist(struct ListNodehead){struct ListNodeprevNULL;struct ListNodecurrenthead;struct ListNodenextNULL;while(current!NULL){nextcurrent-next;current-nextprev;prevcurrent;currentnext;}return prev;}struct ListNodenewNode(int val){struct ListNodenodemalloc(sizeof(struct ListNode));node-valval;node-nextNULL;return node;}void printList(struct ListNodehead){struct ListNodephead;while(p!NULL){printf(“%d “,p-val);pp-next;}printf(”\n”);}int main(int argc, char const *argv[]){struct ListNodehead newNode(11);head-next newNode(21);head-next-next newNode(31);head-next-next-next newNode(41);head-next-next-next-next newNode(51);printf(“原列表:”);printList(head);struct ListNodenewNodereverselist(head);printf(“反转后:”);printList(newNode);return 0;}2.判断链表是否成环代码编写typedef struct ListNode{int val;struct ListNodenext;}ListNode;int hasCycle(ListNodehead){if(headNULL||head-nextNULL){return 0;}ListNodeslowhead;ListNodefasthead;while(fast!NULLfast-next!NULL){slowslow-next;fastfast-next-next;if(fastslow)return 1;}return 0;}ListNodecreateNode(int val){ListNodenodemalloc(sizeof(ListNode));node-valval;node-nextNULL;return node;}int main(int argc, char const *argv[]){ListNode *n1 createNode(1);ListNode *n2 createNode(2);ListNode *n3 createNode(3);ListNode *n4 createNode(4);ListNode *n5 createNode(5);n1-next n2;n2-next n3;n3-next n4;n4-next n2;if (hasCycle(n1)) {printf(“链表有环\n”);} else {printf(“链表无环\n”);}return 0;}3.数组与链表的区别是什么各有什么优缺点**数组**连续内存支持随机访问**优点**查询快缓存友好**缺点**中间增删慢静态数组容量固定**链表**离散节点靠指针相连无随机访问**优点**动态扩容找到节点后增删快**缺点**查询慢额外指针占用内存缓存差4.栈和队列的区别什么业务场景各有哪些栈(Stack):LIFO后进先出特点只能一端操作(栈顶优点实现简单缺点只能访问栈顶场景函数调用栈表达式求值括号匹配浏览器回退队列(Queue)FIFO先进先出特点两端操作队尾入队队头出队优点有序排队处理缺点不能随机访问中间元素场景消息队列、任务排队、广度优先 BFS、打印任务5.链表排序代码编写struct ListNode{int data;struct ListNodenext;};struct ListNodecreatNode(int val){struct ListNodenodemalloc(sizeof(struct ListNode));node-dataval;node-nextNULL;return node;}struct ListNodeheibinglist(struct ListNodel1,struct ListNodel2){struct ListNode dummy;struct ListNodepdummy;dummy.nextNULL;while(l1!NULLl2!NULL){if(l1-datadata){p-nextl1;l1l1-next;}else{p-nextl2;l2l2-next;}pp-next;}p-nextl1?l1:l2;return dummy.next;}struct ListNodeSort(struct ListNodehead){if(headNULL||head-nextNULL)return head;struct ListNodeslowhead;struct ListNodefasthead-next;while(fast!NULLfast-next!NULL){slowslow-next;fastfast-next-next;}struct ListNodemidslow-next;slow-nextNULL;struct ListNodeleftSort(head);struct ListNoderightSort(mid);return heibinglist(left,right);}struct ListNodeprintlist(struct ListNodehead){struct ListNodephead;while(p!NULL){printf(“%d “,p-data);pp-next;}printf(”\n”);}int main(int argc, char constargv[]){struct ListNodeheadcreatNode(5);head-nextcreatNode(9);head-next-nextcreatNode(4);head-next-next-nextcreatNode(2);head-next-next-next-nextcreatNode(3);head-next-next-next-next-nextcreatNode(1);printf(“原代码:”);printlist(head);headSort(head);printf(“排序后的代码:”);printlist(head);return 0;}6.编写快排或其它高性能排序算法的代码并描述时间复杂度与空间复杂度以及稳定性void quickSort(int arr[],int left,int right){if(leftright)return;int ileft;int jright;int pivotarr[left];while(ij){while(ijarr[j]pivot){j–;arr[i]arr[j];}while(ijarr[i]pivot){i;arr[j]arr[i];}arr[i]pivot;}quickSort(arr,left,i-1);quickSort(arr,i1,right);}时间复杂度平均O(n log n),最坏O(nn)空间复杂度空间开销来自递归调用栈平均O(log n递归深度log n最坏O(n)递归深度n7.已知二叉树的前序和中序如何知道后序遍历前序根 → 左子树 → 右子树中序左子树 → 根 → 右子树前序第一个元素是根节点在中序序列找到根的位置根左边全部是左子树节点右边全部是右子树节点根据左子树节点数量切割前序数组分出左子树前序、右子树前序递归构造左子树、右子树后序输出规则先递归左再递归右最后输出根8.编写二分法查找代码int erfencheck(int arr[],int left,int right,int target){while(leftright){int midleft(right-left)/2;if(arr[mid]target)return mid;else if(arr[mid]target)leftmid1;elserightmid-1;}return -1;}int main(int argc, char constargv[]){int arr[]{1,2,3,4,5,6,7,8,9};int indexerfencheck(arr,0,8,7);printf(“%d”,index);return 0;}9.霍夫曼树权值计算方法WPL叶子结点权值 × 叶子到根的边数全部相加快速验算技巧哈夫曼树 WPL 另一种算法所有非叶子结点相加10.合并有序链表代码编写struct ListNodeheibinglist(struct ListNodel1,struct ListNodel2){struct ListNode dummy;struct ListNode*pdummy;dummy.nextNULL;while(l1!NULLl2!NULL){if(l1-datadata){p-nextl1;l1l1-next;}else{p-nextl2;l2l2-next;}pp-next;}p-nextl1?l1:l2;return dummy.next;}IO面试1.Linux操作系统下各路径存放什么文件usr/dev/opt/etc/home/bin/media/userunix系统资源存放用户程序工具库文档/dev设备文件目录硬件映射成文件硬盘外设/opt第三方大型应用软件存放目录/etc系统文件配置目录/home普通用户家目录/bin存放系统基础核心命令lscp等所有用户可用/media可移动介质的自动挂载点2.Linux操作指令查看文件前5行head -5查看文件后5行 且实时查看tail -5f搜索文件是否包含特定内容grep 关键词“ ”文件“grep -r ”关键词“/homegrep -ni ”关键词“ 文件”压缩与解压文件gzip文件tar zcvf test.tar.gztar zxvf test.tar.gzbz2文件:tar jcvf test.tar.bz2tar jxvf test.tar.bz2xz文件:tar Jcvf test.tar.xztar Jxvf test.tar.xz创建软连接与硬链接ln 源文件 硬链接名ln -s 源文件/目录 软连接名如何修改某网卡的ip地址ip addr del 网卡地址 子网掩码 网卡设备 网卡名删除ip addr add 网卡地址 子网掩码 网卡设备 网卡名添加3.软连接与硬链接 的区别4.环境变量有什么用什么时候需要使用到环境变量如何配置环境变量**作用**系统键值对给shell程序提供全局配置环境PATH命令搜索路径**使用场景**配置命令搜索路径指定库/头文件路径给程序传递全局参数脚本共享变量配置临时export用户永久~/.bashrc(source执行全局永久/etc/profilesudo或root执行5.现有文件main.c 1.c 1.h 2.c 2.h 五个文件编写一个Makefile将五个文件编译成main.exe,要求使用符号优化写法并增加clean指令main.exe:main.o1.o2.o gcc main.o1.o2.o-o main.exe main.o:main.c1.h2.h gcc-c main.c-o main.o1.o:1.c1.h gcc-c1.c-o1.o2.o:2.c2.h gcc-c2.c-o2.o clean:rm-f*.o main.exe6、GDB调试一般用于排查什么问题GDB的调试流程排查段错误内存异常崩溃死循环死锁core文件崩溃现场流程gcc -g编译带调试信息gdb加载程序breakb打断点r运行s/n单进/单跳p看变量bt看栈定位bug7、段错误是什么意思产生段错误的原因有哪些含义程序访问无权访问的内存原因空指针野指针数组越界修改常量字符串栈溢出访问受保护内存8、标准IO与文件IO的区别有哪些文件IO系统调用fd无用户缓冲区移植差适合设备管道标准IOc库封装FILE*自带用户缓冲区可移植减少系统调用标准IO底层依赖文件IO9、标准IO的缓冲区有哪些作用于哪里刷新条件有哪些为什么需要用到缓冲区全缓冲普通文件行缓冲stdout终端无缓冲stderr刷新缓冲区满遇\fflush自动刷新fclose程序正常退出作用减少用户内核态切换降低系统调用开销提高IO速度10、动态库和静态库的区别各有什么好处链接方式静态库在编译时链接动态库在运行时链接可执行文件体积;静态库使可执行文件体积大动态库使可执行文件体积小内存占用多个程序使用同一静态库时占用内存多多个程序共享同一动态库时节省内存更新维护静态库更新需要重新编译链接程序动态库更新只需替换库文件依赖性静态库无运行时依赖动态库有运行时依赖库缺失程序无法运行11、使用文件IO实现文件的拷贝#includestdio.h#includefcntl.h#includesys/stat.h#includesys/types.h#includeunistd.hintmain(intargc,charconst*argv[]){if(argc!3){printf(输入参数错误);printf(usage:./a.out src dst\n);return-1;}intfd_src0;if((fd_srcopen(argv[1],O_RDONLY))0){perror(源文件打开失败);return-1;}printf(源文件打开成功\n);intfd_destopen(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0644);if(fd_dest0){perror(目标文件打开失败);return-1;}printf(目标文件打开成功\n);charbuf[BUFSIZ]{0};ssize_tread_ret;while((read_retread(fd_src,buf,sizeof(buf)))0){write(fd_dest,buf,read_ret);}if(read_ret0){perror(读取失败);return-1;}close(fd_src);close(fd_dest);return0;}