【面试笔记】嵌入式软件工程师,汽车电子软件相关

news2024/12/24 4:28:20

在这里插入图片描述

文章目录

  • 1. C语言基础
    • 1.1 const
    • 1.2 static
    • 1.3 回调函数的用法
    • 1.4 宏定义
    • 1.5 编译、链接过程
    • 1.6 堆与栈的区别?
    • 1.7 简单的字符串算法题,C语言实现
      • 1.7.1 给定一个字符串,按顺序筛选出不重复的字符组成字符串,输出该字符串
      • 1.7.2 给定4*4矩阵,回文打印输出
    • 1.8 字节对其
  • 2. MCU相关
    • 2.1 MCU的启动过程描述
    • 2.2 MCU的内存布局
    • 2.3 使用volatile关键字的作用?
  • 3. 汽车电子软件
    • 3.1 CAN/CANFD相关
      • 3.1 概述一个CAN消息如何被发送和接收的
      • 3.2 CAN和FIFO CAN
      • 3.3 CANFD的知识点
    • 3.2 概述bootloader实现要点
      • 3.2.1 跳转前要做什么?
    • 3.3 简述ASPICE在项目研发中的应用
    • 3.4 举例说明某个功能安全需求的实现过程
    • 3.5 概述14229协议
    • 3.6 概述15765协议


1. C语言基础

1.1 const

修饰变量
只可访问,不可重新赋值。

const int MAX_VALUE = 100;

void printValue(const int value);

修饰指针

  • 限制指向位置
const int *ptr;
  • 限制指向数据
const int *const ptr;

1.2 static

静态变量
使用 static 关键字声明静态变量时,变量的生命周期会延长到整个程序运行期间,而不仅仅局限在其定义的作用域内。静态变量在第一次被赋值时初始化,并且保留其值直到程序结束。

静态全局变量
使用 static 关键字在全局作用域中声明的变量具有静态存储持续时间,但是其作用域被限制在声明该变量的源文件内。这使得该变量对其他源文件不可见,可以防止命名冲突。

静态函数
使用 static 关键字声明静态函数时,该函数仅在声明所在的源文件中可见,即它具有内部链接性。静态函数的作用域仅限于声明所在的源文件。这种方式可以避免与其他源文件中的同名函数产生冲突。


1.3 回调函数的用法

用于在函数执行过程中调用另一个函数。回调函数允许我们向一个函数传递另一个函数的地址,从而在需要时执行特定的操作。回调函数通常用于事件处理、异步编程、库函数的扩展等场景。

  1. 定义回调函数
    首先定义一个函数作为回调函数,其函数原型应与回调的要求相匹配。例如:
void callbackFunction(int result) {
    printf("Callback result: %d\n", result);
}
  1. 在函数中注册回调函数
    在需要的地方将回调函数注册进目标函数中,通常通过函数指针实现。例如:
void performOperation(void (*callback)(int)) {
    int result = 100; // 模拟操作结果

    // 执行操作...

    // 调用回调函数
    callback(result);
}
  1. 调用包含回调函数的函数
    最后调用包含回调函数的函数,将回调函数的地址传递给要调用的函数。例如:
int main() {
    performOperation(callbackFunction); // 注册回调函数
    return 0;
}

在这个示例中,performOperation 函数执行某个操作后调用了注册的回调函数 callbackFunction,并将结果传递给回调函数进行处理。

通过回调函数,我们可以实现灵活的程序设计,允许函数根据不同情况来调用不同的操作,增加了程序的可扩展性和可重用性。当需要在函数执行过程中动态切换功能时,回调函数是一个非常有用的工具。

使用回调函数有以下一些好处:

  1. 灵活性和可扩展性
    回调函数提供了一种灵活的机制,使得代码可以在不同的场景中进行定制和扩展。通过将特定的功能封装在回调函数中,可以根据需要动态地更改或添加行为,而无需修改主函数的逻辑。
  2. 解耦和模块化
    回调函数有助于将不同的功能模块分离,使代码更具有模块化和可维护性。主函数可以专注于其核心逻辑,而将特定的任务委托给回调函数来处理。这样可以提高代码的可读性和可重用性。
  3. 异步处理和事件驱动
    回调函数常用于异步操作或事件驱动的场景中。例如,在异步 I/O 操作完成或特定事件发生时,可以通过回调函数来处理相应的逻辑。这有助于提高程序的并发性和响应性。
  4. 定制性和扩展性
    回调函数允许用户提供自己的自定义逻辑,以满足特定的需求。这使得程序可以更好地适应各种不同的用例和业务逻辑。
  5. 代码复用
    回调函数可以作为可复用的模块,在多个地方被调用,从而减少代码冗余。

需要注意的是,在使用回调函数时,要确保正确处理回调函数的参数和返回值,并注意线程安全等问题。合理使用回调函数可以提高代码的灵活性和扩展性,但也需要谨慎设计和管理,以避免引入复杂度过高或难以调试的问题。


1.4 宏定义

在 C 语言中,宏定义是一种预处理器指令,用于在编译阶段进行文本替换。它允许你定义一个标识符(通常是一个宏名),并将其与一个特定的文本表达式或代码块关联起来。当在代码中使用该宏名时,编译器会将其替换为相应的文本。
宏定义的常见用法和好处包括:

  1. 常量定义
    使用宏定义可以创建常量,例如定义一些具有特定值的常量,以增强代码的可读性和可维护性。
  2. 代码简化和抽象
    宏定义可以用于简化复杂的表达式或代码块,使其更易于阅读和理解。例如,将常用的计算或操作封装在宏中,以便在多个地方重复使用。
  3. 条件编译
    通过宏定义可以实现条件编译,根据不同的条件编译不同的代码块。这对于处理不同平台、版本或配置的情况非常有用。
  4. 代码移植性
    宏定义可以帮助提高代码的可移植性。例如,可以使用宏来定义平台特定的代码或处理不同编译器的差异。
  5. 提高性能
    在一些情况下,宏定义可以提供一定的性能优势,特别是对于一些简单的计算或操作。
    例如,以下是一个简单的宏定义示例:
#define MAX_SIZE 100

在上面的示例中,MAX_SIZE 是一个宏名,100 是它关联的文本。在代码中使用 MAX_SIZE 时,它将被替换为 100。
需要注意的是,宏定义也有一些潜在的问题和限制:

  1. 宏展开问题
    宏在编译时会进行文本替换,可能会导致一些意外的副作用,例如嵌套宏展开、参数求值顺序等问题。
  2. 缺乏类型检查
    宏不进行类型检查,可能会导致在使用时出现类型不匹配或其他错误。
  3. 可读性问题
    过度使用宏可能会使代码变得难以理解,特别是当宏的定义和使用变得复杂时。

因此,在使用宏定义时,应该谨慎考虑,并确保其使用不会导致代码的可读性和可维护性下降。在一些情况下,使用函数或其他语言特性可能是更好的选择。


1.5 编译、链接过程

在这里插入图片描述
预处理
根据以字符#开头的命令修饰的main.c的C源文件,生成预处理后的C源文件 main.i。
该过程主要进行文本替换、宏展开、删除注释等工作。
对应的gcc命令:

gcc -E main.c main.i

编译
编译器将文本文件main.i翻译(编译)成汇编文件main.s
对应的gcc命令:

gcc -S main.i mian.s

汇编
编译器将main.s翻译成机器语言指令,并把这些指令打包成一种可重定位目标程序的格式,并将结果保存在目标文件main.o中

把一个源文件翻译成目标程序的工作过程分为五个阶段:词法分析、语法分析、语义检查和中间代码生成。主要是进行词法分析和语法分析,又称为源程序分析,分析过程中发现语法错误并给出提示信息。
对应的gcc命令:

gcc -c main.s mian.o

链接
该过程编译器将静态库和动态库的库函数链接到可执行程序中。
静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时就不在需要库文件了,其后缀一般为.a。
动态库则是在程序运行时被链接加载,这样可以节省系统的开销,其后缀一般为.so,gcc在编译时默认使用动态库。


1.6 堆与栈的区别?

请添加图片描述

  1. 栈空间是系统自动分配和回收,堆的空间是用户手动分配回收的;
  2. 栈空间较小,堆空间较大;
  3. 栈的地址空间向下生长,堆则向上生长;
  4. 栈的存储效率更高。
    参考:栈和堆,以STM32为例说明

1.7 简单的字符串算法题,C语言实现

1.7.1 给定一个字符串,按顺序筛选出不重复的字符组成字符串,输出该字符串

参考示例:

#include <stdio.h>
#include <string.h>

void removeDuplicates(char *str) {
    int len = strlen(str);
    if (len < 2) return;

    int tail = 1;
    for (int i = 1; i < len; ++i) {
        int j;
        for (j = 0; j < tail; ++j) {
            if (str[i] == str[j]) break;
        }
        if (j == tail) {
            str[tail] = str[i];
            ++tail;
        }
    }
    str[tail] = '\0'; //此处是关键
}

int main() {
    char str[100];
    printf("Enter a string: ");
    scanf("%s", str);

    removeDuplicates(str);

    printf("String with duplicates removed: %s\n", str);

    return 0;
}

测试结果:

Enter a string: asbdssjikSNjs78137!@#ssa00smk
String with duplicates removed: asbdjikSN7813!@#0m

1.7.2 给定4*4矩阵,回文打印输出

参考示例:

#include <stdio.h>

#define ROWS 4
#define COLS 4

void printClockwise(int matrix[ROWS][COLS]) {
    int top = 0, bottom = ROWS - 1, left = 0, right = COLS - 1;

    while (top <= bottom && left <= right) {
        // Print top row
        for (int i = left; i <= right; ++i)
            printf("%d ", matrix[top][i]);
        top++;

        // Print right column
        for (int i = top; i <= bottom; ++i)
            printf("%d ", matrix[i][right]);
        right--;

        // Print bottom row
        if (top <= bottom) {
            for (int i = right; i >= left; --i)
                printf("%d ", matrix[bottom][i]);
            bottom--;
        }

        // Print left column
        if (left <= right) {
            for (int i = bottom; i >= top; --i)
                printf("%d ", matrix[i][left]);
            left++;
        }
    }
}

int main() {
    int matrix[ROWS][COLS] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12},
        {13, 14, 15, 16}
    };

    printf("Clockwise printing of the matrix:\n");
    printClockwise(matrix);

    return 0;
}

测试结果:

Clockwise printing of the matrix:
1 2 3 4 8 12 16 15 14 13 9 5 6 7 11 10

参考:算法11:顺时针转圈打印矩阵


1.8 字节对其

问题】32位系统,一个结构体中,成员依次是char、short、int、char类型,问这个结构体总共占多少字节?
回答这个问题需要深刻理解结构体所占空间的分布:

|char |-----|short|short|4字节
|int  |int  |int  |int  |4字节
|char |-----|-----|-----|4字节

所以,该结构体共占12字节

测试代码:

#include <stdio.h>

struct TMP{
    char a;
    short b;
    int c;
    char d;
};

int main(void) {
    printf("size = %d", sizeof(struct TMP));
    return 0;
}

总结:

  • 结构体成员占位是其自身类型长度的整数倍
  • 结构体整体需要对齐,目标对齐长度的整数倍

2. MCU相关

2.1 MCU的启动过程描述

参考STM32的启动过程 — startup_xxxx.s文件解析(MDK和GCC双环境)


2.2 MCU的内存布局

在这里插入图片描述
在这里插入图片描述

参考:

  1. 内存布局:深度剖析应用程序中的内存布局
  2. stm32的内存分布

2.3 使用volatile关键字的作用?

  1. 硬件寄存器操作
    单片机通常与硬件设备交互,硬件寄存器的值可能会在硬件事件的触发下发生改变。通过将访问硬件寄存器的变量声明为 volatile,可以告诉编译器不要对该变量进行优化,以确保每次访问都能获取到最新的寄存器值。
  2. 共享变量
    在多线程或中断处理程序中,多个执行路径可能同时访问和修改同一个变量。将这样的共享变量声明为 volatile,可以确保编译器生成的代码正确地处理变量的读和写,避免出现竞态条件等问题。
  3. 中断服务程序
    中断服务程序可能会修改一些全局变量,而这些变量在主程序中也会被访问。将这些变量声明为 volatile,可以保证中断服务程序对变量的修改能及时反映到主程序中。
  4. 实时性要求高的代码
    在一些对实时性要求较高的场景中,使用 volatile 可以确保关键变量的访问不会被编译器优化掉,从而保证代码的实时性。

通过使用 volatile,可以帮助编译器生成更准确的代码,避免一些由于变量的不确定性导致的问题。然而,具体的应用场景和使用方法可能会因单片机的类型、编译器的特性以及项目的需求而有所不同。在实际编程中,还需要根据具体情况进行适当的测试和调试。


3. 汽车电子软件

3.1 CAN/CANFD相关

3.1 概述一个CAN消息如何被发送和接收的

TBD.


3.2 CAN和FIFO CAN

TBD.


3.3 CANFD的知识点

TBD.


3.2 概述bootloader实现要点

3.2.1 跳转前要做什么?

  • 禁止所有外设时钟;
  • 禁止使用的 PLL;
  • 禁止所有中断;
  • 清除所有中断挂起标志。

3.3 简述ASPICE在项目研发中的应用

TBD.


3.4 举例说明某个功能安全需求的实现过程

TBD.


3.5 概述14229协议

TBD.


3.6 概述15765协议

TBD.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1792739.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

三相五柱变压器饱和Simulink仿真

此示例说明了三相五柱变压器的饱和情况&#xff0c;并将专用电力系统模型与基于 Simscape 的物理模型进行了比较。 顶部电路使用 SimPowerSytems 模块来实现连接到 315 kV 电网的 300 MVA、315 kV/120 kV/25 kV、Yg/Yg/D 变压器。在不平衡电压条件下&#xff0c;与三柱变压器相…

opencv进阶 ——(十一)基于RMBG实现生活照生成寸照

实现步骤 1、检测人脸&#xff0c;可以使用opencv自带的级联分类器或者dlib实现人脸检测 2、放大人脸范围&#xff0c;调整到正常寸照尺寸 3、基于RMGB算法得到人像掩码 4、生成尺寸相同的纯色背景与当前人像进行ALPHA融合即可 alpha融合实现 void alphaBlend(cv::Mat&…

Python SMTP配置示例中如何处理发送失败?

Python SMTP的加密方式怎么设置&#xff1f;如何设置SMTP服务器&#xff1f; 在Python中&#xff0c;SMTP常被用于发送电子邮件。然而&#xff0c;SMTP配置和使用过程中&#xff0c;难免会遇到发送失败的情况。Aok将探讨在Python SMTP配置示例中如何处理这些发送失败的情况&am…

Redis实战篇——搭建主从复制

Redis实战篇——搭建主从复制 1.Redis主从1.1.主从集群结构1.2.搭建主从集群1.2.1.启动多个Redis实例1.2.2.建立集群1.2.3.测试 1.Redis主从 单节点Redis的并发能力是有上限的&#xff0c;要进一步提高Redis的并发能力&#xff0c;就需要搭建主从集群&#xff0c;实现读写分离…

探索 LLM 预训练的挑战,GPU 集群架构实战

万卡 GPU 集群实战&#xff1a;探索 LLM 预训练的挑战 一、背景 在过往的文章中&#xff0c;我们详细阐述了LLM预训练的数据集、清洗流程、索引格式&#xff0c;以及微调、推理和RAG技术&#xff0c;并介绍了GPU及万卡集群的构建。然而&#xff0c;LLM预训练的具体细节尚待进一…

安卓Zygote进程详解

目录 一、概述二、Zygote如何被启动的&#xff1f;2.1 init.zygote64_32.rc2.2 Zygote进程在什么时候会被重启2.3 Zygote 启动后做了什么2.4 Zygote启动相关主要函数 三、Zygote进程启动源码分析3.1 Nativate-C世界的Zygote启动要代码调用流程3.1.1 [app_main.cpp] main()3.1.2…

0605-JavaSE-单例模式-饿懒汉模式

​​​​​​​ 不能放在方法里面&#xff08;因为每个线程调用都会在方法里面实例化一个locker对象&#xff0c;但不属于同一个对象&#xff09;&#xff0c;然后要用static修饰成静态变量才会起到效果 //单例设计模式 //饿汉模式&#xff1a;在加载类的时候就已经开始创建 /…

Python3数据类型(新)详细介绍

文章目录 数据类型类型查看同时多个变量赋值标准数据类型1.数字(Number)2.字符串3.bool(布尔类型)4.元组元组的运算 运算运算* 运算元组的删除 5.列表(List)查找列表修改列表列表的切片列表是可以修改的列表的追加列表的插入列表的连接列表的删除列表的清空列表的复制 6.字典查…

创建常规DLL的动态链接库

本文仅供学习交流&#xff0c;严禁用于商业用途&#xff0c;如本文涉及侵权请及时联系本人将于及时删除 【例9.3】创建一个MFC 常规DLL的动态链接库Areadll&#xff0c;在该动态链接库中添加一个导出类CArea&#xff0c;通过该类获取正方形和圆的面积。 (1) 使用“MFC动态链接…

水泵选型指南

在现代暖通空调&#xff08;HVAC&#xff09;系统中&#xff0c;冷冻水泵是关键组件之一&#xff0c;它在提供冷却和空调效果方面起着至关重要的作用。选择合适的冷冻水泵不仅可以提高系统效率&#xff0c;还能节省能源和维护成本。本文将介绍冷冻水泵选型的关键因素和步骤。 …

深度学习之非极大值抑制NMS介绍

1. 基本介绍 非极大值抑制&#xff08;Non-Maximum Suppression&#xff0c;NMS&#xff09;是深度学习中一种常用的目标检测算法&#xff0c;用于在检测结果中去除冗余的边界框。 在目标检测任务中&#xff0c;通常会使用候选框&#xff08;bounding boxes&#xff09;来表示可…

wincc7.5在现有Report中增加页面

已有报告&#xff0c;页面是3页&#xff0c;需要新加1页数据。 在布局文件中&#xff0c;看到有3个项&#xff0c;这个版本的Wincc就是这种布置&#xff1a; 需要在报告内容中加入新内容&#xff0c;分页依据内容上下间的缝隙&#xff08;gap &#xff09;。 这里插入嵌入la…

SwiftUI中Menu和ControlGroup的使用

本篇文章主要介绍一下Menu组件和ControlGroup组件的使用。Menu组件是在iOS 14&#xff08;tvOS 17&#xff09;推出的一个组件&#xff0c;点击后提供一个可选择的操作列表。ControlGroup组件是一个容器视图&#xff0c;以视觉上适当的方式为给定的上下文显示语义相关的控件&am…

【设计模式深度剖析】【3】【行为型】【职责链模式】| 以购物中心客户服务流程为例加深理解

&#x1f448;️上一篇:命令模式 设计模式-专栏&#x1f448;️ 文章目录 职责链模式定义英文原话直译如何理解呢&#xff1f; 职责链模式的角色1. Handler&#xff08;抽象处理者&#xff09;2. ConcreteHandler&#xff08;具体处理者&#xff09;3. Client&#xff08;客户…

Go微服务: 基于rocketmq:server和rocketmq:broker搭建RocketMQ环境,以及生产消息和延迟消费消息的实现

RocketMQ 的搭建 1 ) 配置 docker-compose.yaml 文件 version: 3.5 services:rmqnamesrv:image: foxiswho/rocketmq:servercontainer_name: rmqnamesrvports:- 9876:9876volumes:- ./logs:/opt/logs- ./store:/opt/storenetworks:rmq:aliases:- rmqnamesrvrmqbroker:image: fo…

使用System-Verilog实现FPGA基于DE2-115开发板驱动HC_SR04超声波测距模块|集成蜂鸣器,led和vga提示功能

文章目录 前言一、SystemVerilog——下一代硬件设计语言1.1 语言基础2.2 面向对象编程1.3 接口&#xff08;Interfaces&#xff09;1.4 程序包&#xff08;Packages&#xff09;1.5 数据结构1.6 随机化&#xff08;Randomization&#xff09;1.7 并发性和时序控制1.8 功能增强1…

小程序丨最大填表限制如何开启?

老师在新建填表时&#xff0c;希望设置最大数量限制&#xff0c;若填表达到限制&#xff0c;后续的学生将不能继续提交填表。 通过开启【表格最大限制】功能即可实现&#xff0c;下面就来教大家如何制作吧。 &#x1f50e;如何开启表格最大限制功能&#xff1f; 按照常规流程…

Mac OS 用户开启 8080 端口

开启端口 sudo vim /etc/pf.conf # 开放对应端口 pass out proto tcp from any to any port 8080 # 刷新配置文件 sudo pfctl -f /etc/pf.conf sudo pfctl -e获取本机ip地址 ifconfig en0 | grep inet | grep -v inet6 | awk {print $2}访问指定端口

【UML用户指南】-03-UML的14种图

目录 1、结构图 1、类图&#xff08;class diagram&#xff09; 2、对象图&#xff08;object diagram&#xff09; 3、构件图 &#xff08;component diagram&#xff09; 4、组合结构图 5、包图&#xff08;package diagram&#xff09; 6、部署图&#xff08;deploym…

【LeetCode算法】第100题:相同的树

目录 一、题目描述 二、初次解答 三、官方解法 四、总结 一、题目描述 二、初次解答 1. 思路&#xff1a;二叉树的先序遍历。采用递归的先序遍历方法&#xff0c;首先访问根节点若不同则返回false&#xff0c;其次访问左子树和右子树。在访问左右子树时&#xff0c;需要注意…