华勤驱动开发面试经验

自我介绍

1介绍项目
2项目当中遇到的难点以及怎么解决的

介绍UART IIC SPI CAN通信协议

详见 https://blog.csdn.net/Mr_Guan/article/details/133324610?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%9B%9B%E7%A7%8D%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AE&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-133324610.142^v102^pc_search_result_base5&spm=1018.2226.3001.4187
我觉得这个写的非常好。

指针

如何避免野指针

声明时立即初始化: 指针变量定义时若未明确指向有效内存,应初始化为NULL。
动态分配后检查: 使用malloc/new分配内存后,需检查指针是否为NULL,避免分配失败后误用。
释放内存后立即置空: 调用free或delete后,手动将指针置为NULL,防止成为悬挂指针。
不返回栈内存地址: 函数内局部变量的生命周期仅限于函数作用域,返回其地址会导致野指针。应返回动态分配内存或静态变量地址。
替代裸指针: std::unique_ptr(独占所有权)和std::shared_ptr(共享所有权)可自动管理内存释放,避免手动失误。

指针与数组异同点

关键差异

1内存分配方式
数组:在定义时分配连续的内存空间,大小固定(如 int a[5] 分配20字节)。
指针:是一个变量,存储的是地址,可动态指向任意内存(如 int *p = malloc(20))。

2类型与存储内容
数组:存储实际数据,数组名代表首元素地址(如 a 等价于 &a[0]) 。
指针:存储地址数据,需通过解引用(*p)访问目标值。

3大小计算(sizeof)
数组:sizeof(a) 返回数组总字节数(如 int a[5] 为20字节)。
指针:sizeof(p) 返回指针变量大小(32位系统为4字节,64位为8字节)。

相似性

1访问方式的互通性
数组可通过指针访问:a[i] 等价于 *(a + i)。
指针可模拟数组:p[i] 等价于 *(p + i)(需确保指针指向连续内存)。

2函数参数传递时的退化
数组作为函数参数时,退化为指针(如 void func(int a[]) 实际为 void func(int *a))。

3多维数组与指针的关系
二维数组名可视为指向一维数组的指针(如 int (*p)[5] = a 指向二维数组的行) 。

#define和typedef的异同点

#define预处理指令:在编译前的预处理阶段进行纯文本替换,无类型检查。可定义常量、宏函数、条件编译等,功能更广泛。
typedef编译阶段:为已有类型创建类型安全的别名,编译器会进行类型检查。仅用于类型别名:简化复杂类型(如结构体、函数指针)。

自旋锁和互斥锁的异同点,以及对应使用场景

特性 自旋锁(Spinlock) 互斥锁(Mutex)
等待机制 忙等待(Busy-wait),持续检查锁状态 阻塞等待(Sleep-wait),线程挂起并释放CPU
CPU占用 占用CPU资源(空转) 不占用CPU(线程休眠)
上下文切换 无切换,延迟低 需切换线程,延迟较高(约几微秒)
实现原理 原子指令(如CAS、Test-And-Set) 内核调度+等待队列
锁持有时间 适合极短临界区(如几条指令) 适合较长临界区(如I/O操作)
中断安全性 可用于中断上下文(需禁用中断防死锁) 不可用于中断(可能引发睡眠)
多核适用性 仅多核有效(单核忙等待无意义) 单核/多核均适用
优先级反转风险 高(需配合禁用抢占) 可通过优先级继承缓解

自旋锁的典型场景

短临界区:如修改标志位、计数器等极快操作,避免上下文切换开销。
中断上下文:内核中断处理、硬件驱动等不可睡眠的环境。
多核高竞争:锁竞争不激烈时,自旋锁性能优于互斥锁(如高频短时锁)。

互斥锁的典型场景

长临界区:涉及文件I/O、复杂计算等耗时操作。
用户态线程同步:如多线程共享数据结构、数据库事务等。
可睡眠环境:临界区内可能调用阻塞函数(如malloc、sleep)。

介绍一下内存对齐

内存对齐是指数据在内存中的存储起始地址必须满足特定边界条件(如2、4、8字节的整数倍),其核心目的是提升CPU访问效率和保证硬件兼容性。
原理:CPU通常以固定块(如4字节)访问内存,对齐后数据可一次性读取,而非对齐数据可能需多次操作并拼接,导致性能下降甚至硬件异常(如ARM架构) 。
规则:结构体成员按自身大小或编译器对齐系数(如#pragma pack(n))对齐,整体大小需为最大成员对齐值的整数倍,编译器自动插入填充字节 。
优化:通过调整成员顺序(如按对齐值降序排列)或显式指定对齐(如C++的alignas)可减少内存浪费,提升缓存命中率 。

手撕:用C语言声明一个结构体,结构体包含常量指针和指针常量,在写一个回调函数来打印这个结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>
#include <string.h>

// 定义结构体,包含常量指针和指针常量
typedef struct {
const int* ptr_const_value; // 常量指针(指向的值不可修改)
int* const const_ptr; // 指针常量(指针本身不可修改)
char description[50]; // 附加描述字段
} ComplexStruct;

// 回调函数类型定义
typedef void (*PrintCallback)(const ComplexStruct*);

// 打印结构体内容的回调函数
void print_struct(const ComplexStruct* data) {
printf("Description: %s\n", data->description);
printf("Constant Pointer (ptr_const_value): %d\n", *(data->ptr_const_value));
printf("Pointer Constant (const_ptr): %d\n", *(data->const_ptr));
}

int main() {
int value1 = 42;
int value2 = 100;

// 初始化结构体
ComplexStruct my_struct = {
.ptr_const_value = &value1, // 常量指针指向value1
.const_ptr = &value2, // 指针常量固定指向value2
.description = "Example of const pointer and pointer const"
};

// 通过回调函数打印结构体
PrintCallback callback = print_struct;
callback(&my_struct);


return 0;
}

华勤驱动开发面试经验
https://chrisy0618.github.io/2025/05/09/huaqin/
作者
Chris.Y
发布于
2025年5月9日
许可协议