1.stdint.h简介
stdint.h 是从 C99 中引进的一个标准 C 库的文件,路径:D:\MDK5.34\ARM\ARMCC\include
推荐使用符合C99标准的uint8_t、uint16_t、uint32_t等类型别名
注意:配置MDK需要配置支持C99模式
2.位操作*
- 给变量某个位,置位(例如位6)
temp &= 0xFFFFFFBF; //位清零
temp |= 0x00000040; //置位
temp &= ~(1<<6); //位清零
temp |= 1<<6; //置位
- 给变量某个位,翻转(例如位6)
temp ^= 0x00000040;
temp ^= 1<<6;
3.宏定义
宏定义可以提高效率、可读性、易改性,核心是替换
#define 标识符 字符串
标识符:宏定义的名字
字符串:常数、表达式、格式串等
- 一般的宏定义
#define PI 3.14159
#define HSE_VALUE 8000000U
- 带参数的宏定义
#define LED1(x) do{ x ? \
HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_RESET); \
}while(0)
建议使用 do{ … }while(0)来构造宏定义,这样不会受到大括号、分号、运算符优先级等的影响,总是会按你期望的方式调用运行。具体可以参考https://blog.csdn.net/xiaoyilong2007101095/article/details/77067686
4.条件编译
让编译器只对满足条件的代码进行编译,不满足条件的不参与编译
头文件的条件编译
#ifndef _LED_H
#define _LED_H
#include "./SYSTEM/sys/sys.h"
//code...
#endif
代码条件编译
#if SYS_SUPPORT_OS
//code...
#endif
5.extern声明
放在函数/变量前,表示此函数/变量在其他作用域定义,以便本文件引用
extern uint16_t g_usart_rx_sta;
extern void delay_us(uint32_t nus);
6.类型别名(typedef)
为现有数据类型创建一个新的名字,或称为类型别名,用来简化变量的定义
typedef 现有类型 新名字
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
在类型别名时的应用
struct GPIO_TypeDef
{
__IO uint32_t CRL;
__IO uint32_t CRH;
…
};
struct GPIO_TypeDef gpiox; //必须加struct
//等同于,上面的更繁琐
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
…
} GPIO_TypeDef;
GPIO_TypeDef gpiox
更多参考:
https://www.runoob.com/cprogramming/c-structures.html
https://www.runoob.com/cprogramming/c-typedef.html
7.结构体
由若干基本数据类型集合组成的一种自定义数据类型,也叫聚合类型
struct tag {
member-list
member-list
member-list
...
} variable-list ;
在一般情况下,tag、member-list、variable-list 这 3 部分至少要出现 2 个。
typedef struct
{
uint32_t Pin ; /* 引脚号 */
uint32_t Mode ; /* 工作模式 */
uint32_t Pull ; /* 上下拉 */
uint32_t Speed ; /* IO速度 */
} GPIO_InitTypeDef;
8.指针
指针就是内存的地址,指针就是内存的地址
类型名 * 指针变量名
char * p_str = “This is a test!”;
*p_str:取p_str 变量的值
&p_str:取p_str变量的地址
指针练习
uint8_t buf[5] = {1, 3, 5, 7, 9};
uint8_t * p_buf = buf;
*p_buf = 1
p_buf[0] = 1
p_buf[1] = 3
p_buf++;
*p_buf = 3
p_buf[0] = 3
指针使用的2大最常见问题:
- 未分配(申请)内存就用
char * p_buf;
p_buf[0] = 100;
p_buf[1] = 120;
p_buf[2] = 150;
- 越界使用**
uint8_t buf[5] = {1, 3, 5, 7, 9};
uint8_t * p_buf = buf;
p_buf[5] = 200;
p_buf[6] = 250;
9.代码规范
规范的关键点:
- 所有函数/变量名字非特殊情况,一般使用小写字母;
- 注释风格使用 doxgen 风格,除屏蔽外,一律使用 /* */ 方式进行注释;
- TAB 键统一使用 4 个空格对齐,不使用默认的方式进行对齐;
- 每两个函数之间,一般有且只有一个空行;
- 相对独立的程序块之间,使用一个空行隔开;
- 全局变量命名一般用 g_开头,全局指针命名一般用 p_开头;
- if、 for、 while、 do、 case、 switch、 default 等语句单独占一行, 一般无论有多少行执行语句,都要用加括号: {}。