嵌入式C语言的几个实用技巧

白话嵌入式 2024-12-04 17:29


在嵌入式开发中,C语言的一些小技巧可以帮我们提高开发效率,事半功倍。

C语言常常让人觉得它所能表达的东西非常有限。它不具有类似第一级函数和模式匹配这样的高级功能。但是C非常简单,并且仍然有一些非常有用的语法技巧和功能,只是没有多少人知道罢了。 

01
指定的初始化 

很多人都知道像这样来静态的初始化数组:

int fibs[] = {11235};

C99标准实际上支持一种更为直观简单的方式来初始化各种不同的集合类数据(如:结构体,联合体和数组)。 
02
数组

 我们可以指定数组的元素来进行初始化。这非常有用,特别是当我们需要根据一组#define来保持某种映射关系的同步更新时。来看看一组错误码的定义,如:

/* Entries may not correspond to actual numbers. Some entries omitted. */

#define EINVAL 1

#define ENOMEM 2

#define EFAULT 3

/* ... */

#define E2BIG 7

#define EBUSY 8

/* ... */

#define ECHILD 12

/* ... */

现在,假设我们想为每个错误码提供一个错误描述的字符串。为了确保数组保持了最新的定义,无论头文件做了任何修改或增补,我们都可以用这个数组指定的语法。

char *err_strings[] = {
[0] = "Success",
[EINVAL] = "Invalid argument",
[ENOMEM] = "Not enough memory",
[EFAULT] = "Bad address",
/* ... */
[E2BIG ] = "Argument list too long",
[EBUSY ] = "Device or resource busy",
/* ... */
[ECHILD] = "No child processes"
/* ... */
};

这样就可以静态分配足够的空间,且保证最大的索引是合法的,同时将特殊的索引初始化为指定的值,并将剩下的索引初始化为0。 
03

结构体和联合体

 用结构体与联合体的字段名称来初始化数据是非常有用的。假设我们定义:

struct point {
  int x;
  int y;
  int z;
}


然后,我们这样初始化struct point:

struct point p = {.x = 3, .y = 4, .z = 5};

当我们不想将所有字段都初始化为0时,这种法可以很容易的在编译时就生成结构体,而不需要专门调用一个初始化函数。


对联合体来说,我们可以使用相同的办法,只是我们只用初始化一个字段。 

04

宏列表

 C中的一个惯用方法,是说有一个已命名的实体列表,需要为它们中的每一个建立函数,将它们中的每一个初始化,并在不同的代码模块中扩展它们的名字。这在Mozilla的源码中经常用到,我就是在那时学到这个技巧的。例如,在我去年夏天工作的那个项目中,我们有一个针对每个命令进行标记的宏列表。其工作方式如下:
#define FLAG_LIST(_) \
_(InWorklist) \
_(EmittedAtUses) \
_(LoopInvariant) \
_(Commutative) \
_(Movable) \
_(Lowered) \
_(Guard)

它定义了一个FLAG_LIST宏,这个宏有一个参数称之为 _ ,这个参数本身是一个宏,它能够调用列表中的每个参数。举一个实际使用的例子可能更能直观地说明问题。假设我们定义了一个宏DEFINE_FLAG,比如:

#define DEFINE_FLAG(flag) flag,
enum Flag {
  None = 0,
  FLAG_LIST(DEFINE_FLAG)
  Total
};
#undef DEFINE_FLAG


对FLAG_LIST(DEFINE_FLAG)做扩展能够得到如下代码:

enum Flag {
  None = 0,
  DEFINE_FLAG(InWorklist)
  DEFINE_FLAG(EmittedAtUses)
  DEFINE_FLAG(LoopInvariant)
  DEFINE_FLAG(Commutative)
  DEFINE_FLAG(Movable)
  DEFINE_FLAG(Lowered)
  DEFINE_FLAG(Guard)
  Total
};

接着,对每个参数都扩展DEFINE_FLAG宏,这样我们就得到了enum如下:

enum Flag {
    None = 0,
    InWorklist,
    EmittedAtUses,
    LoopInvariant,
    Commutative,
    Movable,
    Lowered,
    Guard,
    Total
};

然后,我们可能要定义一些访问函数,这样才能更好地使用flag列表:

#define FLAG_ACCESSOR(flag) \
bool is##flag() const {\
    return hasFlags(1 << flag);\
}\
void set##flag() {\
    JS_ASSERT(!hasFlags(1 << flag));\
    setFlags(1 << flag);\
}\
void setNot##flag() {\
    JS_ASSERT(hasFlags(1 << flag));\
    removeFlags(1 << flag);\
}

FLAG_LIST(FLAG_ACCESSOR)
#undef FLAG_ACCESSOR

一步步的展示其过程是非常有启发性的,如果对它的使用还有不解,可以花一些时间在gcc –E上。 
05

编译时断言

 这其实是使用C语言的宏来实现的非常有“创意”的一个功能。有些时候,特别是在进行内核编程时,在编译时就能够进行条件检查的断言,而不是在运行时进行,这非常有用。不幸的是,C99标准还不支持任何编译时的断言。

但是,我们可以利用预处理来生成代码,这些代码只有在某些条件成立时才会通过编译(最好是那种不做实际功能的命令)。有各种各样不同的方式都可以做到这一点,通常都是建立一个大小为负的数组或结构体。最常用的方式如下:

/* Force a compilation error if condition is false, but also produce a result
* (of value 0 and type size_t), so it can be used e.g. in a structure
* initializer (or wherever else comma expressions aren't permitted). */

/* Linux calls these BUILD_BUG_ON_ZERO/_NULL, which is rather misleading. */
#define STATIC_ZERO_ASSERT(condition) (sizeof(struct { int:-!(condition); }) )
#define STATIC_NULL_ASSERT(condition) ((void *)STATIC_ZERO_ASSERT(condition) )
/* Force a compilation error if condition is false */
#define STATIC_ASSERT(condition) ((void)STATIC_ZERO_ASSERT(condition))

如果(condition)计算结果为一个非零值(即C中的真值),即! (condition)为零值,那么代码将能顺利地编译,并生成一个大小为零的结构体。如果(condition)结果为0(在C中为假),那么在试图生成一个负大小的结构体时,就会产生编译错误。

它的使用非常简单,如果任何某假设条件能够静态地检查,那么它就可以在编译时断言。例如,在上面提到的标志列表中,标志集合的类型为uint32_t,所以,我们可以做以下断言:
STATIC_ASSERT(Total <= 32)

它扩展为:
(void)sizeof(struct { int:-!(Total <= 32) })

现在,假设Total<=32。那么-!(Total <= 32)等于0,所以这行代码相当于:
(void)sizeof(struct { int0 })

这是一个合法的C代码。现在假设标志不止32个,那么-!(Total <= 32)等于-1,所以这时代码就相当于:
(void)sizeof(struct { int-1 } )

因为位宽为负,所以可以确定,如果标志的数量超过了我们指派的空间,那么编译将会失败。
原文:百问科技

文章来源于网络,仅用于学习传播,版权归原作者所有,如有侵权,请联系删除。nd

--- END ---

关注【白话嵌入式】,轻松学习嵌入式。



觉得文章不错,请点“分享”、“”、“在看” 吧!

白话嵌入式 简单易懂的嵌入式知识。关注我,不迷路
评论 (0)
  • 文/郭楚妤编辑/cc孙聪颖‍伴随贸易全球化的持续深入,跨境电商迎来蓬勃发展期,物流行业 “出海” 成为不可阻挡的必然趋势。加之国内快递市场渐趋饱和,存量竞争愈发激烈。在此背景下,国内头部快递企业为突破发展瓶颈,寻求新的增长曲线,纷纷将战略目光投向海外市场。2024 年,堪称中国物流企业出海进程中的关键节点,众多企业纷纷扬帆起航,开启海外拓展之旅。然而,在一片向好的行业发展表象下,部分跨境物流企业的经营状况却不容乐观。它们受困于激烈的市场竞争、不断攀升的运营成本,以及复杂的国际物流环境,陷入了微利
    华尔街科技眼 2025-04-09 15:15 111浏览
  •   卫星图像智能测绘系统全面解析   一、系统概述   卫星图像智能测绘系统是基于卫星遥感技术、图像处理算法与人工智能(AI)技术的综合应用平台,旨在实现高精度、高效率的地理空间数据获取、处理与分析。该系统通过融合多源卫星数据(如光学、雷达、高光谱等),结合AI驱动的智能算法,实现自动化、智能化的测绘流程,广泛应用于城市规划、自然资源调查、灾害监测等领域。   应用案例   目前,已有多个卫星图像智能测绘系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润卫星图像智能测绘系统
    华盛恒辉l58ll334744 2025-04-08 15:04 108浏览
  •   卫星图像智能测绘系统:地理空间数据处理的创新引擎   卫星图像智能测绘系统作为融合卫星遥感、地理信息系统(GIS)、人工智能(AI)以及大数据分析等前沿技术的综合性平台,致力于达成高精度、高效率的地理空间数据采集、处理与应用目标。借助自动化、智能化的技术路径,该系统为国土资源管理、城市规划、灾害监测、环境保护等诸多领域输送关键数据支撑。   应用案例   目前,已有多个卫星图像智能测绘系统在实际应用中取得了显著成效。例如,北京华盛恒辉北京五木恒润卫星图像智能测绘系统。这些成功案例为卫星
    华盛恒辉l58ll334744 2025-04-08 16:19 96浏览
  •   工业自动化领域电磁兼容与接地系统深度剖析   一、电磁兼容(EMC)基础认知   定义及关键意义   电磁兼容性(EMC),指的是设备或者系统在既定的电磁环境里,不但能按预期功能正常运转,而且不会对周边其他设备或系统造成难以承受的电磁干扰。在工业自动化不断发展的当下,大功率电机、变频器等设备被大量应用,现场总线、工业网络等技术也日益普及,致使工业自动化系统所处的电磁环境变得愈发复杂,电磁兼容(EMC)问题也越发严峻。   ​电磁兼容三大核心要素   屏蔽:屏蔽旨在切断电磁波的传播路
    北京华盛恒辉软件开发 2025-04-07 22:55 264浏览
  •     根据 IEC术语,瞬态过电压是指持续时间几个毫秒及以下的过高电压,通常是以高阻尼(快速衰减)形式出现,波形可以是振荡的,也可以是非振荡的。    瞬态过电压的成因和机理,IEC 60664-1给出了以下四种:    1. 自然放电,最典型的例子是雷击,感应到电力线路上,并通过电网配电系统传输,抵达用户端;        2. 电网中非特定感性负载通断。例如热处理工厂、机加工工厂对
    电子知识打边炉 2025-04-07 22:59 166浏览
  • 文/Leon编辑/侯煜‍就在小米SU7因高速交通事故、智驾性能受到质疑的时候,另一家中国领先的智驾解决方案供应商华为,低调地进行了一场重大人事变动。(详情见:雷军熬过黑夜,寄望小米SU7成为及时雨)4月4日上午,有网友发现余承东的职务发生了变化,华为官网、其个人微博认证信息为“常务董事,终端BG董事长”,不再包括“智能汽车解决方案BU董事长”。余承东的确不再兼任华为车BU董事长,但并非完全脱离华为的汽车业务,而是聚焦鸿蒙智行。据悉,华为方面寻求将车BU独立出去,但鸿蒙智行仍留在华为终端BG部门。
    华尔街科技眼 2025-04-09 15:28 101浏览
  • 在人工智能技术飞速发展的今天,语音交互正以颠覆性的方式重塑我们的生活体验。WTK6900系列语音识别芯片凭借其离线高性能、抗噪远场识别、毫秒级响应的核心优势,为智能家居领域注入全新活力。以智能风扇为起点,我们开启一场“解放双手”的科技革命,让每一缕凉风都随“声”而至。一、核心技术:精准识别,无惧环境挑战自适应降噪,听懂你的每一句话WTK6900系列芯片搭载前沿信号处理技术,通过自适应降噪算法,可智能过滤环境噪声干扰。无论是家中电视声、户外虫鸣声,还是厨房烹饪的嘈杂声,芯片均能精准提取有效指令,识
    广州唯创电子 2025-04-08 08:40 196浏览
  • HDMI从2.1版本开始采用FRL传输模式,和2.0及之前的版本不同。两者在物理层信号上有所区别,这就需要在一些2.1版本的电路设计上增加匹配电路,使得2.1版本的电路能够向下兼容2.0及之前版本。2.1版本的信号特性下面截取自2.1版本规范定义,可以看到2.1版本支持直流耦合和交流耦合,其共模电压和AVCC相关,信号摆幅在400mV-1200mV2.0及之前版本的信号特性HDMI2.0及之前版本采用TMDS信号物理层,其结构和参数如下:兼容设计根据以上规范定义,可以看出TMDS信号的共模电压范
    durid 2025-04-08 19:01 175浏览
  •   物质扩散与污染物监测系统:环境守护的关键拼图   一、物质扩散原理剖析   物质扩散,本质上是物质在浓度梯度、温度梯度或者压力梯度等驱动力的作用下,从高浓度区域向低浓度区域迁移的过程。在环境科学范畴,物质扩散作为污染物在大气、水体以及土壤中迁移的关键机制,对污染物的分布态势、浓度动态变化以及环境风险程度有着直接且重大的影响。   应用案例   目前,已有多个物质扩散与污染物监测系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润物质扩散与污染物监测系统。这些成功案例为物质
    华盛恒辉l58ll334744 2025-04-09 11:24 68浏览
  • 在万物互联时代,智能化安防需求持续升级,传统报警系统已难以满足实时性、可靠性与安全性并重的要求。WT2003H-16S低功耗语音芯片方案,以4G实时音频传输、超低功耗设计、端云加密交互为核心,重新定义智能报警设备的性能边界,为家庭、工业、公共安防等领域提供高效、稳定的安全守护。一、技术内核:五大核心突破,构建全场景安防基座1. 双模音频传输,灵活应对复杂场景实时音频流传输:内置高灵敏度MIC,支持环境音实时采集,通过4G模块直接上传至云端服务器,响应速度低至毫秒级,适用于火灾警报、紧急呼救等需即
    广州唯创电子 2025-04-08 08:59 159浏览
  • ## DL/T645-2007* 帧格式:* 帧起始字符:68H* 地址域:A0 A1 A2 A3 A4 A5* 帧起始字符:68H* 控制码:1字节* 主站:* 13H:请求读电能表通信地址* 11H:请求读电能表数据* 1CH:请求跳闸、合闸* 从站:* 91H:正常应答读电能表* 9CH:正常应答跳闸、合闸* 数据域长度:1字节* 数据域:DI0 DI1 DI2 DI3* 发送方:每字节+33H* 接收方:每字节-33H* 数据标识:* 电能量* 最大需量及发生时间* 变量* 事件记录*
    四毛打印店 2025-04-09 10:53 76浏览
  •   物质扩散与污染物监测系统软件:多领域环境守护的智能中枢   北京华盛恒辉物质扩散与污染物监测系统软件,作为一款融合了物质扩散模拟、污染物监测、数据分析以及可视化等多元功能的综合性工具,致力于为环境科学、公共安全、工业生产等诸多领域给予强有力的技术支撑。接下来,将从功能特性、应用场景、技术实现途径、未来发展趋势等多个维度对这类软件展开详尽介绍。   应用案例   目前,已有多个物质扩散与污染物监测系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润物质扩散与污染物监测系统。这
    华盛恒辉l58ll334744 2025-04-09 14:54 124浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦