0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

嵌入式C开发关键字的应用技巧

硬件攻城狮 来源:嵌入式系统 作者:嵌入式系统 2022-12-12 14:51 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

摘要:嵌入式C开发关键字的应用技巧

1、volatile

volatile修饰表示变量是易变的,编译器中的优化器在用到这个变量时必须每次都小心地从内存中重新读取这个变量的值,而不是使用保存在寄存器里的备份,有效的防止编译器自动优化,从而与软件设计相符合。

中断服务与主程序共享变量:

//volatileuint8_tflag=1;
uint8_tflag=1;

voidtest(void)
{
while(flag)
{
//dosomething
}
}

//interruptserviceroutine
voidisr_test(void)
{
flag=0;
}

如果没使用volatile定义flag,可能在优化后test陷入死循环,因为test里使用的flag并没修改它,开启优化后,编译器可能会固定从某个内存取值。例如:

for(inti=0;i<100000;i++);
//对比
for(volatileinti=0;i<100000;i++);

前者可能被优化掉,虽然编码本意是需要执行操作延时,但编译器认为代码无意义。

总的来说,volatile是告知编译器,不管代码如何,必须保留,而且使用时需要重新从内存读取更新,不能使用先前读取的缓存,一般在驱动代码中使用较多。

2、const

const是恒定不变的意思,其修饰的各种数据类似只读效果。

1、 修饰变量

采用const修饰变量,即变量声明为只读,保护变量值以防被修改。例如

constinti=1;

上面这个例子表明,变量i具有只读特性,不能够被更改;若想对i重新赋值,如i = 10;属于错误操作。

特别说明,定义变量的同时进行初始化,写成int const i=1,是正确的。

2、 修饰数组

C语言中const还可以修饰数组,举例如下:

constintarray[5]={1,2,3,4,5};
array[0]=array[0]+1;//错误,array是只读的,禁止修改

数组元素与变量类似,具有只读属性,不能被更改;一旦更改,编译时就会报错。

使用大数组存储固定的信息,例如查表(表驱动法的键值表),可以使用const节省ram。编译器并不给普通const只读变量分配空间,而是将它们保存到符号表中,无需读写内存操作,程序执行效率也会提高。

3、 修饰指针

C语言中const修饰指针要特别注意,共有两种形式,一种是用来限定指向空间的值不能修改;另一种是限定指针不可更改。举例如下:

inti=1;
intj=2;

constint*p1=&i;
int*constp2=&j;

上面定义了两个指针p1和p2,区别是const后面是指针本身还是指向的内容。

在定义1中const限定的是* p1,即其指向空间的值不可改变,若改变其指向空间的值如* p1=10,则程序会报错;但p1的值是可以改变的,对p1重新赋值如p1=&k是没有任何问题的。

在定义2中const限定的是指针p2,若改变p2的值如p2=&k,程序将会报错;但* p2,即其所指向空间的值可以改变,如* p2=20是没有问题的,程序正常执行。

4、 修饰函数参数

const关键字修饰函数参数,对参数起限定作用,防止其在函数内部被修改。所限定的函数参数可以是普通变量,也可以是指针变量。例如:

voidfun(constinti)
{
……
i++;//对i的值进行了修改,程序报错
}

常用的函数如strlen

size_tstrlen(constchar*string);

const在库函数中使用非常普遍,是一种自我保护的安全编码思维。

3、struct与union

对于struct 结构体和union共联体在嵌入式领域是使用得非常频繁的,一些可编程芯片提供的寄存器库都是采用结构体和共联体结合的方式来提供给软件人员进行开发,同时在平时的编码过程中这两个数据类型的灵活应用也能够实现代码更好的封装与简化。

如下面的简单示例,就可以非常灵活的访问Val中的bit位。

typedefunion
{
BYTEVal;
struct__packed
{
BYTEb0:1;
BYTEb1:1;
BYTEb2:1;
BYTEb3:1;
BYTEb4:1;
BYTEb5:1;
BYTEb6:1;
BYTEb7:1;
}bits;
}BYTE_VAL,BYTE_BITS;

其中:1表示按位操作。不只是位-字节可以,单字节与多字节也可以简化拼接。

#include"stdio.h"

typedefstruct
{
union
{
struct
{
unsignedcharlow;
unsignedcharhigh;
};
unsignedshortresult;
};
}test_t;


intmain(intargc,char*argv[])
{
test_thello;

hello.high=0x12;
hello.low=0x34;

printf("result=%04X
",hello.result);//输出result=1234

return0;
}

运行输出 result=1234 (win7系统下QT开发环境),原本需要 (high<<8)|low 运算,可以简化为共用体类型自动完成,但必须注意平台的字节顺序,属于大端还是小端模式。

在应用层面,如果明确某个数据可能存在两种可能,而且两种结果不会同时存在,也可以使用结构体与共用体组合的方式,确保模块对外接口统一。

例如移动通信模块,使用数据结构保存其基站信息,因为制式不同,模块可能工作在2G-GSM,也可能在4G-Cat1,为保证上层读取基站信息接口唯一,使用共用体就非常合适,否则需定义两套接口。如果觉得文章可以,可关注微信公众号【嵌入式系统】获取更多信息。

4、预定义标识符

一般编译器都支持预定义标识符,这些标识符结合printf等打印信息帮助程序员调试程序是非常有用的,一般编译器会自动根据用户指定完成替换和处理。

部分标识:

__FILE__//表示编译的源文件名
__LINE__//表示当前文件的行号
__FUNCTION__//表示函数名
__DATE__//表示编译日期
__TIME__//表示编译时间

使用范例:

printf("file:%s,line:%d,date:%s,time:%s",__FILE__,__LINE__,__DATE__,__TIME__);

这些比较常见,主要用于日志分析、版本记录,便于调试。

5、#与##

#:是一种运算符,用于带参宏的文本替换,将跟在后面的参数转成一个字符串常量。

##:是一种运算符,是将两个运算对象连接在一起,也只能出现在带参宏定义的文本替换中。

#include"stdio.h"

#defineTO_STR(s)#s
#defineCOMB(str1,str2)str1##str2

intmain(intargc,char*argv[])
{
intUART0=115200;

printf("UART0=%d
",COMB(UART,0));//字符串合并为变量UART0
printf("%s
",TO_STR(3.14));//将数字变成字符串

return0;
}

6、void 与 void*

void表示的是无类型,不能声明变量或常量,但是可以把指针定义为void类型,如void* ptr。void* 指针可以指向任意类型的数据,在C语言指针操作中,任意类型的数据地址都可转为void* 指针。因为指针本质上都是unsigned int。

常用的内存块操作库函数:

void*memcpy(void*dest,constvoid*src,size_tlen);
void*memset(void*buffer,intc,size_tnum);

数据指针为void* 类型,对传入任意类型数据的指针都可以操作。另外其中memcpy第二个参数,const现在也如前文所述,拷贝时对传入的原数据内容禁止修改。

特殊说明,指针是不能使用sizeof求内容大小的,在ARM系统固定为int 4字节。对于函数无输入参数的,也尽量加上void,如

voidfun(void);

7、weak

一般简化定义

#define_WEAK__attribute__((weak))

函数名称前面加上__WEAK属性修饰符称为“弱函数”,类似C++的虚函数。链接时优先链接为非weak定义的函数,如果找不到则再链接带weak函数。

_WEAKvoidfun(void)
{
//dothis
}

//不在同一个.c,两同名函数不能在同一个文件
voidfun(void)
{
//dothat
}

这种自动选择的机制,在代码移植和多模块配合工作的场景下应用较多。例如前期移植代码,需要调用某个接口fun,但当前该接口不存在或者未移植完整使用,可以使用weak关键字定义为空函数先保证编译正常。后续移植完成实现了fun,即软件中有2个fun函数没有任何错误,编译器自动会识别使用后者。当然也粗暴的#if 0屏蔽对fun的调用,但要确保后续记得放开。

8、总结

存在即合理,C语言里面的关键字,每个都有其特殊的意义,只是一般使用较少,譬如作文,使用常用的汉字可以;但引经据典,使用特殊的修饰辞藻更能显出水平。后续对section 进行详细说明,它和动态加载(OTA)、接口自启动相关。

审核编辑 :李倩

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式
    +关注

    关注

    5209

    文章

    20645

    浏览量

    336925
  • C语言
    +关注

    关注

    183

    文章

    7646

    浏览量

    146132
  • 编译器
    +关注

    关注

    1

    文章

    1672

    浏览量

    51907

原文标题:8、总结

文章出处:【微信号:mcu168,微信公众号:硬件攻城狮】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    2025嵌入式开发教程|远航哥

    https://www.bilibili.com/opus/1178756596191199237 嵌入式通信协议三剑客:串口、I2C与SPI的深度解析 在嵌入式系统开发中,通信协议是
    的头像 发表于 03-20 17:35 1325次阅读
    2025<b class='flag-5'>嵌入式开发</b>教程|远航哥

    Parasoft C/C++test:嵌入式安全关键行业的一体化软件测试解决方案

    缝融入 DevOps 的自动化测试工具。Parasoft C/C++test 正是为解决这一痛点而生,它是面向嵌入式安全关键行业的集成软件测试解决方案,凭借全面的自动化能力与深度流程适
    的头像 发表于 03-06 09:42 379次阅读

    什么是嵌入式应用开发

    概述 所谓的嵌入式应用开发就是在嵌入式操作系统下进行开发、软硬件综合开发嵌入式应用
    发表于 01-12 16:13

    C语言嵌入式系统编程注意事项-内存操作

    C语言嵌入式系统编程注意事项之内存操作 在嵌入式系统的编程中,常常要求在特定的内存单元读写内容,汇编有对应的MOV指令,而除C/C++以外的
    发表于 01-04 07:31

    关键字volatile的含意

    关键字volatile有什么含意? 一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心
    发表于 12-23 06:33

    interrupt关键字使用

    中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字__interrupt。下面的代码就使用了__interrup
    发表于 12-22 09:36

    C语言单元测试在嵌入式软件开发中的作用及专业工具的应用

    平台和操作系统上运行,无需进行大量的修改 二、C语言单元测试在嵌入式开发中的关键作用 嵌入式系统对实时性和可靠性要求极高,单元测试是确保代码质量的
    发表于 12-18 11:46

    什么是嵌入式操作系统?

    不是学会 API 调用,而是理解任务调度的逻辑,多任务通信的原理,以及,如何将实际项目拆分成合理的任务—— 这也是嵌入式开发从裸机到OS 级的关键跨越。
    发表于 12-09 10:33

    Temu跨境电商按关键字搜索Temu商品API的应用及接口请求示例

    Temu跨境电商按关键字搜索Temu商品API的应用及接口请求示例 Temu跨境电商按关键字搜索Temu商品API的应用场景 Temu跨境电商平台按关键字搜索Temu商品API的主要应用场景包括但不
    的头像 发表于 11-29 15:08 682次阅读

    哪些场合会用到volatile关键字?

    当你理解 volatile关键字的含义之后,你就能明白,其实很多场合都能用到 volatile关键字。 1.全局变量单片机开发,难免会用到全局变量。一些初级工程师,更是全局变量满天飞。这种情况下
    发表于 11-25 08:19

    C语言在嵌入式开发中的应用

    C 语言在汽车电子控制系统开发中的主导地位。 2、设备驱动程序 设备驱动程序是嵌入式系统中连接硬件和软件的桥梁,它负责实现嵌入式系统与外部设备之间的通信和控制。
    发表于 11-21 08:09

    嵌入式开发关键点介绍

    嵌入式开发关键点: 1. 硬件限制: 嵌入式系统通常具有严格的硬件限制,例如处理器速度、内存和存储容量等。因此,嵌入式开发需要考虑这些限制,并编写高效的代码以确保系统的可靠性和性
    发表于 11-13 08:12

    亚马逊平台根据关键字搜索商品API接口

    ​  在电商开发中,集成亚马逊平台的商品搜索功能是常见需求。通过亚马逊的Product Advertising API(PAAPI),开发者可以根据关键字高效检索商品信息。本技术帖将逐步介绍如何调用
    的头像 发表于 11-05 15:00 512次阅读
    亚马逊平台根据<b class='flag-5'>关键字</b>搜索商品API接口

    micro 关键字搜索全覆盖商品,并通过 API 接口提供实时数据

    micro 关键字搜索全覆盖商品”并通过 API 接口提供实时数据
    的头像 发表于 07-13 10:13 990次阅读

    嵌入式开发入门指南:从零开始学习嵌入式

    特定功能的计算机系统,广泛应用于智能家居、工业控制、医疗设备、车载系统等领域。 2. 学习嵌入式开发的前置知识熟悉C语言编程掌握基本的数据结构与算法了解数字电路与微控制器原理熟悉Linux操作系统
    发表于 05-15 09:29