
CC选择题练习1. 程序运行结果为#includestdio.hintfun(inta){intb0;staticintc3;b;c;return(abc);}intmain(){inti0;for(;i3;i){printf(%d,fun(2));}return0;}上述程序运行结果为:789, 注意这里的static2. 关于FreeRTOS关于FreeRTOS消息队列的描述, 不正确的是©A、FreeRTOS的消息队列支持FIFO和LIFO两种数居存取方式B、FreeRTOS消息队列最大传递数据为一个32位值C、FreeRTOS消息队列的底层是通过memcpy实现的D、消息队列本质上传递的是消息的地址解释A、默认的写入函数xQueueSend()和xQueueSendToBack()实现的是FIFO先进先出插在队尾而xQueueSendToFront()实现的是LIFO后进先出插在队头常用于发送紧急消息。B、FreeRTOS队列的数据项大小Item Size是在创建时由开发者随意定义的没有任何最大32位的强制限制。你可以传 1 个字节的 char也可以传 100 个字节的自定义大 struct。任务通知确实只能传递一个 32 位的整型值。C、当调用发送/接收函数时FreeRTOS底层会调用prvCopyDataToQueue()或类似函数其核心实现就是使用 C 标准库的memcpy()或者针对特定架构优化的字节拷贝机制将你的数据一字节一字节地复制到队列的内部环形缓冲区中。D、FreeRTOS的消息队列本质上是“值传递”Copy by Value。它会把你提供的整个数据包完整地“拷贝”进队列内存里而不是仅仅记下这个数据的地址。老牌的UCOS-II / UCOS-III消息队列才是纯粹的“传递地址”引用传递。这也是面试中最常问的RTOS差异题。FreeRTOS 消息队列核心知识点数据存储与拷贝机制值传递由于是“值传递”这意味着一旦你通过 xQueueSend 把局部变量放进队列你就可以立刻在原任务中修改或销毁这个变量因为队列里已经保存了一份硬拷贝。优点极致安全。发送方和接收方的内存完全解耦不用担心局部变量被销毁导致野指针。缺点如果传递的是几十 KB 的图像数据memcpy 会消耗大量的 CPU 时间和 RAM。实战解法当遇到大数据时我们会把队列的 Item Size 设置为 4 字节即一个指针的大小。此时我们向队列传递一个内存地址FreeRTOS 底层仍然是在做“值拷贝”只不过它拷贝的是这 4 字节的指针值。阻塞与唤醒机制Block Time这是 RTOS 队列区别于裸机“全局数组 环形缓冲”的根本所在。当任务去读一个空队列时它可以选择进入阻塞态死等或者等一段超时时间此时不消耗任何 CPU 资源CPU 会去执行其他就绪任务。一旦有其他任务或中断向这个队列写了数据RTOS 调度器会瞬间唤醒等待的接收任务实现完美的并发协作。中断安全FromISR API在 FreeRTOS 中普通的 API 绝对不能在中断服务函数ISR中调用。如果你需要在串口接收中断、定时器中断里发送消息必须使用带 FromISR 后缀的函数例如 xQueueSendFromISR()。这类函数内部不包含任何可能引起当前任务上下文切换的阻塞代码保证了中断执行的实时性和安全性。3. 程序运行结果为#includeiostreamusingnamespacestd;intmain(){chara101;intsum200;a27;/* a -128 */suma;printf(%d\n,sum);return0;}上述程序运行结果为:72分析注意char的取值范围;4. SDIO总线在嵌入式软件开发中SDIO总线主要是为SDIO卡提供一个高速的I/O能力。SDIO总线能够支持的设备有很多比如bluetooth wifiGPScamera sensor等。关于SDIO总线设备描述错误的是 ()A、SDIO的每次操作都是由HOST在CMD线上发起一个CMD对于有的CMDDEVICE需要返回ReSponSe有的则不需要B、SDIO的信号传输模式有SPI、1-bit、4-bit三种C、SDIO总线采用HOST-DEVICE设计所有的通信都是由HOST端发出命令开始的D、在SDIO总线定义中DAT1信号线用来传输数据解析A、SDIO 协议规定所有的总线操作都必须由 Host 发送 Command (CMD) 发起。绝大多数命令如 CMD52、CMD53 读写命令都需要设备返回 Response如 R5 响应来确认。但也有部分广播命令例如让所有卡复位的 CMD0 GO_IDLE_STATE是不需要设备回复 Response 的。B、SDIO 规范明确支持这三种物理总线模式。上电复位时默认处于 1-bit 模式或 SPI 模式Host 可以通过发送特定命令如修改 CCCR 寄存器将其切换到带宽更高的 4-bit 模式。C、SD/SDIO 采用的是严格的“主从架构Master-Slave”。Device 端是个“纯受体”它绝对不能主动往数据线上塞数据。即便是 Device 需要通过 DAT1 拉低来触发中断真正的“数据通信读取中断状态或获取数据”依然必须由 Host 收到中断后主动下发 CMD52 或 CMD53 命令才能开始。D、在 SDIOSecure Digital Input Output协议的定义中DAT1 信号线最核心且特殊的使命是作为“中断信号线IRQ”而不是单纯用来传输数据。在 SPI 模式 和 1-bit SD 模式 下数据传输完全由 DAT0或 MISO/MOSI完成DAT1 专门且唯一地用作设备向 Host 发送中断请求IRQ的引脚完全不传输数据。只有在 4-bit SD 模式 下DAT1 才会被时分复用Time-multiplexed在数据传输周期内它作为数据线 Data 1 使用而在非数据传输周期它依然要承担 IRQ 中断线的职责。由于 SDIO 设备如 WiFi、蓝牙属于 I/O 外设强依赖中断机制来主动通知主机比如“接收到了网络数据包”因此把 DAT1 描述为“用来传输数据”忽略了 SDIO 协议最关键的特征5. 对于一个示波器下列哪一项不属于其三大重要指标A、采样率B、带宽C、通道数D、存储深度带宽 (Bandwidth - 决定“能不能测”)含义 示波器前端模拟放大器幅频特性曲线下降到 -3dB即真实信号幅度的 70.7%时的频率点。重要性 带宽决定了示波器能测量多高频率的信号而不失真。如果信号频率超过了示波器带宽信号的幅度和上升沿时间就会被严重衰减和改变。这是示波器最基本、也是最昂贵的指标。采样率 (Sample Rate - 决定“测得准不准”)含义 内部的 ADC模数转换器每秒对模拟信号进行采样的次数单位通常是 Sa/sSample/second。重要性 根据奈奎斯特采样定理采样率至少要是信号最高频率的 2 倍工程上通常要求是带宽的 3~5 倍甚至更高。采样率不够会导致“波形混叠”和细节丢失你看到的波形可能根本不是真实的波形。存储深度 (Memory Depth - 决定“能看多久的细节”)含义 示波器在一次触发采集中能够存储的采样点总数单位通常是 ptspoints。重要性 存储深度、采样率和波形记录时间之间有一个经典公式存储深度 采样率 × 记录时间。如果你想长时间记录一段波形记录时间大又不想丢失高频细节采样率大你就必须要有极大的存储深度。6. FreeRTOS在我们使用FreeRTOS任务通知功能时我们可以使用函数xTaskNotify()来发送任务通知该函数需要指定任务通知更新的方法。在FreeRTOS中任务通知更新的方法不包括A、增加接收任务的通知值B、覆盖接收任务的通知值C、不覆盖接收任务的通知值D、减少接收任务的通知值解析在 FreeRTOS 中xTaskNotify() 函数的第四个参数 eAction 决定了如何更新目标任务的通知值。该参数支持的枚举操作包括eIncrement目标任务的通知值增加 1对应选项 A。eSetValueWithOverwrite无条件覆盖目标任务的通知值对应选项 B。eSetValueWithoutOverwrite如果目标任务当前没有挂起的通知则设置其通知值如果已有挂起的通知则不更新对应选项 C。eSetBits将目标任务的通知值与指定值进行按位“或”操作。eNoAction仅发送事件不更新通知值。FreeRTOS 的任务通知机制不包含直接“减少接收任务的通知值”的操作方法。因此选项 D 是不包括的。7. SPISPI作为全双工同步串行接口标准在串行通信时双方需采用连线进行通信A、三根B、两根C、一根D、四根8. 指针对于下面的C语言声明描述正确的一项是char (*p)[16]A、p是指向长度为16的字符数组的指针B、p是包含16个字符的字符串C、p是长度为16的字符指针数组D、p是长度为16的字符数组在 C 语言的声明中运算符是有优先级的。特别是 []数组的优先级高于 *指针。因为有圆括号 () 的存在强制改变了优先级。* 先和 p 结合即 (*p)。这明确告诉编译器“p 是一个指针”。指针指向什么呢往右看是 [16]。说明它指向一个长度为 16 的数组。这个数组里装的是什么类型的数据往左看是 char。说明它指向的是一个字符数组。p 是一个指针它指向一个长度为 16 的字符数组通常被称为“数组指针”。去掉圆括号即可写成 char *p[16]因为没有圆括号p 首先和优先级更高的 [16] 结合说明 p 是一个数组然后再和 * 结合说明数组里的元素是指针最后加上 char说明是指向字符的指针。这就是所谓的“指针数组”即装满指针的数组。9. 白盒测试白盒测试技术是软件工程中非常常见的测试技术。白盒测试技术中逻辑覆盖是对一系列测试技术的总称。下列选项中按照逻辑覆盖标准从到强排序正确的是A、条件组合覆盖路径覆盖判定覆盖B、判定覆盖语包覆盖条件覆盖C、语句覆盖条件覆盖判定覆盖D、判定覆盖条件覆盖路径覆盖解析正确答案是 D、判定覆盖条件覆盖路径覆盖。详细解析在白盒测试中逻辑覆盖标准按照测试的严格程度从弱到强通常的排序为语句覆盖 判定覆盖分支覆盖 条件覆盖 判定/条件覆盖 条件组合覆盖 路径覆盖。我们来逐一分析各个选项A选项条件组合覆盖强于判定覆盖但路径覆盖是比条件组合覆盖更强的标准因此排序错误。B选项条件覆盖强于判定覆盖选项中“判定覆盖条件覆盖”的顺序是从弱到强但“语包覆盖”应为语句覆盖是最弱的放在中间排序错误。C选项条件覆盖强于判定覆盖选项中“条件覆盖判定覆盖”的顺序颠倒了应为“判定覆盖条件覆盖”。D选项判定覆盖 条件覆盖 路径覆盖完全符合逻辑覆盖标准从弱到强的递增顺序因此是正确选项。10. uC/OS-III下列关于uC/OS-I中的互信号量说法不正确的是A、互斥信号量是一个内置优先级的特殊类型信号量B、互斥信号量用于资源管理C、互斥信号量不支持套使用D、任务可申请同一个互斥信号量多达250次解析A选项说法正确在 uC/OS-II 中互斥信号量Mutex是一种特殊的事件控制块。在创建互斥信号量时必须指定一个“优先级继承优先级PIP”这个 PIP 的优先级必须高于所有可能使用该互斥信号量的任务优先级。因此它是一个内置优先级的特殊类型信号量。B选项说法正确互斥信号量Mutex的核心设计目的就是用于保护共享资源实现临界区的互斥访问从而防止多个任务同时修改同一资源导致数据不一致。C选项说法错误uC/OS-III 明确支持互斥信号量的嵌套使用嵌套持有。如果一个任务持有了某个互斥锁它可以再次申请同一个互斥锁系统通过内部计数器来记录嵌套次数。因此“不支持套使用”的说法是不正确的。D选项说法正确根据 uC/OS-III 的官方文档系统允许用户嵌套持有互斥锁一个任务最多可以持有同一个互斥锁多达 250 次并且持有者必须释放相应次数才能完全释放该锁。11. 程序运行结果为#includeiostreamusingnamespacestd;intfunc(){inti,j,k0;/* 注意这里写的是单等号, 而不是双等号 *//* 在c中, 赋值表达式的值就是被赋的那个值 */for(i0,j-1;j0;i,j){/* 循环体从未被执行 */k;}returnk;}intmain(){cout(func());return0;}上述程序运行结果为:0#includeiostreamintmain(){inta2;std::cout(a0)std::endl;std::coutastd::endl;return0;}12. 低功耗低功耗需要软件和硬件的系统优化设计来实现。从软件对于系统的低功耗设计来说下列说法中错误的是A、对于包含有无线功能的芯片配置合理的待机参数以降低功耗B、降低CPU运行频率会导致程序运行时间延迟过长处理速度有问题。所以任何时候都不能降低CPU频率C、关注每一个GPIO口电平状态在进入睡眠之前配置所有的GPIO口到高电平或者低电平以降低漏电流D、合理的使用MCU的待机模式在当前没有任务需要处理时将MCU进入到低功耗的睡眠模式解析A真相无线射频如 Wi-Fi、蓝牙、4G/5G是系统里的“耗电大户”。具体做法软件工程师通常会通过配置无线芯片的休眠周期如 Wi-Fi 的 DTIM 间隔、蓝牙的广播间隔 / 嗅探周期让射频模块在不需要收发数据时进入极低功耗的休眠状态只在特定时间窗口醒来以此大幅降低平均功耗。B真相降低 CPU 运行频率不仅是可以的而且是软件低功耗设计中最核心的技术之一即 动态电压频率调节DVFS, Dynamic Voltage and Frequency Scaling。底层逻辑在数字电路中芯片的动态功耗公式约为P ∝ C ⋅ V 2 ⋅ f P \propto C \cdot V^2 \cdot fP∝C⋅V2⋅fP PP为功耗C CC为电容V VV为电压f ff为频率。可以看出功耗与频率成正比。实际应用在系统任务繁重时我们让 CPU 全速运行但在系统空闲或仅处理简单任务如仅维持心跳包、简单传感器采样时完全可以主动降低 CPU 频率。虽然这会让该段代码的执行时间变长但在对实时性要求不高的场景下降频配合降压能极大地节省整体电量。因此“任何时候都不能降低CPU频率”是非常极端的错误说法。C真相这是嵌入式软件工程师在做深度休眠时必做的“基本功”。底层逻辑如果 MCU 睡眠时 GPIO 引脚处于“悬空浮空”状态外部的电磁干扰会导致引脚电压在逻辑高和逻辑低之间频繁波动。这会使芯片内部的 CMOS 输入缓冲器处于半导通状态产生持续的内部穿通电流漏电流这可能会吃掉几十到几百微安的电量导致低功耗彻底破功。因此休眠前必须将不用的 GPIO 配置为确定的状态如模拟输入、内部上拉或下拉。D真相这是 RTOS实时操作系统或裸机程序实现低功耗的基础框架。具体做法通常在系统的 Idle空闲任务中加入进入睡眠的代码。当所有高优先级业务都处理完毕时系统自动切入休眠模式如 ARM Cortex-M 系列的 Sleep、Deep Sleep 或 Standby 模式关闭 CPU 时钟和不必要的外设等待下一次中断如定时器、按键唤醒再恢复运行。13. volatile关于C语言中volatile关键字下面的说法哪一个是错误的A、用volatile修饰的变量读取速度会更快B、编译器会禁止对volatile修饰的变量进行读写优化C、每一次读取volatile修饰的变量都会从内存中读取14. 数组数组a的定义语句为float a[3][4];下列是对数组元素不正确的引用方法A、((ai)j)B、(a[i]j)C、a[i][j]D、(ai*4j)解析对于定义 float a[3][4];a 是一个二维数组包含 3 行每行有 4 个 float 元素。在涉及指针运算时a 会退化为指向一维数组的指针即行指针其类型为 float (*)[4]。因为 a 的类型是 float (*)[4]指向含有 4 个浮点数数组的指针所以当对 a 进行加减运算时步长是“一整行”的长度即 4 个 float 的字节数。a 1 会让指针向后移动 1 行4 个元素。a i4 j 会让指针向后跨越极其遥远的内存跨越了 i4 j 行这会直接导致数组越界。返回值类型错误即使不考虑越界a … 的结果依然是行指针对其解引用(a …) 得到的是一个一维数组名退化为列指针 float而不是具体的 float 数值。15.能把函数处理结果的二个数据返回给主调函数在下面的方法中不正确的是A、形参用二个指针B、形参用数组C、retuen这二个数D、用二个全局变量解析在 C/C 语言中一个函数只能有一个返回值。机制剖析你不能使用 return 语句直接返回两个独立的数据。如果你强行写成 return a, b;编译器不仅不会同时返回两个数还会因为“逗号表达式”的特性先计算 a然后丢弃 a 的结果最终只返回 b 的值。16.在传感器设计的时候下面的描述哪个是错误的A、使用噪声系数尽量小B、功耗尽量低C、小信号尽量多级放大D、可靠性尽量好解析放大级数绝对不是越多越好。对于微弱的传感器信号正确的设计原则是在保证稳定性的前提下尽量通过单级或少量的级数如高质量的低噪声前置放大器实现所需增益。噪声与漂移的累加每一级放大器本身都会产生内部噪声。如果是直流DC放大多级放大还会导致严重的零点漂移前一级的微小漂移会被后一级成倍放大最终淹没真实信号。带宽缩减放大器的级数越多整个系统的综合频带宽度就会越窄导致高频信号失真或丢失。自激振荡系统失稳每一级放大电路都会带来一定的相位延迟。级数过多时分布电容和杂散电感极易引入正反馈导致电路产生强烈的自激振荡使系统完全瘫痪。传感器的输出通常是微伏μ V \mu VμV或毫伏m V mVmV级别的极小信号。如果电路本身的噪声系数过大真实的物理信号就会被掩盖在电子噪声如热噪声、散粒噪声中。因此采用低噪声放大器LNA和低噪声元件是设计的核心诉求。现代传感器大量应用于物联网IoT、可穿戴设备和工业分布式网络中这些场景通常采用电池供电。低功耗设计不仅能延长电池寿命还能减少芯片发热温度升高会导致热噪声增加和参数漂移从而间接提升传感器的测量精度。传感器通常要被部署在极端温度、高湿度、强电磁干扰或震动等恶劣的物理环境中。良好的可靠性如采用抗干扰屏蔽、宽温漂范围的元器件、冗余设计等是传感器能够长期稳定工作的前提这是所有硬件设计的第一法则。17. 程序运行结果为intmain(intargc,char**argv){/* argc代表参数个数 */intn9,i;for(i1;iargc;i)nn*10*argv[i]-0;printf(%d\n,n);return0;}经编译连接生成的可执行文件tt.exe, 若执行输入以下命令行tt 12 345 678, 则程序运行的输出结果是看不明白这是什么意思.18. 以下程序段完全正确的是A、int k, *p k; scanf(%d:, p)B、int *p; scanf(%d, p)C、int *p; scanf(%d, p)D、int k, *p; *p k; scanf(%d, p)19. Cortex-M当我们使用Cortex-M系列的产品时可能需要对中断进行配置下面对该中断的描述不正确的是A、Cortex-M系列可以通过相应的寄存器来查看当前正在运行的异常编号B、有些中断不可屏蔽C、Cortex-M系列的所有中断都可以通过编程设置优先级D、PRIMASKFAULTMASK和BASEPRI是中断屏蔽寄存器在 Cortex-M 内核中绝大多数外部中断IRQ和部分系统异常如 SysTick、PendSV确实可以通过编程来修改优先级。但是有 3 个特殊的系统异常它们的优先级是固定的绝对最高并且永远无法通过编程修改。这三个不可修改的“大佬”分别是复位 (Reset)优先级固定为 -3最高。不可屏蔽中断 (NMI)优先级固定为 -2。硬故障 (HardFault)优先级固定为 -1。(注在 Cortex-M 中优先级数值越小代表优先级越高。这三个异常的优先级被焊死成了负数以确保它们能打断任何普通的程序代码。)A、Cortex-M 内核内部有一个名为 IPSR (Interrupt Program Status Register中断程序状态寄存器) 的专用寄存器。它的最低 9 位在某些型号中更宽存储了当前正在执行的异常或中断的编号ISR_NUMBER。如果在主程序非中断态中这个值为 0。B、正如前面提到的NMI (Non-Maskable Interrupt) 顾名思义就是不可屏蔽中断。无论你如何配置中断屏蔽寄存器只要 NMI 引脚被触发内核就必须立刻响应。另外Reset 和 HardFault 也是不可屏蔽的。C、这三个是 Cortex-M特别是 M3、M4、M7 架构M0 只有 PRIMASK提供的专用中断屏蔽寄存器通常需要用汇编指令如 CPSID I或底层库函数如 __disable_irq()来操作PRIMASK最常用的“总开关”。置 1 时会屏蔽所有优先级可配置的中断只允许 NMI 和 HardFault 响应。FAULTMASK比 PRIMASK 更狠。置 1 时连 HardFault 都会被屏蔽只允许 NMI 响应优先级等效提升到 -1。BASEPRI极其好用它可以设置一个特定的优先级阈值。比如把它设置为 5那么优先级为 5~255 的中断全部被屏蔽但优先级高于 5如 0~4的高优先级中断依然可以正常响应。这在 RTOS如 FreeRTOS的临界区保护中应用非常广泛。20. FreeRTOS下列关于FreeRTOS中时间片的描述正确的是A、时间片需要通过systick来实现定时器无法实现时间片的功能B、时间片是系统固有特性程序员无法通过代码进行修改C、时间片本质上是通过定时器实现的D、SYSTICK的定时时间不可修改RTOS 调度的“心跳”被称为 Tick系统时钟节拍。为了产生这个定期的节拍底层硬件必须配置一个硬件定时器。这个定时器每隔固定的时间比如 1ms就会产生一次中断。在中断服务函数ISR中FreeRTOS 会检查当前任务的时间片是否用完如果用完且有同优先级的就绪任务就会触发任务切换。因此时间片的本质完全依赖于底层硬件定时器的周期性中断。SysTick系统滴答定时器确实是 ARM Cortex-M 内核专属的一个通用定时器FreeRTOS 在 Cortex-M 芯片上默认使用它来产生系统节拍。但是SysTick 本质上也就是一个硬件定时器。 * 反驳如果你使用的芯片没有 SysTick或者 SysTick 被其他高优先级业务占用了你完全可以自由地修改 FreeRTOS 的底层移植文件port.c使用芯片上的任何一个普通定时器比如 STM32 的 TIM2、TIM3来产生系统节拍中断。只要能定期产生中断任何定时器都能实现时间片功能。FreeRTOS 赋予了程序员极大的自由度。时间片机制是完全可控的。SysTick 的定时时间是完全可以编程修改的。反驳SysTick 内部有一个重装载寄存器Reload Register。通过向这个寄存器写入不同的数值你可以随时改变 SysTick 产生中断的频率。实际上FreeRTOS 的启动源码中如 vPortSetupTimerInterrupt 函数正是通过代码计算并配置了这个寄存器的值来设定 SysTick 的定时时间的。编程题编程题一输入数字n, 按顺序打印出从1到最大的n位十进制数, 比如输入3, 则打印输出1、2、3一直到最大的3位数999.打印从1到最大的n位数_牛客题霸_牛客网classSolution{public:/** * 代码中的类名、方法名、参数名已经指定请勿修改直接返回方法规定的值即可 * * * param n int整型 最大位数 * return int整型vector */vectorintprintNumbers(intn){vectorintans;intmax_number1;for(inti0;in;i){max_number*10;}for(inti1;imax_number;i){ans.push_back(i);}returnans;}};