北京零零科技 嵌入式软件开发一面

1. 先做个自我介绍吧

参考答案

您好,我是XXX,主要从事嵌入式软件开发。技术栈方面,熟练掌握C/C++编程,有单片机裸机开发和Linux系统开发经验。

在单片机方面,做过基于ARM Cortex-M内核的项目,涉及串口通信、ADC采集、PWM控制、定时器等外设驱动开发。

Linux方面,熟悉应用层编程,包括多进程、多线程、网络编程等。

最近在做的项目涉及多核处理器的核间通信和DMA数据传输优化。对嵌入式系统的实时性和资源优化比较有心得。

2. 看你简历上有单片机和Linux开发经验,具体做过哪些?驱动层有接触吗?

参考答案

单片机方面:

主要使用过STM32和Infineon TRAVEO系列MCU,做过完整的外设驱动开发,包括:

  • GPIO控制(按键输入、LED输出)
  • UART串口通信(包括DMA方式收发)
  • ADC多通道采集
  • PWM波形生成(电机控制)
  • 定时器中断
  • 外部中断
  • Flash读写操作

Linux方面:

主要在应用层开发,做过:

  • 多进程/多线程程序设计
  • 进程间通信(管道、共享内存、消息队列)
  • Socket网络编程(TCP/UDP服务器客户端)
  • 文件IO操作

驱动层:

有一定了解,看过字符设备驱动的代码,了解驱动的基本框架(init、exit、open、read、write、ioctl等接口),但实际项目中主要还是调用现成的驱动接口进行应用开发。

3. 内存管理这块,进程的虚拟地址空间是怎么划分的?

参考答案

进程的虚拟地址空间从低到高主要分为以下几个区域:

1. 代码段(.text)

  • 存放程序的可执行指令
  • 只读属性,防止程序被意外修改
  • 多个进程可以共享同一份代码段

2. 数据段(.data)

  • 存放已初始化的全局变量和静态变量
  • 可读可写

3. BSS段(.bss)

  • 存放未初始化的全局变量和静态变量
  • 程序加载时自动清零
  • 不占用可执行文件空间,只记录大小

4. 堆(Heap)

  • 动态内存分配区域
  • 通过malloc/free或new/delete管理
  • 从低地址向高地址增长
  • 程序员手动管理,容易出现内存泄漏

5. 内存映射区(mmap)

  • 动态链接库加载区域
  • 文件映射区域

6. 栈(Stack)

  • 存放局部变量、函数参数、返回地址
  • 从高地址向低地址增长
  • 自动分配和释放
  • 大小有限制(通常几MB)

7. 内核空间

  • 位于最高地址
  • 用户态程序不能直接访问

典型的内存布局:

0xFFFFFFFF  ┌─────────────┐
            │  内核空间   │
            ├─────────────┤
            │    栈 ↓     │
            │             │
            │     ...     │
            │             │
            │    堆 ↑     │
            ├─────────────┤
            │  BSS段      │
            ├─────────────┤
            │  数据段     │
            ├─────────────┤
            │  代码段     │
0x00000000  └─────────────┘

4. 那BSS段的变量有什么特点?初始值是多少?

参考答案

BSS段(Block Started by Symbol)存放未初始化的全局变量和静态变量,有以下特点:

1. 自动初始化为0

  • 所有未显式初始化的全局变量和静态变量,系统会自动将其初始化为0
  • 这是确定性的行为,不是随机值

2. 不占用磁盘空间

  • 在可执行文件中,BSS段只记录大小信息,不实际存储数据
  • 节省可执行文件的体积

3. 运行时分配

  • 程序加载到内存时,操作系统为BSS段分配空间并清零

示例:

int g_uninit;              // BSS段,值为0
static int s_uninit;       // BSS段,值为0
int g_init = 100;          // 数据段,值为100
static int s_init = 200;   // 数据段,值为200

void func() {
    static int local_uninit;  // BSS段,值为0
    int auto_var;             // 栈上,值未定义(随机)
}

注意区别:

  • 全局/静态变量未初始化 → BSS段 → 自动为0
  • 局部变量未初始化 → 栈上 → 值不确定(随机垃圾值)

5. 动态内存分配和释放你了解吗?用什么函数?释放的是哪块内存?

参考答案

C语言动态内存管理

分配函数:

  • malloc(size):分配指定大小内存,内容未初始化
  • calloc(n, size):分配n个size大小的内存,并清零
  • realloc(ptr, new_size):重新调整已分配内存的大小

释放函数:

  • free(ptr):释放之前分配的内存

C++动态内存管理

  • 分配:new / new[]
  • 释放:delete / delete[]

释放的内存位置

动态分配和释放的内存都在堆区(Heap)。调用free或delete后,内存归还给堆管理器,可以被后续的malloc/new重用。

示例:

// C语言
int *p = (int*)malloc(sizeof(int) * 10);  // 堆上分配
if (p != NULL) {
    // 使用内存
    free(p);      // 释放堆内存
    p = NULL;     // 避免野指针
}

// C++
int *arr = new int[10];   // 堆上分配
delete[] arr;             // 释放堆内存
arr = nullptr;

常见问题

  • 内存泄漏:分配后忘记释放
  • 重复释放:对同一指针多次free/delete
  • 野指针:释放后继续使用
  • 不匹配释放:malloc配delete,或new配free

6. 说说你对C语言指针的理解?

参考答案

指针是C语言最核心也最强大的特性,我的理解主要有以下几点:

1. 本质

指针是一个变量,它存储的是另一个变量的内存地址。通过地址可以间接访问和修改数据。

2. 基本操作

int a = 10;
int *p = &a;    // 取地址:&a得到a的地址
*p = 20;        // 解引用:*p访问p指向的内存,修改a的值

3. 指针的优势

  • 高效传参:传递大结构体时,传指针避免拷贝开销
  • 动态内存:通过指针操作堆上分配的内存
  • 直接操作硬件:嵌入式开发中通过指针访问寄存器
volatile uint32_t *reg = (uint32_t*)0x40020000;
*reg |= 0x01;  // 设置寄存器某一位

  • 实现复杂数据结构:链表、树等都依赖指针

4. 指针类型

  • 普通指针int *p
  • 指针数组int *arr[10] - 10个int指针的数组
  • 数组指针int (*p)[10] - 指向含10个int的数组的指针
  • 函数指针void (*func)(int) - 指向函数的指针
  • 多级指针int **pp - 指向指针的指针

5. 注意事项

  • 野指针:未初始化或已释放的指针
  • 空指针:使用前要判断是否为NULL
  • 指针越界:访问超出分配范围的内存
  • 类型匹配:指针类型要与指向的数据类型匹配

6. 嵌入式应用

在单片机开发中,指针用于:

  • 寄存器操作
  • DMA缓冲区管理
  • 中断向量表
  • 内存映射外设访问

7. 函数指针和普通指针的大小一样吗?为什么?

参考答案

在同一个平台上,函数指针和普通数据指针的大小是相同的

原因

无论是函数指针还是数据指针,它们的本质都是存储一个内存地址。地址的大小由系统的地址总线宽度决定,与指向的内容类型无关。

具体大小

  • 32位系统:所有指针都是4字节
  • 64位系统:所有指针都是8字节

验证代码

#include <stdio.h>

void test_func(int x) {
    printf("x = %d\n", x);
}

int main() {
    int a = 10;
    int *p1 = &a;                    // 数据指针
    void (*p2)(int) = test_func;     // 函数指针
    char *p3 = "hello";              // 字符指针
    double *p4;                      // double指针
    
    printf("int*    size: %zu\n", sizeof(p1));
    printf("func*   size: %zu\n", sizeof(p2));
    printf("char*   size: %zu\n", sizeof(p3));
    printf("double* size: %zu\n", sizeof(p4));
    
    // 在64位系统上都输出8,32位系统上都输出4
    return 0;
}

注意

虽然指针大小相同,但指向的内容大小可能不同:

sizeof(int*)    // 指针本身:4或8字节
sizeof(*p)      // 指向的int:4字节
sizeof(double*) // 指针本身:4或8字节
sizeof(*p)      // 指向的double:8字节

8. C语言如何模拟实现面向对象中的成员方法?

参考答案

C语言可以通过结构体+函数指针的方式模拟面向对象的成员方法,这种设计模式在Linux内核和很多C语言框架中广泛使用。

实现方式

#include <stdio.h>
#include <stdlib.h>

// 定义"类"结构体
typedef struct Motor {
    // 数据成员
    int speed;
    int direction;
    
    // 方法成员(函数指针)
    void (*start)(struct Motor* self);
    void (*stop)(struct Motor* self);
    void (*setSpeed)(struct Motor* self, int speed);
    void (*getStatus)(struct Motor* self);
} Motor;

// 实现"成员方法"
void 

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

嵌入式面试八股文全集 文章被收录于专栏

这是一个全面的嵌入式面试专栏。主要内容将包括:操作系统(进程管理、内存管理、文件系统等)、嵌入式系统(启动流程、驱动开发、中断管理等)、网络通信(TCP/IP协议栈、Socket编程等)、开发工具(交叉编译、调试工具等)以及实际项目经验分享。专栏将采用理论结合实践的方式,每个知识点都会附带相关的面试真题和答案解析。

全部评论

相关推荐

晚上和一个老哥聊天,加深了自己对一些事情的思考就是一个人在公共场合敢实名表达自己的感受,自己的思考,自己的观点,是一件需要非常非常大勇气的事情,这意味着你触达内心的想法感受,会被大众所注视,审判,而绝大多数人都会异常在意别人对自己的看法,所以当大规模的眼光都看在你身上的时候,这种压力不是谁都能抗下来的。小一点的是在朋友圈写小作文发表自己内心的想法,之前我是能经常看到不少同学吃一个东西&nbsp;或者&nbsp;去一个地方玩然后长篇大论写下自己感受的朋友圈,但现在我也很少在朋友圈看到这些内容了,大家是长大了,开始忽略这些感受了,还是越来越不愿意拿出来分享了……大一点是直接做自媒体,更大范围地展示自己,直接向全互联网的人述说自己的经历,表达自己的想法,展示自己性感的大脑,让互联网的所有人凝视你,审判你,赞扬你,诋毁你……说实话,这非常像把自己扒光了游街示众的感觉,只有真正在互联网上实名发表过这种口播视频之后,才懂这种感觉有多奇妙哈哈哈我们不说钱不钱的问题,关说对个人能力的提升,这非常锻炼人,非常非常锻炼人,你的表达能力,你的心理素质都在全方面提升,你的心理抗压能力也会不断提升,因为无论怎么说都有人骂你,你说苹果手机好用,都有人骂你叛国贼[捂脸][捂脸][捂脸]一开始最大的障碍就是怕熟人看到,特别尴尬,怕大家议论你,嘲笑你,但其实真的有那么多人关注你吗?真的有那么多人嘲笑你吗?可能都是自己在臆想,出现幻觉了就算真的有人当面嘲笑你,这又怕什么呢,我始终坚信一个真正从0到1在某个领域做成功一件事的人(标准:得到这个领域人的普遍认可)是不会嘲笑一个开始很笨拙的人,因为谁不是这样走过来的?谁一开始就做的很好,谁刚开始做就很随心所欲,是你吗?一个健身大神会嘲笑一个刚入健身房的新手吗?一个高级程序员会嘲笑一个刚学会打hello&nbsp;world的新手吗?一个减肥成功的人会嘲笑刚开始跑几步路就喘的胖子吗?一个作家会嘲笑刚开始写小作文词不达意语句不通顺的菜鸟吗?一个人但凡能嘲笑你,那就证明他没做成哪怕至少一件事,没在一个领域得到绝大多数人的普遍认可,这种人的嘲笑是多么无力,这是他无能的狂怒,他自己不敢,自己半途而废,他怕你做成了,证明他自己是废物而已(这里说话比较难听)从我刚开始从化学跨行当程序员时,我就开始向外展示这些事情,然后无论在现实里还是在互联网上,我都听过非常非常多嘲笑的声音,否定的声音,所以我一度非常敏感,非常脆弱在大二这一年我几乎不敢见人,我每天吃喝拉撒都在实验室的小工位,我怕出去会被人嘲笑,会被否定,因为随随便便一句话我就能蹲在天台哭一晚上,直到我突然进了美团的日常实习,直到我突然进了字节的暑期实习,直到我秋招又拿了字节的offer,这个时候我已经站在高处,我回头看,我向下看,之前那些否定嘲笑的声音早已听不见,我已经在山上了,而他们又在哪呢?而我发现当时那些鼓励我,认可我,支持我的兄弟们,不是那些已经在某个领域取得一定成果的人,就是那些同样在路上的同伴,好像只有这些人,他们才会对蹒跚学步的新手给予鼓励与帮助……最后居然戏剧性地来了一个callback&nbsp;呼应了我大一演小品的一句台词且视他人之疑目如盏盏鬼火大胆地去走你的夜路!这是我自己的亲身经验,也是我想表达,传递的内容,想干什么就去干吧,至于别人怎么看,随他吧,反正弱者才会嘲笑你,强者都会向你伸出援手……
牛友故事会
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务