青岛网站设计流程,海南做网站的网络公司,如何建设网站 知乎,网站推广营销活动目录
1、volatile关键字
2、const关键字
3、static关键字
4、struct与union
5、预定义标识符
6、#与##
7、void 与 void*关键字
8、weak关键字 1、volatile关键字
volatile是一个特征修饰符#xff0c;提醒编译器它后面所定义的变量随时都有可能改变#xff0c;因此…目录
1、volatile关键字
2、const关键字
3、static关键字
4、struct与union
5、预定义标识符
6、#与##
7、void 与 void*关键字
8、weak关键字 1、volatile关键字
volatile是一个特征修饰符提醒编译器它后面所定义的变量随时都有可能改变因此编译后的程序每次需要存储或读取这个变量的时候告诉编译器对该变量不做优化都会直接从变量内存地址中读取数据从而可以提供对特殊地址的稳定访问。
常用场景中断服务与主程序共享变量。
示例代码
//volatile uint8_t flag1;
uint8_t flag1;void test(void)
{while(flag){//do something}
}//interrupt service routine
void isr_test(void)
{flag0;
}
如果没使用volatile定义flag可能在优化后test陷入死循环因为test里使用的flag并没修改它开启优化后编译器可能会固定从某个内存取值。
2、const关键字
const 是 constant 的缩写意思是“恒定不变的”它是定义常变量的关键字。
通常有4种用法。
1、修饰变量
采用const修饰变量即变量声明为只读保护变量值以防被修改。
const int i 1;
或者
int const i1;
变量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即其指向空间的值不可改变若改变其指向空间的值如*p110则程序会报错但p1的值是可以改变的对p1重新赋值如p1k是没有任何问题的。
在定义2中const限定的是指针p2若改变p2的值如p2k程序将会报错但*p2即其所指向空间的值可以改变如*p220是没有问题的程序正常执行。
4、 修饰函数参数
const关键字修饰函数参数对参数起限定作用防止其在函数内部被修改所限定的函数参数可以是普通变量也可以是指针变量。
void fun(const int i)
{……i; //对i的值进行了修改程序报错
}
常用的函数如strlen。
size_t strlen(const char *string);
const在库函数中使用非常普遍是一种自我保护的安全编码思维。
3、static关键字
1、static修饰全局变量该变量只在本文件内被访问不能在其他文件被直接访问。
2、static修饰函数该函数只能在本文件内被访问不能被其他文件访问。但是可以通过嵌套的方式调用变相的封装的表现。
3、static修饰局部变量更改该局部变量的生命周期。
生命周期将临时变量的生命周期变成全局变量的生命周期。作用域不变作用域仍然是在本代码块内。
4、struct与union
可以使用struct结构体来存放一组不同类型的数据。
struct 结构体名{结构体所包含的变量或数组
};
结构体是一种集合它里面包含了多个变量或数组它们的类型可以相同也可以不同每个这样的变量或数组都称为结构体的成员通常我们使用结构体定义和解析协议如下所示
// WiFi接收数据帧控制切换模式
#pragma pack(1)
typedef struct receive_data_mode_t
{uint8_t device_head; // 数据帧头0XA0功能码FUNCTION_ID3A款产品智能插座uint16_t device_len; // 数据包总长度uint16_t device_id; // 节点ID 0X0001~0XFFFEchar 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.htypedef struct
{union{struct{unsigned char low;unsigned char high;};unsigned short result;};
}test_t;int main(int argc, char *argv[])
{test_t hello;hello.high0x12;hello.low0x34;printf(result%04X\r\n,hello.result);//输出 result1234 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##str2int main(int argc, char *argv[])
{int UART0 115200;printf(UART0%d\n, COMB(UART, 0));//字符串合并为变量UART0printf(%s\n, TO_STR(3.14));//将数字变成字符串return 0;
}
7、void 与 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);
8、weak关键字
一般简化定义如下所示
#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的调用但要确保后续记得放开。