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

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

3天内不再提示

嵌入式软件开发常用的关键字和运算符

jf_Vqngj70R 来源:美男子玩编程 2023-12-04 12:22 次阅读

1volatile关键字

volatile是一个特征修饰符,提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,告诉编译器对该变量不做优化,都会直接从变量内存地址中读取数据,从而可以提供对特殊地址的稳定访问。

常用场景:中断服务与主程序共享变量。示例代码如下:

//volatile uint8_t flag=1;
uint8_t flag=1;


void test(void)
{
    while(flag)
    {
        //do something
    }
}


//interrupt service routine
void isr_test(void)
{
    flag=0;
}

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

2const关键字

const 是 constant 的缩写,意思是“恒定不变的”,它是定义常变量的关键字。

通常有4种用法。

1、修饰变量

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

const int i = 1;
或者
int const i=1;

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

2、修饰数组

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

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

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

3、修饰指针

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

int i = 1;
int j = 2;


const int *p1 = &i;
int* const p2 = &j;

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

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

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

4、 修饰函数参数

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

void fun(const int i)
{
    ……
    i++; //对i的值进行了修改,程序报错
}

常用的函数如strlen。

size_t strlen(const char *string);

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

3static关键字

1、static修饰全局变量,该变量只在本文件内被访问,不能在其他文件被直接访问。

2、static修饰函数,该函数只能在本文件内被访问,不能被其他文件访问。但是可以通过嵌套的方式调用,变相的封装的表现。

3、static修饰局部变量,更改该局部变量的生命周期。

生命周期:将临时变量的生命周期变成全局变量的生命周期。

作用域不变:作用域仍然是在本代码块内。

4struct与union

可以使用struct结构体来存放一组不同类型的数据。

struct 结构体名{
    结构体所包含的变量或数组
};

结构体是一种集合,它里面包含了多个变量或数组,它们的类型可以相同,也可以不同,每个这样的变量或数组都称为结构体的成员,通常我们使用结构体定义和解析协议,如下所示:

// WiFi接收数据帧,控制切换模式
#pragma pack(1)
typedef struct receive_data_mode_t
{
    uint8_t device_head;        // 数据帧头:0XA0+功能码(FUNCTION_ID3),A款产品智能插座
    uint16_t device_len;        // 数据包总长度
    uint16_t device_id;         // 节点ID 0X0001~0XFFFE
    char software_version[15];  // 软件版本 SMART_SW_A1_1.0 A款产品软件1.0版本
    char hardware_version[15];  // 硬件版本 SMART_HW_A1_1.0 A款产品硬件1.0版本
    uint8_t switch_mode;        // 切换模式 0:运行模式,1:配置模式,2:节点升级,3:节点重启
    uint16_t crc;               // 校验位
}ReceiveData_Mode_t;
#pragma pack()

union共用体关键字,定义union下面的成员变量共享一块内存,每一个成员在任一时刻有且只有一个成员使用此块内存。

union 共用体名{
    成员列表
};

结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。

通常使用共用体做一些标志位操作,例如以下示例,可以非常灵活的访问Val中的bit位。

typedef union
 {
     BYTE Val;
     struct __packed
     {
        BYTE b0:1;
        BYTE b1:1;
        BYTE b2:1;
        BYTE b3:1;
        BYTE b4:1;
        BYTE b5:1;
        BYTE b6:1;
        BYTE b7:1;
    } bits;
}BYTE_VAL, BYTE_BITS;

或者使用共用体实现单字节与多字节的转化和拼接,如下所示:

#include "stdio.h"


typedef struct
{
    union
    {
        struct
        {
            unsigned char low;
            unsigned char high;
        };
        unsigned short result;
    };
}test_t;




int main(int argc, char *argv[])
{
    test_t hello;


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


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


    return 0;
}

5预定义标识符

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

常用的预定义标识符如下所示:

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

在Debug打印日志时候经常会用到,如下所示:

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

6#与##

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

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

#include "stdio.h"


#define TO_STR(s) #s
#define COMB(str1,str2) str1##str2


int main(int argc, char *argv[])
{
    int UART0= 115200;


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


    return 0;
}

7void 与 void*关键字

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

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

void * memcpy( void *dest, const void *src, size_t len );
void * memset( void *buffer, int c, size_t num);

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

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

void fun(void);

8weak关键字

一般简化定义如下所示:

#define _WEAK __attribute__((weak))

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

_WEAK void fun(void)  
{  
    //do this
}  


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

这种自动选择的机制,在代码移植和多模块配合工作的场景下应用较多。例如前期移植代码,需要调用某个接口fun,但当前该接口不存在或者未移植完整使用,可以使用weak关键字定义为空函数先保证编译正常。

后续移植完成实现了fun,即软件中有2个fun函数没有任何错误,编译器自动会识别使用后者。当然也粗暴的#if 0屏蔽对fun的调用,但要确保后续记得放开。

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

    关注

    5082

    文章

    19117

    浏览量

    304963
  • 软件开发
    +关注

    关注

    0

    文章

    614

    浏览量

    27356
  • 关键字
    +关注

    关注

    0

    文章

    37

    浏览量

    6895
  • 运算符
    +关注

    关注

    0

    文章

    172

    浏览量

    11083

原文标题:嵌入式软件开发常用的关键字和运算符

文章出处:【微信号:美男子玩编程,微信公众号:美男子玩编程】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    C语言面试最常问的关键字

    大家好,我是嵌入式老林,从事嵌入式软件开发多年,今天分享的内容是C语言面试最常问的三个关键字,希望能对你有所帮助。
    发表于 07-11 12:41 431次阅读

    【下载】《嵌入式C编程与Atmel.AVR》

    对Atmel AVR RISC处理器的介绍,读者会立即进入嵌入式C语言教程之中。在本书中,读者将学习C语言的变量和常量、运算符和表达式、控制语句、指针和数组、存储器类型、预处理指令、实时方法等等。本书特点
    发表于 01-11 18:02

    【下载】《嵌入式C编程与Atmel AVR》

    对Atmel AVR RISC处理器的介绍,读者会立即进入嵌入式C语言教程之中。在本书中,读者将学习C语言的变量和常量、运算符和表达式、控制语句、指针和数组、存储器类型、预处理指令、实时方法等等。  本书
    发表于 01-31 18:06

    嵌入式软件编程中关键字的用法和原理

    青岛职业威廉希尔官方网站 学院刘浩山东省文登市泽头镇***张斌引言计算机编程语言的关键字就好比是它的灵魂,只有深入理解了它们的含义才能编写出优秀的代码。C语言以其简洁、高效和强大等特性成为嵌入式软件编程的首选语言
    发表于 06-20 07:37

    Volatile关键字对于嵌入式开发有什么作用呢

    前言在进行嵌入式开发中,我们往往会看到一个名为volatile或者__IO的关键字,那么,它们对于嵌入式开发有什么作用呢?浅析volatile数据类型实际上,__IO和volatile是相同的
    发表于 11-05 09:20

    嵌入式程序员常见的const、static、volatile关键字

    开发中,const、static、volatile都是很常见的,我们嵌入式程序员需要熟知这三个关键字的恰当用法。下面是我参照网路上的大咖,大牛的博文,然后做一些代码测试,撰写一下自己的体会。欢迎大家一...
    发表于 12-21 06:08

    什么是嵌入式软件开发

    嵌入式软件开发又是指什么?   随着嵌入式软件系统结构越来越复杂,嵌入式软件
    发表于 04-20 08:43 8779次阅读

    ARM嵌入式软件开发

    ARM嵌入式软件开发ARM嵌入式软件开发ARM嵌入式软件开发
    发表于 01-15 17:29 65次下载

    C语言32个关键字九种控制语句34种运算符

    C语言32个关键字九种控制语句34种运算符 C语言32个关键字九种控制语句34种运算符
    发表于 05-06 11:47 6次下载

    32个关键字9个控制语句34个运算符概述

    32个关键字9个控制语句34个运算符
    发表于 05-15 09:32 15次下载

    关于嵌入式Linux软件开发常用知识

    C语言是嵌入式软件开发的基础,c佳佳在嵌入式领域也有一定的份额,但是很少,所以我们如果要做嵌入式软件开发,就得熟练掌握c语言语法。
    发表于 08-26 17:11 1452次阅读

    python运算符是什么

    python运算符 0. 什么是运算符? 本章节主要说明Python的运算符。举个简单的例子 4 +5 = 9 。 例子中,4和5被称为操作数,“+”号为运算符。 Python语言支持
    的头像 发表于 02-21 16:44 2385次阅读

    什么是运算符重载

    重载运算符是具有特殊名称的函数,是通过关键字** operator **后跟运算符的符号来定义的
    的头像 发表于 01-20 15:30 2494次阅读

    C语言32个关键字可以分为这几类

    C语言是一种面向过程、抽象化的通用程序设计语言,广泛应用于嵌入式底层和系统开发。C语言关键字共有32个,根据用途,可分为四类:第一类:变量数据类型关键字:14个第二类:9条基本语句用到
    的头像 发表于 12-31 15:28 5068次阅读
    C语言32个<b class='flag-5'>关键字</b>可以分为这几类

    嵌入式软件开发软件开发的区别

    嵌入式软件开发软件开发是两个不同的概念,它们在一些关键方面有着明显的区别。嵌入式软件开发是指
    的头像 发表于 01-22 15:27 2281次阅读