目录

C 语言总结与提高

前言

C 语言是兼具高效与灵活的结构化编程语言,诞生于 1972 年,兼具低级语言的硬件操控能力和高级语言的抽象特性,能直接操作内存、寄存器,适配操作系统、嵌入式等底层开发场景。其长久不衰的核心原因:语法简洁通用,跨平台性强,编译器生态成熟;贴近硬件的高效性无可替代,是操作系统、驱动、嵌入式固件的核心开发语言;同时作为编程入门经典,衍生出 C++、Java 等语言,奠定了编程基础,至今仍是底层开发与系统编程的首选。

基本语法

1、数据类型关键字
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 //A.基本数据类型(5个)

    int      :整型数据,通常为默认类型;
    float    :单精度浮点型;
    double   :双精度浮点型;
    char     :字符型类型数据,属于整型数据的一种;
    void     :空类型,声明函数无返回值或无参数,声明无类型指针,显式丢弃运算结果;

 //B.类型修饰关键字(4个)

    short    :修饰int,短整型数据,可省略被修饰的int
    long     :修饰int,长整形数据,可省略被修饰的int
    signed   :修饰整型数据,有符号数据类型;
    unsigned :修饰整型数据,无符号数据类型;

 //C.复杂类型关键字(5个)

    struct   :结构体声明;
    union    :共用体声明;
    enum     :枚举声明;
    typedef  :声明类型别名;
    sizeof   :得到特定类型或特定类型变量的大小; 

 //D.存储级别关键字(6个)

    auto     :指定为自动变量,由编译器自动分配及释放。通常在栈上分配
    static   :指定为静态变量,分配在静态变量区; 修饰函数时,指定函数作用域为文件内部
    register :指定为寄存器变量,建议编译器将变量存储到寄存器中使用,也可以修饰函数形参,建议编译器通过寄存器而不是堆栈传递参数
    extern   :指定对应变量为外部变量,即标示变量或者函数的定义在别的文件中,表示编译器遇到此变量和函数时在其他模块中寻找其定义。
    const    :指定变量不可被当前线程/进程改变(但有可能被系统或其他线程/进程改变)
    volatile :指定变量的值有可能会被系统或其他进程/线程改变, 强制编译器每次从内存中取得该变量的值
2、流程控制关键字
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
 //A.跳转结构(4个)

    return   :用在函数体中,返回特定值(或者是void值,即不返回值)
    continue :结束当前循环,开始下一轮循环
    break    :跳出当前循环或switch结构
    goto     :无条件跳转语句

 //B.分支结构(5个)

    if      :条件语句,后面不需要放分号
    else    :条件语句否定分支(if连用)
    switch  :开关语句(多重分支语句)
    case    :开关语句中的分支标记
    default :开关语句中的“其他”分支,可选。

 //C.循环结构(3个)

    for     :循环结构,for(1;2;3)4;的执行顺序为1->2->4->3->2...循环,其中2为循环条件;
    do      :do循环结构,do 1 while(2); 的执行顺序是1->2->1...循环,2为循环条件;
    while   :while循环结构,while(1) 2; 的执行顺序是1->2->1...循环,1为循环条件; 
3、预编译宏命令
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
    #define       :定义一个预处理宏
    #undef        :取消宏的定义
    #include      :包含文件命令
    #include_next :与#include相似, 但它有着特殊的用途
    #if           :编译预处理中的条件命令, 相当于C语法中的if语句
    #ifdef        :判断某个宏是否被定义, 若已定义, 执行随后的语句
    #ifndef       :与#ifdef相反, 判断某个宏是否未被定义
    #elif         :若#if, #ifdef, #ifndef或前面的#elif条件不满足, 则执行#elif之后的语句, 相当于C语法中的else-if
    #else         :与#if, #ifdef, #ifndef对应,  若这些条件不满足, 则执行#else之后的语句, 相当于C语法中的else
    #endif        :#if, #ifdef, #ifndef这些条件命令的结束标志.
    defined       :与#if, #elif配合使用, 判断某个宏是否被定义
    #line         :标志该语句所在的行号
    #             :将宏参数替代为以参数值为内容的字符串常量(包括""号)
    ##            :将两个相邻的标记(token)连接为一个单独的标记
    #pragma       :说明编译器信息
    #warning      :显示编译警告信息
    #error        :显示编译错误信息
4、操作符优先级
表达式 优先级
()(小括号) [](数组下标) .(结构成员) ->(指针型结构成员) 最高
!(逻辑非) ~(位取反) -(负号) ++(加1) --(减1) &(变量地址)
*(指针所指内容) (type)(类型转换) sizeof(长度计算)
*(乘) /(除) %(取模)
+(加) -(减)
<<(位左移) >>(位右移)
<(小于) <=(小于等于) >(大于) >=(大于等于)
==(等于) !=(不等于)
&(位与)
^(位异或)
|(位或)
&&(逻辑与)
||(逻辑或)
?:(?表达式)
= += -=(联合操作)
,(逗号运算符) 最低

PS:什么是一元操作符、二元操作符、三元操作符?
 一元操作符:如 & p = &x;
 二元操作符:如 + c = a + b;
 三元操作符:如 ?:x = a ? b : c;

5、数据类型值域范围

5.1、数据类型值域范围:

数据类型 长度/bit 长度/Byte 值域范围
bit 1 0,1
unsigned char 8 1 0~255
signed char 8 1 -128~127
unsigned int 16 2 0~65536
singned int 16 2 -32768~32767
unsigned long 32 4 0~4294967295
singned long 32 4 -2147483648~2147483647
float 32 4 -3.40E+38~3.40E+38 (有效小数位为6-7)
double 64 8 -1.79E+308~1.79E+308 (有效小数位为15-16)

5.2、科学计数法:

科学计数法 说明
1.23456789e+8 = 123456789,小数点需要往【右】移动【8】位
1.2345e-3 = 0.0012345,小数点需要往【左】移动【4】位

5.3、浮点数的数据类型:

数据类型 符号位
(位数与正负)
指数位
(位数与范围)
尾数位
(位数与范围)
float 1bit
0: 正数,1: 负数
8bits
-128~127
23bits
0~(2^23)
double 1bit
0: 正数,1: 负数
11bits
-1024~1023
52bits
0~(2^52)

5.4、浮点数的数学法则:

类别 算式案例 数学法则
符号位 ±(正负号) 由“浮点数符号位”决定,0: 正数,1: 负数
指数位 1.111……1(二进制数) 【算式整数】固定为“1”,【算式小数】为“浮点数指数的二制数值”
尾数位 2^12356(十进制数) 【算式底数】固定为“2”,【算式指数】为“浮点数尾数的转换后十进制数”

5.5、浮点数的公式:

数值 = (±符号位)((2^指数)(1+(尾数/尾数最大值)))
floatL:数值 = (±符号位)((2^指数)
(1+(尾数/(2^23))))
double:数值 = (±符号位)((2^指数)*(1+(尾数/(2^25))))

浮点数19.625用float是如何存储的:
将浮点数转换成二进制:10011.101(将 19.625 整数部分采用除 2 取余,小数部分采用乘 2 取整法);
用科学计数法表示二进制浮点数:1.0011101*2^4;
计算指数偏移后的值:127 + 4 = 131 (10000011);
拼接综上所述,float 类型的 19.625 在内存中的值为:0 – 10000011 – 001 1101 0000 0000 0000 0000。
C语言浮点数float类型的秘密

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
> 补注:  
> E-38就是小数点需要往左移动38  
> E+38就是小数点需要往右移动38  

> float : 1bit(符号位) 8bits(指数位,最高位为符号位,即数值是7bits 23bits(尾数位)  
> double: 1bit(符号位)11bits(指数位,最高位为符号位,即数值是10bits52bits(尾数位)  

> float : 【指数数值】: 最大值:(2^7)-1=127,      最小值:-(2^7)=-128,     即【指数数值】范围:-128~127,   精度:1  
> float : 【尾数数值】: 最大值:(2-(1/(2^23)))2, 最小值:(2-(1/(2^0)))=1, 即【尾数数值】范围:1.0~2.0,    精度:(0~(2^23))/(2^23)  

> double: 【指数数值】: 最大值:(2^10)-1=1023,    最小值:-(2^10)=-1024,   即【指数数值】范围:-1024~1023, 精度:1   
> double: 【尾数数值】: 最大值:(2-(1/(2^52)))2, 最小值:(2-(1/(2^0)))=1, 即【尾数数值】范围:1.0~2.0,    精度:(0~(2^23))/(2^52)   

> 数值 = (±符号位)((2^指数数值) * 1.尾数数值)  
> 数值 = (±符号位)((2^指数数值) * 1.尾数二进制数值)  

> 备注-1:“1.尾数二进制数值”的“1”是带小数点二进制数的整数部分  
> 备注-2:“1.尾数二进制数值”的“尾数二进制数值”是带小数点二进制数的小数部分  

> 数值 = (±符号位)((2^指数)*(1+(尾数/尾数最大值)))  

> 参考 [网文1](https://blog.csdn.net/black_kyatu/article/details/79257346)  
> 参考 [网文2](http://blog.sina.com.cn/s/blog_6ebd49350101gdgo.html)  

宏定义应用

1、位操作
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//生成第n位为1的立即数(n=0表示bit0)
#define XT_1BIT_GEN(n)                       (1UL<<(n))

//生成第n位为0的立即数(n=0表示bit0)
#define XT_0BIT_GEN(n)                       (~(1UL<<(n)))

//生成第n~m位为1的立即数((n=0表示bit0)
#define XT_1BITS_GEN(n,m)                    (((~0UL)>>(32-((m)-(n)+1)))<<(n))

//生成第n~m位为0的立即数(n=0表示bit0)
#define XT_0BITS_GEN(n,m)                    (~(((~0UL)>>(32-((m)-(n)+1)))<<(n)))

//将变量数x的第n位置位(n=0表示bit0)
#define XT_BIT_SET(x,n)                      ((x) | (1UL<<(n)))

//将变量x的第n位清零(n=0表示bit0)
#define XT_BIT_CLR(x,n)                      ((x) & (~(1UL<<(n))))

//将变量x的第n~m位置位(n=0表示bit0)
#define XT_BITS_SET(x,n,m)                   ((x) | (((~0UL)>>(32-((m)-(n)+1)))<<(n)))

//将变量x的第n~m位清零(n=0表示bit0)
#define XT_BITS_CLR(x,n,m)                   ((x) & (~(((~0UL)>>(32-((m)-(n)+1)))<<(n))))

//获取变量x的第n~m位数(n=0表示bit0)
#define XT_BITS_GET(x,n,m)                   ((x) & (((~0UL)>>(32-((m)-(n)+1)))<<(n)))

//获取变量x的第n~m位并转成低位数(n=0表示bit0)
#define XT_BITS_GET2(x,n,m)                  (((x) & (((~0UL)>>(32-((m)-(n)+1)))<<(n))) >> (n))
2、结构体运算
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
//算出结构体某个成员占的空间大小             (使用例子: XT_STRUCT_MB_SZ(dev_obj_t, name/*不能有[0]*/))
#define XT_STRUCT_MB_SZ(s,m)                 (((uint32_t)(sizeof(((s *)0)->m))))

//算出结构体某个成员的前面成员占的空间大小   (使用例子: XT_STRUCT_100PART_SZ(dev_obj_t, name))
#define XT_STRUCT_100PART_SZ(s,m)            (((uint32_t)(&(((s *)0)->m))))

//算出结构体某个成员和前面成员占的空间大小   (使用例子: XT_STRUCT_110PART_SZ(dev_obj_t, name/*不能有[0]*/))
#define XT_STRUCT_110PART_SZ(s,m)            (((uint32_t)(&(((s *)0)->m))) + ((uint32_t)(sizeof(((s *)0)->m))))

//算出结构体某个成员的后面成员占的空间大小   (使用例子: XT_STRUCT_001PART_SZ(dev_obj_t, name/*不能有[0]*/))
#define XT_STRUCT_001PART_SZ(s,m)            (((uint32_t)(sizeof(s))) - ((uint32_t)(&(((s *)0)->m))) - ((uint32_t)(sizeof(((s *)0)->m))))

//算出结构体某个成员和后面成员占的空间大小   (使用例子: XT_STRUCT_011PART_SZ(dev_obj_t, name))
#define XT_STRUCT_011PART_SZ(s,m)            (((uint32_t)(sizeof(s))) - ((uint32_t)(&(((s *)0)->m))))

//算出结构体某个成员在结构体中的位置偏移量   (使用例子: XT_STRUCT_MB_OFFSET(dev_obj_t, name))
#define XT_STRUCT_MB_OFFSET(s,m)             (((uint32_t)(&(((s *)0)->m))))

//算出结构体某个成员后一个成员的位置偏移量   (使用例子: XT_STRUCT_NX_OFFSET(dev_obj_t, name/*不能有[0]*/))
#define XT_STRUCT_NX_OFFSET(s,m)             (((uint32_t)(&(((s *)0)->m))) + ((uint32_t)(sizeof(((s *)0)->m))))
3、数据合成
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
//将[uint8_t]合并成[uint16_t]                (d2作最高位, d1最低位)
#define XT_U8_TO_U16(d2,d1)                  ((((uint16_t)(d2) & 0xFF) << 8) | ((uint16_t)(d1) & 0xFF))

//将[uint16_t]合并成[uint32_t]               (d2作最高位, d1最低位)
#define XT_U16_TO_U32(d2,d1)                 ((((uint32_t)(d2) & 0xFFFF) << 16) | ((uint32_t)(d1) & 0xFFFF))

//将[uint8_t]合并成[uint32_t]                (d4作最高位, d1最低位)
#define XT_U8_TO_U32(d4,d3,d2,d1)            ((((uint32_t)(d4) & 0xFF) << 24)\
                                             |(((uint32_t)(d3) & 0xFF) << 16)\
                                             |(((uint32_t)(d2) & 0xFF) <<  8)\
                                             |(((uint32_t)(d1) & 0xFF)      ))
4、数据转换
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
//二进制书写格式数据转换                     (使用例子: XT_0XBIN_TO_HEX16(0x00001111,0x11110000))
#define XT_0XBIN_TO_HEX8(n)                  (((n>>(28-7))&0x80)|((n>>(24-6))&0x40)|((n>>(20-5))&0x20)|((n>>(16-4))&0x10)|\
                                              ((n>>(12-3))&0x08)|((n>>( 8-2))&0x04)|((n>>( 4-1))&0x02)|((n)        &0x01))
#define XT_0XBIN_TO_HEX16(n1,n0)             (((uint16_t)(XT_0XBIN_TO_HEX8(n1))<<8)|(XT_0XBIN_TO_HEX8(n0)&0xFF))
#define XT_0XBIN_TO_HEX32(n3,n2,n1,n0)       (((uint32_t)(XT_0XBIN_TO_HEX8(n3))<<24)|((uint32_t)(XT_0XBIN_TO_HEX8(n2))<<16)\
                                             |((uint32_t)(XT_0XBIN_TO_HEX8(n1))<<8)|(XT_0XBIN_TO_HEX8(n0)&0xFF))

//二进制书写格式数据转换                     (使用例子: XT_8BIN_TO_HEX16(00001111,11110000))
#define _XT_8BIN_TO_HEX8(n)                  XT_0XBIN_TO_HEX8(0x##n##UL) //应用程序不能调用本宏,请调用下行宏定义
#define XT_8BIN_TO_HEX8(n)                  _XT_8BIN_TO_HEX8(n)
#define XT_8BIN_TO_HEX16(n1,n0)              (((uint16_t)(_XT_8BIN_TO_HEX8(n1))<<8)|(_XT_8BIN_TO_HEX8(n0)&0xFF))
#define XT_8BIN_TO_HEX32(n3,n2,n1,n0)        (((uint32_t)(_XT_8BIN_TO_HEX8(n3))<<24)|((uint32_t)(_XT_8BIN_TO_HEX8(n2))<<16)\
                                             |((uint32_t)(_XT_8BIN_TO_HEX8(n1))<<8)|(_XT_8BIN_TO_HEX8(n0)&0xFF))
5、序列化提取

X-MACRO(X-宏)是一种用于维护代码或数据并行列表的可靠技术,核心优势在于确保多个关联列表始终保持顺序一致(匹配)。其应用场景广泛,可覆盖数组、枚举、结构体、普通列表及代码段生成等场景;本质是通过定义「汇总集合表」,实现使用时对某类或多类元素的批量提取与复用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 事先编排的宏与组员关系表(注:DEF_X() 还没有定义,后面使用时才定义)
#define XCMD_PFUNC                       \
        DEF_X(PLAY     , func_play     ) \
        DEF_X(PAUSE    , func_pause    ) \
        DEF_X(STOP     , func_stop     ) \
        DEF_X(PLAY_NEXT, func_play_next) \
        DEF_X(PLAY_PREV, func_play_prev)

// 利用预编译生成有序枚举(表):enum {PLAY, PAUSE, STOP, PLAY_NEXT, PLAY_PREV}
typedef enum
{
	#define DEF_X(n,p) n,
	XCMD_PFUNC
	#undef DEF_X
	XCMD_MAX
}tXCMD;

// 利用预编译生成指针数组(表):pfunc[] = {func_play, func_pause, func_stop, func_play_next, func_play_prev}
typedef int (*PF)(void *);
const PF pfunc[] =
{
	#define DEF_X(n,p) p,
	XCMD_PFUNC
	#undef DEF_X
};

PS:关于 X-宏 更多介绍,可参考【X-MACRO 数据与代码序列化】。

6、常用宏汇总
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
/**
  * @file  xt_comdef.h
  * @brief 常用宏定义集合
  * COPYRIGHT (C) 2022, XT 技术联合小组
  * Change Logs:
  * Date           Author       Notes
  * 2022-11-08     o2ospring    原始版本
  */
#ifndef XT_COMDEF_H__
#define XT_COMDEF_H__

#include <stdint.h> //////////////////////// <- 使用的数据定义,如: int8_t, uint32_t 等

#ifdef __cplusplus
extern "C" {
#endif

//【标准数据类型】----------------------------------------------------------------------------------------

#if 0
typedef unsigned long int uint32_t;          /* unsigned 32 bit value */
typedef unsigned short    uint16_t;          /* unsigned 16 bit value */
typedef unsigned char     uint8_t;           /* unsigned 8  bit value */
typedef signed long int   int32_t;           /* signed 32 bit value */
typedef signed short      int16_t;           /* signed 16 bit value */
typedef signed char       int8_t;            /* signed 8  bit value */
#endif

//【编译器识别宏】----------------------------------------------------------------------------------------

// 1、IAR-ARM 编译器识别宏(由 IARSystems 公司推出,应用的 IDE 有:IAR-EWARM)
#if   defined(__ICCARM__)
// 2、ARM-CC 编译器识别宏(由 ARM 公司推出,应用的 IDE 有:Keil-MDK)(即将淘汰)
#elif defined(__CC_ARM)
// 3、LLVM-clang 编译器识别宏(由 Apple 公司主导,应用的 IDE 有:Keil-MDK 等)
#elif defined(__clang__)
#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) //Keil-MDK
// 4、GNU-gcc 编译器识别宏(由 GNU 推出,应用的 IDE 有:Eclipse、VScode 等等)
#elif defined(__GNUC__)
// 5、Microsoft-MSC 编译器识别宏(由 微软 推出,应用的 IDE 有:Visual Studio)
#elif defined (_MSC_VER)
#endif

//【编译属性】--------------------------------------------------------------------------------------------

// 1、IAR-ARM 编译器识别宏(由 IARSystems 公司推出,应用的 IDE 有:IAR-EWARM)
#if   defined(__ICCARM__)

#define XT_PRAGMA(x)                         _Pragma(#x)
#define XT_SECTION(x)                        @ x                             //数据拼接 (对象名称后明声明)
#define XT_AT(a)                             @ a                             //地址指定 (对象名称后明声明)
//#define XT_PACKED                          ??? /*无法兼容*/                //字节对齐 (对象名称后明声明,强烈建议改用 #pragma pack(push, 1) ... #pragma pack(pop) 的兼容性更好)
#define XT_ALIGN(n)                          XT_PRAGMA(data_alignment=n)     //地址对齐 (对象整体最前声明,等效于伪指令 #pragma location=地址 ... 注意:由[static]修饰的变量,[_Pragma]必须放在[static]之前,否则[_Pragma]不生效)
#define XT_UNUSED                            /*无法兼容,但影响不大,忽略即可*///未用不警告(对象整体最前声明)
#define XT_USED                              __root                          //未用不优化(对象整体最前声明)
#define XT_WEAK                              __weak                          //弱化对象 (对象整体最前声明)
#define XT_INLINE                            static inline                   //内联函数 (对象整体最前声明,c/h文件中直接编写函数(体),不能外部声明)

// 2、ARM-CC 编译器识别宏(由 ARM 公司推出,应用的 IDE 有:Keil-MDK)(即将淘汰)
#elif defined(__CC_ARM)

#define XT_SECTION(x)                        __attribute__((__section__(x))) //数据拼接 (对象名称后明声明)
#define XT_AT(a)                             __attribute__((__at__(a)))      //地址指定 (对象名称后明声明)
#define XT_PACKED                            __attribute__((__packed__))     //字节对齐 (对象名称后明声明,强烈建议改用 #pragma pack(push, 1) ... #pragma pack(pop) 的兼容性更好)
#define XT_ALIGN(n)                          __attribute__((__aligned__(n))) //地址对齐 (对象整体最前声明)
#define XT_UNUSED                            __attribute__((__unused__))     //未用不警告(对象整体最前声明)
#define XT_USED                              __attribute__((__used__))       //未用不优化(对象整体最前声明)
#define XT_WEAK                              __attribute__((__weak__))       //弱化对象 (对象整体最前声明)
#define XT_INLINE                            static __inline                 //内联函数 (对象整体最前声明,c/h文件中直接编写函数(体),不能外部声明)

// 3、LLVM-clang 编译器识别宏(由 Apple 公司主导,应用的 IDE 有:Keil-MDK 等)
#elif defined(__clang__) \
||   (defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))

#define XT_SECTION(x)                        __attribute__((__section__(x))) //数据拼接 (对象名称后明声明)
#define XT_AT(a)                             __attribute__((__at__(a)))      //地址指定 (对象名称后明声明)
#define XT_PACKED                            __attribute__((__packed__))     //字节对齐 (对象名称后明声明,强烈建议改用 #pragma pack(push, 1) ... #pragma pack(pop) 的兼容性更好)
#define XT_ALIGN(n)                          __attribute__((__aligned__(n))) //地址对齐 (对象整体最前声明)
#define XT_UNUSED                            __attribute__((__unused__))     //未用不警告(对象整体最前声明)
#define XT_USED                              __attribute__((__used__))       //未用不优化(对象整体最前声明)
#define XT_WEAK                              __attribute__((__weak__))       //弱化对象 (对象整体最前声明)
#define XT_INLINE                            static __inline                 //内联函数 (对象整体最前声明,c/h文件中直接编写函数(体),不能外部声明)

// 4、GNU-gcc 编译器识别宏(由 GNU 推出,应用的 IDE 有:Eclipse、VScode 等等)
#elif defined(__GNUC__)

#define XT_SECTION(x)                        __attribute__((__section__(x))) //数据拼接 (对象名称后明声明)
#define XT_AT(a)                             __attribute__((__at__(a)))      //地址指定 (对象名称后明声明)
#define XT_PACKED                            __attribute__((__packed__))     //字节对齐 (对象名称后明声明,强烈建议改用 #pragma pack(push, 1) ... #pragma pack(pop) 的兼容性更好)
#define XT_ALIGN(n)                          __attribute__((__aligned__(n))) //地址对齐 (对象整体最前声明)
#define XT_UNUSED                            __attribute__((__unused__))     //未用不警告(对象整体最前声明)
#define XT_USED                              __attribute__((__used__))       //未用不优化(对象整体最前声明)
#define XT_WEAK                              __attribute__((__weak__))       //弱化对象 (对象整体最前声明)
#define XT_INLINE                            static __inline                 //内联函数 (对象整体最前声明,c/h文件中直接编写函数(体),不能外部声明)

#endif

//【位操作】----------------------------------------------------------------------------------------------

//生成第n位为1的立即数(n=0表示bit0)
#define XT_1BIT_GEN(n)                       (1UL<<(n))

//生成第n位为0的立即数(n=0表示bit0)
#define XT_0BIT_GEN(n)                       (~(1UL<<(n)))

//生成第n~m位为1的立即数((n=0表示bit0)
#define XT_1BITS_GEN(n,m)                    (((~0UL)>>(32-((m)-(n)+1)))<<(n))

//生成第n~m位为0的立即数(n=0表示bit0)
#define XT_0BITS_GEN(n,m)                    (~(((~0UL)>>(32-((m)-(n)+1)))<<(n)))

//将变量数x的第n位置位(n=0表示bit0)
#define XT_BIT_SET(x,n)                      ((x) | (1UL<<(n)))

//将变量x的第n位清零(n=0表示bit0)
#define XT_BIT_CLR(x,n)                      ((x) & (~(1UL<<(n))))

//将变量x的第n~m位置位(n=0表示bit0)
#define XT_BITS_SET(x,n,m)                   ((x) | (((~0UL)>>(32-((m)-(n)+1)))<<(n)))

//将变量x的第n~m位清零(n=0表示bit0)
#define XT_BITS_CLR(x,n,m)                   ((x) & (~(((~0UL)>>(32-((m)-(n)+1)))<<(n))))

//获取变量x的第n~m位数(n=0表示bit0)
#define XT_BITS_GET(x,n,m)                   ((x) & (((~0UL)>>(32-((m)-(n)+1)))<<(n)))

//获取变量x的第n~m位并转成低位数(n=0表示bit0)
#define XT_BITS_GET2(x,n,m)                  (((x) & (((~0UL)>>(32-((m)-(n)+1)))<<(n))) >> (n))

//【结构体运算】------------------------------------------------------------------------------------------

//算出结构体某个成员占的空间大小             (使用例子: XT_STRUCT_MB_SZ(dev_obj_t, name/*不能有[0]*/))
#define XT_STRUCT_MB_SZ(s,m)                 (((uint32_t)(sizeof(((s *)0)->m))))

//算出结构体某个成员的前面成员占的空间大小   (使用例子: XT_STRUCT_100PART_SZ(dev_obj_t, name))
#define XT_STRUCT_100PART_SZ(s,m)            (((uint32_t)(&(((s *)0)->m))))

//算出结构体某个成员和前面成员占的空间大小   (使用例子: XT_STRUCT_110PART_SZ(dev_obj_t, name/*不能有[0]*/))
#define XT_STRUCT_110PART_SZ(s,m)            (((uint32_t)(&(((s *)0)->m))) + ((uint32_t)(sizeof(((s *)0)->m))))

//算出结构体某个成员的后面成员占的空间大小   (使用例子: XT_STRUCT_001PART_SZ(dev_obj_t, name/*不能有[0]*/))
#define XT_STRUCT_001PART_SZ(s,m)            (((uint32_t)(sizeof(s))) - ((uint32_t)(&(((s *)0)->m))) - ((uint32_t)(sizeof(((s *)0)->m))))

//算出结构体某个成员和后面成员占的空间大小   (使用例子: XT_STRUCT_011PART_SZ(dev_obj_t, name))
#define XT_STRUCT_011PART_SZ(s,m)            (((uint32_t)(sizeof(s))) - ((uint32_t)(&(((s *)0)->m))))

//算出结构体某个成员在结构体中的位置偏移量   (使用例子: XT_STRUCT_MB_OFFSET(dev_obj_t, name))
#define XT_STRUCT_MB_OFFSET(s,m)             (((uint32_t)(&(((s *)0)->m))))

//算出结构体某个成员后一个成员的位置偏移量   (使用例子: XT_STRUCT_NX_OFFSET(dev_obj_t, name/*不能有[0]*/))
#define XT_STRUCT_NX_OFFSET(s,m)             (((uint32_t)(&(((s *)0)->m))) + ((uint32_t)(sizeof(((s *)0)->m))))

//【数据转换】--------------------------------------------------------------------------------------------

//十进制转换为BCD码
#define XT_DEC_TO_BCD(dec)                   ((((uint8_t)(dec)/10U)<<4)|((uint8_t)(dec)%10U))

//BCD码转换为十进制
#define XT_BCD_TO_DEC(bcd)                   ((((uint8_t)(bcd)>>4)*10U)+((uint8_t)(bcd)&0xF))

//将一个字母转换为大写
#define XT_FS_TOUPPER(c)                     (((c)>='a'&&(c)<='z')?((c)-0x20):(c))

//将一个字母转换为小写
#define XT_FS_TOLOWER(c)                     (((c)>='A'&&(c)<='Z')?((c)+0x20):(c))

//大小端模式转换
#define XT_SWAP16(x)                         ((((uint16_t)(x)<<8)&0xFF00) | (((uint16_t)(x)>>8)&0xFF))

//大小端模式转换
#define XT_SWAP32(x)                         ((((uint32_t)(x)>>24)&0xFF)    \
                                             |(((uint32_t)(x)>> 8)&0xFF00)  \
                                             |(((uint32_t)(x)<< 8)&0xFF0000)\
                                             |(((uint32_t)(x)<<24)&0xFF000000))
//交换a、b值(要求相同数据类型)
#define XT_SWAP(a,b)                         (a) = (a)^(b); \
                                             (b) = (a)^(b); \
                                             (a) = (a)^(b)

//二进制书写格式数据转换                     (使用例子: XT_0XBIN_TO_HEX16(0x00001111,0x11110000))
#define XT_0XBIN_TO_HEX8(n)                  (((n>>(28-7))&0x80)|((n>>(24-6))&0x40)|((n>>(20-5))&0x20)|((n>>(16-4))&0x10)|\
                                              ((n>>(12-3))&0x08)|((n>>( 8-2))&0x04)|((n>>( 4-1))&0x02)|((n)        &0x01))
#define XT_0XBIN_TO_HEX16(n1,n0)             (((uint16_t)(XT_0XBIN_TO_HEX8(n1))<<8)|(XT_0XBIN_TO_HEX8(n0)&0xFF))
#define XT_0XBIN_TO_HEX32(n3,n2,n1,n0)       (((uint32_t)(XT_0XBIN_TO_HEX8(n3))<<24)|((uint32_t)(XT_0XBIN_TO_HEX8(n2))<<16)\
                                             |((uint32_t)(XT_0XBIN_TO_HEX8(n1))<<8)|(XT_0XBIN_TO_HEX8(n0)&0xFF))

//二进制书写格式数据转换                     (使用例子: XT_8BIN_TO_HEX16(00001111,11110000))
#define _XT_8BIN_TO_HEX8(n)                  XT_0XBIN_TO_HEX8(0x##n##UL) //应用程序不能调用本宏,请调用下行宏定义
#define XT_8BIN_TO_HEX8(n)                  _XT_8BIN_TO_HEX8(n)
#define XT_8BIN_TO_HEX16(n1,n0)              (((uint16_t)(_XT_8BIN_TO_HEX8(n1))<<8)|(_XT_8BIN_TO_HEX8(n0)&0xFF))
#define XT_8BIN_TO_HEX32(n3,n2,n1,n0)        (((uint32_t)(_XT_8BIN_TO_HEX8(n3))<<24)|((uint32_t)(_XT_8BIN_TO_HEX8(n2))<<16)\
                                             |((uint32_t)(_XT_8BIN_TO_HEX8(n1))<<8)|(_XT_8BIN_TO_HEX8(n0)&0xFF))

//将[uint8_t]合并成[uint16_t]                (d2作最高位, d1最低位)
#define XT_U8_TO_U16(d2,d1)                  ((((uint16_t)(d2) & 0xFF) << 8) | ((uint16_t)(d1) & 0xFF))

//将[uint16_t]合并成[uint32_t]               (d2作最高位, d1最低位)
#define XT_U16_TO_U32(d2,d1)                 ((((uint32_t)(d2) & 0xFFFF) << 16) | ((uint32_t)(d1) & 0xFFFF))

//将[uint8_t]合并成[uint32_t]                (d4作最高位, d1最低位)
#define XT_U8_TO_U32(d4,d3,d2,d1)            ((((uint32_t)(d4) & 0xFF) << 24)\
                                             |(((uint32_t)(d3) & 0xFF) << 16)\
                                             |(((uint32_t)(d2) & 0xFF) <<  8)\
                                             |(((uint32_t)(d1) & 0xFF)      ))

//【其它常用宏定义】--------------------------------------------------------------------------------------

//得到指定地址上的一个单字节数值
#define XT_MEM_B(x)                          (*((uint8_t *)(x)))

//得到指定地址上的一个双字节数值
#define XT_MEM_W(x)                          (*((uint16_t *)(x)))

//得到指定地址上的一个四字节数值
#define XT_MEM_H(x)                          (*((uint32_t *)(x)))

//求最小值
#define XT_MIN(x,y)                          (((x)<(y))?(x):(y))

//求最大值
#define XT_MAX(x,y)                          (((x)>(y))?(x):(y))

//返回一个比x大的最接近8的倍数
#define XT_RND8(x)                           ((((x)+7)/8)*8)

//判断字符是不是10进值的数字
#define XT_DECCHK(c)                         ((c)>='0'&&(c)<='9')

//判断字符是不是16进值的数字
#define XT_HEXCHK(c)                         (((c)>='0'&&(c)<='9') \
                                            ||((c)>='A'&&(c)<='F') \
                                            ||((c)>='a'&&(c)<='f'))
//防止溢出的一个方法
#define XT_INC_SAT(v)                        (v=((v)+1>(v))?(v)+1:(v))

//返回数组元素的个数
#define XT_ARRAY_SIZE(a)                     (sizeof((a))/sizeof((a[0])))

//断言
#define XT_ASSERT(expr)                      if (!(expr)) { while(1) ; }

//【ANSI标准宏】------------------------------------------------------------------------------------------

#if 0

//__FILE__, __LINE__, __FUNCTION__, __DATE__, __TIME__ 
//分别表示当前“文件”“行号”“函数”“日期”“时间”,示范如下:
printf("File = %s\nLine = %d\nFunc = %s\nDate = %s\nTime = %s\n", 
      __FILE__, __LINE__, __FUNCTION__, __DATE__, __TIME__);

//__VA_ARGS__ 是一个可变参数的宏,C99 规范中新增的,示范如下:
#define dev_printf(...)                      rt_kprintf(__VA_ARGS__)

#endif

//【上电初始化机制】--------------------------------------------------------------------------------------

//上电初始化模型
typedef void (*xt_init_fn_t)(void);
typedef struct xt_init_fn_tab_
{
    xt_init_fn_t p_fn; //初始化函数
    const char *desc;  //函数描述
}xt_init_fn_tab_t;

//1、内核上电初始化(如:定时器等)
#define XT_CHIP_INIT_S_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t chip_init_fn_##func XT_SECTION(".xt_section_fn.0000.00") = {func,desc}
#define XT_CHIP_INIT_1_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t chip_init_fn_##func XT_SECTION(".xt_section_fn.0000.10") = {func,desc}
#define XT_CHIP_INIT_2_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t chip_init_fn_##func XT_SECTION(".xt_section_fn.0000.20") = {func,desc}
#define XT_CHIP_INIT_3_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t chip_init_fn_##func XT_SECTION(".xt_section_fn.0000.30") = {func,desc}
#define XT_CHIP_INIT_4_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t chip_init_fn_##func XT_SECTION(".xt_section_fn.0000.40") = {func,desc}
#define XT_CHIP_INIT_5_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t chip_init_fn_##func XT_SECTION(".xt_section_fn.0000.50") = {func,desc}
#define XT_CHIP_INIT_6_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t chip_init_fn_##func XT_SECTION(".xt_section_fn.0000.60") = {func,desc}
#define XT_CHIP_INIT_7_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t chip_init_fn_##func XT_SECTION(".xt_section_fn.0000.70") = {func,desc}
#define XT_CHIP_INIT_8_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t chip_init_fn_##func XT_SECTION(".xt_section_fn.0000.80") = {func,desc}
#define XT_CHIP_INIT_9_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t chip_init_fn_##func XT_SECTION(".xt_section_fn.0000.90") = {func,desc}
#define XT_CHIP_INIT_E_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t chip_init_fn_##func XT_SECTION(".xt_section_fn.0000.99") = {func,desc}

//2、硬件上电初始化(如:IC引脚等)
#define XT_HARD_INIT_S_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t hard_init_fn_##func XT_SECTION(".xt_section_fn.0001.00") = {func,desc}
#define XT_HARD_INIT_1_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t hard_init_fn_##func XT_SECTION(".xt_section_fn.0001.10") = {func,desc}
#define XT_HARD_INIT_2_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t hard_init_fn_##func XT_SECTION(".xt_section_fn.0001.20") = {func,desc}
#define XT_HARD_INIT_3_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t hard_init_fn_##func XT_SECTION(".xt_section_fn.0001.30") = {func,desc}
#define XT_HARD_INIT_4_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t hard_init_fn_##func XT_SECTION(".xt_section_fn.0001.40") = {func,desc}
#define XT_HARD_INIT_5_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t hard_init_fn_##func XT_SECTION(".xt_section_fn.0001.50") = {func,desc}
#define XT_HARD_INIT_6_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t hard_init_fn_##func XT_SECTION(".xt_section_fn.0001.60") = {func,desc}
#define XT_HARD_INIT_7_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t hard_init_fn_##func XT_SECTION(".xt_section_fn.0001.70") = {func,desc}
#define XT_HARD_INIT_8_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t hard_init_fn_##func XT_SECTION(".xt_section_fn.0001.80") = {func,desc}
#define XT_HARD_INIT_9_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t hard_init_fn_##func XT_SECTION(".xt_section_fn.0001.90") = {func,desc}
#define XT_HARD_INIT_E_TAB_EXPORT(func,desc) XT_USED const xt_init_fn_tab_t hard_init_fn_##func XT_SECTION(".xt_section_fn.0001.99") = {func,desc}

//3、器件上电初始化(如:传感器等)
#define XT_DEV_INIT_S_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t dev_init_fn_##func XT_SECTION(".xt_section_fn.0002.00") = {func,desc}
#define XT_DEV_INIT_1_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t dev_init_fn_##func XT_SECTION(".xt_section_fn.0002.10") = {func,desc}
#define XT_DEV_INIT_2_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t dev_init_fn_##func XT_SECTION(".xt_section_fn.0002.20") = {func,desc}
#define XT_DEV_INIT_3_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t dev_init_fn_##func XT_SECTION(".xt_section_fn.0002.30") = {func,desc}
#define XT_DEV_INIT_4_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t dev_init_fn_##func XT_SECTION(".xt_section_fn.0002.40") = {func,desc}
#define XT_DEV_INIT_5_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t dev_init_fn_##func XT_SECTION(".xt_section_fn.0002.50") = {func,desc}
#define XT_DEV_INIT_6_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t dev_init_fn_##func XT_SECTION(".xt_section_fn.0002.60") = {func,desc}
#define XT_DEV_INIT_7_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t dev_init_fn_##func XT_SECTION(".xt_section_fn.0002.70") = {func,desc}
#define XT_DEV_INIT_8_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t dev_init_fn_##func XT_SECTION(".xt_section_fn.0002.80") = {func,desc}
#define XT_DEV_INIT_9_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t dev_init_fn_##func XT_SECTION(".xt_section_fn.0002.90") = {func,desc}
#define XT_DEV_INIT_E_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t dev_init_fn_##func XT_SECTION(".xt_section_fn.0002.99") = {func,desc}

//4、信息上电初始化(如:信号量等)
#define XT_MSG_INIT_S_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t msg_init_fn_##func XT_SECTION(".xt_section_fn.0003.00") = {func,desc}
#define XT_MSG_INIT_1_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t msg_init_fn_##func XT_SECTION(".xt_section_fn.0003.10") = {func,desc}
#define XT_MSG_INIT_2_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t msg_init_fn_##func XT_SECTION(".xt_section_fn.0003.20") = {func,desc}
#define XT_MSG_INIT_3_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t msg_init_fn_##func XT_SECTION(".xt_section_fn.0003.30") = {func,desc}
#define XT_MSG_INIT_4_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t msg_init_fn_##func XT_SECTION(".xt_section_fn.0003.40") = {func,desc}
#define XT_MSG_INIT_5_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t msg_init_fn_##func XT_SECTION(".xt_section_fn.0003.50") = {func,desc}
#define XT_MSG_INIT_6_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t msg_init_fn_##func XT_SECTION(".xt_section_fn.0003.60") = {func,desc}
#define XT_MSG_INIT_7_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t msg_init_fn_##func XT_SECTION(".xt_section_fn.0003.70") = {func,desc}
#define XT_MSG_INIT_8_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t msg_init_fn_##func XT_SECTION(".xt_section_fn.0003.80") = {func,desc}
#define XT_MSG_INIT_9_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t msg_init_fn_##func XT_SECTION(".xt_section_fn.0003.90") = {func,desc}
#define XT_MSG_INIT_E_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t msg_init_fn_##func XT_SECTION(".xt_section_fn.0003.99") = {func,desc}

//5、程序上电初始化(如:线程等)
#define XT_APP_INIT_S_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t app_init_fn_##func XT_SECTION(".xt_section_fn.0004.00") = {func,desc}
#define XT_APP_INIT_1_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t app_init_fn_##func XT_SECTION(".xt_section_fn.0004.10") = {func,desc}
#define XT_APP_INIT_2_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t app_init_fn_##func XT_SECTION(".xt_section_fn.0004.20") = {func,desc}
#define XT_APP_INIT_3_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t app_init_fn_##func XT_SECTION(".xt_section_fn.0004.30") = {func,desc}
#define XT_APP_INIT_4_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t app_init_fn_##func XT_SECTION(".xt_section_fn.0004.40") = {func,desc}
#define XT_APP_INIT_5_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t app_init_fn_##func XT_SECTION(".xt_section_fn.0004.50") = {func,desc}
#define XT_APP_INIT_6_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t app_init_fn_##func XT_SECTION(".xt_section_fn.0004.60") = {func,desc}
#define XT_APP_INIT_7_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t app_init_fn_##func XT_SECTION(".xt_section_fn.0004.70") = {func,desc}
#define XT_APP_INIT_8_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t app_init_fn_##func XT_SECTION(".xt_section_fn.0004.80") = {func,desc}
#define XT_APP_INIT_9_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t app_init_fn_##func XT_SECTION(".xt_section_fn.0004.90") = {func,desc}
#define XT_APP_INIT_E_TAB_EXPORT(func,desc)  XT_USED const xt_init_fn_tab_t app_init_fn_##func XT_SECTION(".xt_section_fn.0004.99") = {func,desc}

//定时调用函数模型
typedef void (*xt_tloop_fn_t)(void);
typedef struct xt_tloop_fn_tab_
{
    xt_tloop_fn_t p_fn; //被定时调用的函数
    uint32_t ms;        //间隔时间(毫秒),受系统节拍限制,还要求被 4000320000UL 整除!
}xt_tloop_fn_tab_t;

//6、定时调用函数(如:扫描等)
#define XT_APP_TLOOP_S_TAB_EXPORT(func,ms)   XT_USED const xt_tloop_fn_tab_t app_tloop_fn_##func XT_SECTION(".xt_section_fn.1000.00") = {func,ms}
#define XT_APP_TLOOP_1_TAB_EXPORT(func,ms)   XT_USED const xt_tloop_fn_tab_t app_tloop_fn_##func XT_SECTION(".xt_section_fn.1000.10") = {func,ms}
#define XT_APP_TLOOP_2_TAB_EXPORT(func,ms)   XT_USED const xt_tloop_fn_tab_t app_tloop_fn_##func XT_SECTION(".xt_section_fn.1000.20") = {func,ms}
#define XT_APP_TLOOP_3_TAB_EXPORT(func,ms)   XT_USED const xt_tloop_fn_tab_t app_tloop_fn_##func XT_SECTION(".xt_section_fn.1000.30") = {func,ms}
#define XT_APP_TLOOP_4_TAB_EXPORT(func,ms)   XT_USED const xt_tloop_fn_tab_t app_tloop_fn_##func XT_SECTION(".xt_section_fn.1000.40") = {func,ms}
#define XT_APP_TLOOP_5_TAB_EXPORT(func,ms)   XT_USED const xt_tloop_fn_tab_t app_tloop_fn_##func XT_SECTION(".xt_section_fn.1000.50") = {func,ms}
#define XT_APP_TLOOP_6_TAB_EXPORT(func,ms)   XT_USED const xt_tloop_fn_tab_t app_tloop_fn_##func XT_SECTION(".xt_section_fn.1000.60") = {func,ms}
#define XT_APP_TLOOP_7_TAB_EXPORT(func,ms)   XT_USED const xt_tloop_fn_tab_t app_tloop_fn_##func XT_SECTION(".xt_section_fn.1000.70") = {func,ms}
#define XT_APP_TLOOP_8_TAB_EXPORT(func,ms)   XT_USED const xt_tloop_fn_tab_t app_tloop_fn_##func XT_SECTION(".xt_section_fn.1000.80") = {func,ms}
#define XT_APP_TLOOP_9_TAB_EXPORT(func,ms)   XT_USED const xt_tloop_fn_tab_t app_tloop_fn_##func XT_SECTION(".xt_section_fn.1000.90") = {func,ms}
#define XT_APP_TLOOP_E_TAB_EXPORT(func,ms)   XT_USED const xt_tloop_fn_tab_t app_tloop_fn_##func XT_SECTION(".xt_section_fn.1000.99") = {func,ms}

#ifdef __cplusplus
	}
#endif

#endif  //#ifndef XT_COMDEF_H__

预处理应用

1、条件判断

主要包括 #if#elif#else#ifdef#ifndef#endif,其使用需以 #define(定义宏)和 #undef(取消宏定义)为前提,用于实现宏条件编译相关逻辑。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <stdio.h> ///////////////////////// <- 使用标准输入输出,如:sprintf(p,...) 等

int main(int argc, char *argv[])
{
	#define XXXX 11

	#ifdef XXXX
	printf("01. #ifdef XXXX\n"); 
	#endif

	#ifndef YYYY
	printf("02. #ifndef YYYY\n"); 
	#endif

	#if (defined XXXX) && !(defined YYYY)
	printf("03. #if (defined XXXX) && !(defined YYYY)\n"); 
	#endif

	#undef XXXX
	#define XXXX 22
	printf("04. #undef XXXX\n");
	printf("05. #define XXXX 22\n");

	#if (XXXX == 22)
	printf("06. #if (XXXX == 22)\n"); 
	#endif

	#if (XXXX == 11)
	printf("07. #if (XXXX == 11)\n"); 
	#elif (XXXX == 22)
	printf("08. #elif (XXXX == 22)\n"); 
	#endif

	#if (XXXX == 33)
	printf("09. #if (XXXX == 33)\n"); 
	#else
	printf("10. #else\n"); 
	#endif

	// etchar(); //等待键盘输入,
	return 0;    //有输入就退出。
}

// 输出结果:(由gcc编译)
// 01. #ifdef XXXX
// 02. #ifndef YYYY
// 03. #if (defined XXXX) && !(defined YYYY)
// 04. #undef XXXX
// 05. #define XXXX 22
// 06. #if (XXXX == 22)
// 08. #elif (XXXX == 22)
// 10. #else
2、错误警告

主要包括 #error#warning,二者用于在编译器编译阶段主动输出错误或警告信息,帮助开发者快速定位代码问题。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h> ///////////////////////// <- 使用标准输入输出,如:sprintf(p,...) 等

int main(int argc, char *argv[])
{
	#define XXXX 11

	#if (XXXX != 22)
	#warning 警告:XXXX != 22
	#endif
	printf("run 22\n"); 

	#if (XXXX != 33)
	#error 错误:XXXX != 33
	#endif
	printf("run 33\n"); 

	// #if (XXXX != 44)
	// #warning 警告:XXXX != 44
	// #endif
	// printf("run 44\n"); 

	// etchar(); //等待键盘输入,
	return 0;    //有输入就退出。
}

// 编译结果:(由gcc编译)
// test.c: In function ‘main’:
// test.c:8:3: warning: #warning 警告:XXXX != 22 [-Wcpp]
//     8 |  #warning 警告:XXXX != 22
//       |   ^~~~~~~
// test.c:13:3: error: #error 错误:XXXX != 33
//    13 |  #error 错误:XXXX != 33
//       |   ^~~~~
3、文件名/行号

主要包括 #line 指令,标准语法为 #line 行号 "文件名"(行号必填),用于重定义当前文件的行号与文件名。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h> ///////////////////////// <- 使用标准输入输出,如:sprintf(p,...) 等

int main(int argc, char *argv[])
{
	// 打印原始信息(验证)
	printf("原始文件行号:行号=%-3d, 文件=%s\n", __LINE__, __FILE__);  // 输出   6, test.c

	// 第一步:修改行号和文件名(模拟代码生成场景)
	#line 100 "code.txt"
	printf("修改后当前行:行号=%-3d, 文件=%s\n", __LINE__, __FILE__);  // 输出 100, code.txt
	printf("修改后下一行:行号=%-3d\n", __LINE__);                     // 输出 101

	// 第二步:手动还原原始行号和文件名
	#line 15 "test.c"
	printf("恢复后当前行:行号=%-3d, 文件=%s\n", __LINE__, __FILE__);  // 输出  15, test.c(注:实际是还原后的逻辑行号)
	printf("恢复后下一行:行号=%-3d\n", __LINE__);                     // 输出  16        (按原始规则递增)

	// etchar(); //等待键盘输入,
	return 0;    //有输入就退出。
}

// 输出结果:(由gcc编译)
// 原始文件行号:行号=6  , 文件=test.c
// 修改后当前行:行号=100, 文件=code.txt
// 修改后下一行:行号=101
// 恢复后当前行:行号=15 , 文件=test.c
// 恢复后下一行:行号=16 
4、字符拼接

在 C 语言预处理阶段,#(字符串化运算符)与 ##(连接运算符)是两类特殊的预处理操作符,其核心特性会阻止参数在预处理阶段的递归扩展—— 这一特性要求开发者在实际应用时需重点关注参数传递规则,避免因未预期的宏替换行为导致语法错误或逻辑异常。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h> ///////////////////////// <- 使用标准输入输出,如:sprintf(p,...) 等

int main(int argc, char *argv[])
{
	// # 使用++++++++++++++++++++++++++
	#define S          abcd
	#define _STR(s)    #s                // 例如:_STR(hello) 代表 "hello",即多了双引号,变为字符串
	#define STR(s)     _STR(s)
	printf("1. %s\n", _STR(S));          //【错误】预想展开为:abcd, 但实际为:S(即 # 会阻参数展开)
	printf("2. %s\n",  STR(S));          //【正确】正常展开为:abcd

	// # ## 使用+++++++++++++++++++++++
	#define A          2
	#define _CONS(a,b) 0x##a##e##b
	#define CONS(a,b)  _CONS(a,b)
	printf("3. %s\n", STR(_CONS(A,A)));  //【错误】预想展开为:0x2e2, 但实际为:0xAeA(即 ## 会阻参数展开)
	printf("4. %s\n", STR( CONS(A,A)));  //【正确】正常展开为:0x2e2
	printf("5. %d\n",      CONS(A,A) );
}

// 输出结果:(由gcc编译)
// 1. S
// 2. abcd
// 3. 0xAeA
// 4. 0x2e2
// 5. 738
5、头文件应用技巧

5.1、头文件一般格式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
  * @file  xt_key.h
  * @brief 按键头文件
  * COPYRIGHT (C) 2022, XT 技术联合小组
  * Change Logs:
  * Date           Author       Notes
  * 2022-11-08     o2ospring    原始版本
  */
#ifndef XT_KEY_H__
#define XT_KEY_H__

#include <stdint.h> //////////////////////// <- 使用的数据定义,如: int8_t, uint32_t 等

#ifdef __cplusplus
extern "C" {
#endif

......

#ifdef __cplusplus
	}
#endif

#endif  //#ifndef XT_KEY_H__

5.2、头文件里声明变量技巧

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 1、在 c 文件最前面
#define   XT_KEY_C__
#include "xt_key.h"

// 2、在 h 头文件中声明
#ifdef XT_KEY_C__
KEY_EXT
#else
KEY_EXT extern
#endif

KEY_EXT uint8_t key;         //声明一个变量,模块内外都可读写

#ifdef XT_KEY_C__
ABC_EXT uint8_t key2 = 0xFF; //声明一个变量,模块内可读写
#else
ABC_EXT const uint8_t key2;  //声明一个变量,模块外部只读
#endif

5.3、内联函数(内嵌函数)

__inline(内联/内嵌函数)在编译时会将函数代码直接嵌入主调函数中(而非调用),属于“空间换时间”的实现方式,效果类似宏定义代码;其中static __inline修饰的内联函数不会生成函数本身的代码,仅嵌入调用处,因此实际中常用该写法。此外,__inline是 C99 新增关键字(C89 不支持),部分编译器支持度不一,通常会用宏来定义:do {……} while(0)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 1、在 a.h 头文件声明
static __inline int add(int a, int b)  // 定义在头文件,加 static 避免多文件重复定义
{
    return a + b;
}

// 2、在 c 文件调用
#include "a.h"
int main(int argc, char *argv[])
{
    add(1, 2);  // 编译器能看到定义,可内联
}

● 根据编译器/环境判断是否支持 inline 的使用技巧

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 1、在 common.h 通用头文件进行兼容性定义
#ifdef _MSC_VER                               // 微软MSVC编译器
#define INLINE static __inline
#elif defined(__GNUC__) || defined(__clang__) // GCC/Clang
#define INLINE static __inline__
#elif __STDC_VERSION__ >= 199901L             // 标准C99
#define INLINE static inline
#else                                         // 无内联支持
#define INLINE static
#endif

// 2、在 a.h 头文件声明
INLINE int add(int a, int b)
{
    return a + b;
}

// 3、在 c 文件调用
#include "a.h"
int main(int argc, char *argv[])
{
    add(1, 2);  // 编译器能看到定义,可内联
}

指针与地址

1、数组及其地址
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdio.h> ///////////////////////// <- 使用标准输入输出,如:sprintf(p,...) 等
#include <stdint.h> //////////////////////// <- 使用的数据定义,如: int8_t, uint32_t 等

int main(int argc, char *argv[])
{
	uint32_t a[10];

	printf("   a:%p\n", a);     //a    -> a 只是首个元素a[0]的地址 -> 0x7fff88892960
	printf("  &a:%p\n", &a);    //&a   -> &a 整个数组a[10]的首地址 -> 0x7fff88892960
	printf(" a+1:%p\n", a+1);   //a+1  -> &a[0] + sizeof(a[0])     -> 0x7fff88892964
	printf("&a+1:%p\n", &a+1);  //&a+1 -> &a + sizeof(a)           -> 0x7fff88892988
}

// 输出结果:(由gcc编译)
//    a:0x7fff88892960
//   &a:0x7fff88892960
//  a+1:0x7fff88892964
// &a+1:0x7fff88892988
2、结构体指针
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
struct obj_stu_
{
	char name[20];
	long num;
};
struct obj_stu_ student_1;    //p = &student_1;  (注:*p 代表结构体 student_1)
struct obj_stu_ students[4];  //p = students;(注:*(p+i) 代表结构体 students[i])(students 与 &students[0] 等价)
struct obj_stu_ *p = students;

// 1、指针方式访问 (students 代表 &students[0],即 p 与 students 等价)
       (p+i)->name;        (p+i)->name[0];        (p+i)->num;
(students+i)->name; (students+i)->name[0]; (students+i)->num; 

// 2、结构体方式访问(students 代表 &students[0],即 p 与 students 等价)
students[i].name; students[i].name[0]; students[i].num;
       p[i].name;        p[i].name[0];        p[i].num;
   (*(p+i)).name;    (*(p+i)).name[0];    (*(p+i)).num;  //(注:*(p+i) 代表结构体 students[i]) 
3、函数指针
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 1、【函数指针】的定义(变量)
int (*p_max_fn)(int a, int b);  //函数指针定义(p_max_fn:变量)
int max(int a, int b)           //函数
{
	return (a > b ? a : b);
}
p_max_fn = &max;                //也可以写成: p_max_fn = max;
(*p_max_fn)(a, b);              //利用函数指针调用函数(注:*p_max_fn 代表函数 max)

// 2、【函数指针类型】的定义(类型)
typedef int (*p_max_fn_t)(int a, int b);
p_max_fn_t ff(int n);           //ff 是返回函数指针的函数(ff:函数)
p_max_fn_t p_fn;                //定义函数指针: p_fn 为指针变量(p_fn:变量)

//3、【函数类型】的定义(类型)
typedef int max_fn_t(int a, int b);
max_fn_t fn;                    //fn 用于文件的函数声明(fn:函数)
max_fn_t *p_fn;                 //p_fn 是一个函数指针变量(p_fn:变量)

函数指针高级应用,利用函数指针实现命令行调试工具

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// 调试命令表项
#include <stdio.h> ///////////////////////// <- 使用标准输入输出,如:sprintf(p,...) 等
#include <string.h> //////////////////////// <- 使用的字符处理,如: strcpy(), memcpy() 等
#include <stdlib.h> //////////////////////// <- 使用到字符转换,如:atoi(s) 等

// 1. 定义函数指针类型
typedef int (*debug_cmd_func_t)(int argc, char *argv[]);

// 2. 定义命令表类型
typedef struct
{
	const char *cmd_name;       // 命令名(如 "read_mem"、"write_reg")
	const char *cmd_help;       // 命令说明(如 "read_mem <addr> - 读取指定地址的内存")
	debug_cmd_func_t handler;   // 命令处理函数指针(空指针=未实现)

} debug_cmd_table_t;

// 3. 命令表(新增命令只需加表项)
static const debug_cmd_table_t debug_cmd_table[] =
{
	{"read_mem" , "read_mem <addr> [len] - 读取指定地址的内存", read_mem_handler },
	{"write_reg", "write_reg <reg> <val> - 写入寄存器值"      , write_reg_handler},
	{"help"     , "help - 显示所有命令"                       , help_handler     },
	{"exit"     , "exit - 退出调试"                           , exit_handler     },
	{NULL       , NULL                                        , null_cmd_handler }  // 表尾哨兵(默认处理)
};

// 4. 分割命令行参数(简化版,按空格分割)
static int split_cmd(char *input, char *argv[], int max_argc)
{
	int argc = 0;
	char *p_token = strtok(input, " \t\n");  //找出一个命令行分割(字符串)的起始地址
	while ((p_token != NULL) && (argc < max_argc-1))
	{
		argv[argc++] = p_token;
		p_token = strtok(NULL, " \t\n");     //找出一个命令行分割(字符串)的起始地址
	}
	argv[argc] = NULL; // 终止符
	return argc;       // 命令行分割的段数(即有几个 token)(每个命令行分割(字符串)的起始地址为 argv[?])
}

// 5. 查找命令对应的处理函数
static debug_cmd_func_t find_cmd_handler(const char *cmd)
{
	for (int i=0; debug_cmd_table[i].cmd_name!=NULL; i++)
	{
		if (strcmp(cmd, debug_cmd_table[i].cmd_name) == 0)
		{
			// 找出命令对应的函数(若函数指针为空,返回默认空处理)
			return debug_cmd_table[i].handler ? debug_cmd_table[i].handler : null_cmd_handler;
		}
	}
	// 未知命令,返回默认空处理
	return null_cmd_handler;
}

// 6. 调试主循环
void debug_shell(void)
{
	char input[256];
	char *argv[16]; // 最多16个参数
	int argc;

	printf("===== 命令行调试工具 =====\n");
	printf("输入 help 查看命令,exit 退出\n");

	while (1)
	{
		// 读取输入
		…… 以换行或回车作为一行命令的结束标志

		// 分割参数
		argc = split_cmd(input, argv, sizeof(argv)/sizeof(argv[0]));
		if (argc == 0) continue; // 空输入,跳过并进入重新等待输入

		// 查找处理函数并调用
		debug_cmd_func_t handler = find_cmd_handler(argv[0]);
		handler(argc, argv);     // 核心:调用函数指针
	}
}

PS:针对单片机等轻量级嵌入式应用场景,可先将 debug_cmd_func_t 函数类型定义改为空指针(如:typedef void* debug_cmd_func_t);在定位到具体函数调用处时,通过 switch-case 分支结构调用参数个数不同的函数,参数传递直接使用原始数据即可(例如统一以 uint32_t 类型传递)。

4、少字节数据向多字节数据赋值

总原则:
『少字节:负数』赋值『多字节:有符号变量』才会【多出的高位填充1】,其它情况都是【多出的高位填充0,即直接赋值】。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h> ///////////////////////// <- 使用标准输入输出,如:sprintf(p,...) 等
#include <stdint.h> //////////////////////// <- 使用的数据定义,如: int8_t, uint32_t 等

int main(int argc, char *argv[])
{
	int32_t ii;               //有符号数
	int8_t  s8 = -1;          //有符号数(寄存值0xFF)
	uint8_t u8 = 0xFF;        //无符号数(寄存值0xFF)

	// 1、【高位填充1】, 用于有符号数赋值: 【『少字节』有符号[负数]】 ─赋值→ 【『多字节』有符号数】
	ii = s8;                  //则ii为0xFFFFFFFF,即也是-1;
	printf("s8 = %d(0x%X)\n", s8, (uint8_t)s8);
	printf("ii = 0x%08X\n", (uint32_t)ii);

	//                                                                          ↓↓↓↓↓↓↓↓↓↓
	// 2、【高位填充0】, 用于原始数据赋值: 【『少字节』有符号[正数]】 ─赋值→ 【『多字节』有符号数】
	//                                     【『少字节』无符号任意数】 ─赋值→ 【『多字节』有符号数】
	//                                     【『少字节』任意数      】 ─赋值→ 【『多字节』无符号数】
	ii = (unsigned long)(u8); //则i为0x000000FF,数学值为255;
	printf("u8 = %d(0x%X)\n", u8, (uint8_t)u8);
	printf("ii = 0x%08X\n", (uint32_t)ii);
}

// 输出结果:(由gcc编译)
// s8 = -1(0xFF)
// ii = 0xFFFFFFFF
// u8 = 255(0xFF)
// ii = 0x000000FF
5、结构体元素/地址对齐

3.1、变量地址单字节对齐(常用于二进制指令定义)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#pragma pack(push,1)      //可 1,2,4,8 对齐,(push)与(pop)要配对, 可以嵌套

typedef struct xt_test_
{
	uint8_t   a; //&0 --+
	uint32_t  b; //&1 --+-- 共 7 字节
	uint16_t  c; //&5 --+
}xt_test_t;

#pragma pack(pop)

3.2、起始地址多字节对齐(针对处理器硬件的快速正确访问)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <stdio.h> ///////////////////////// <- 使用标准输入输出,如:sprintf(p,...) 等
#include <stdint.h> //////////////////////// <- 使用的数据定义,如: int8_t, uint32_t 等

int main(int argc, char *argv[])
{
	typedef struct obj_1_
	{
		uint16_t a; //&0 --+
		uint8_t  b; //&2 --+-- 大小共 4 字节【与处理器和系统有关】

	}obj_1_t __attribute__((__aligned__(64))); //1.影响『单体结构体』的【起始地址】,
											   //  因`obj_1_t`视为没有包含地址对齐修饰字段,只能【临时性】对齐修饰一次,意味着不能用于[数组的组员]
	typedef struct obj_n_
	{
		uint16_t a; //&0 --+
		uint8_t  b; //&2 --+-- 大小共 64 字节【由__aligned__决定】

	}__attribute__((__aligned__(64))) obj_n_t; //2.影响结构体『每个组员』的【起始地址】和【大小】,
											   //  因`obj_n_t`实际上有包含地址对齐修饰字段,则可【全局性】对齐修饰多次,也就是说可用于[数组的组员]
	typedef struct obj_x_
	{
		uint16_t a; //&0 --+
		uint8_t  b; //&2 --+-- 大小共 4 字节【与处理器和系统有关】

	}obj_x_t;

	obj_1_t aaaaaa; //1.影响『单体结构体』的起始地址对齐,不能用于数组
	obj_n_t bbb[6]; //2.影响结构体『每个组员』的起始地址对齐和大小限制
					//3.影响『数组整体』的起始地址,但不影响〖其它组员〗的起始地址和大小 ─┐
	__attribute__((__aligned__(64))) struct obj_1_ ccc[6]; //1.                           │
	__attribute__((__aligned__(64))) struct obj_n_ ddd[6]; //2.                           │
	__attribute__((__aligned__(64))) struct obj_x_ xxx[6]; //3.←──────────────────────────┘

	printf("aaaaaa: addr=%p, size=%d\n", &aaaaaa, (int32_t)(sizeof(aaaaaa)));
	printf("bbb[0]: addr=%p, size=%d\n", &bbb[0], (int32_t)(sizeof(bbb[0])));
	printf("ccc[0]: addr=%p, size=%d\n", &ccc[0], (int32_t)(sizeof(ccc[0])));
	printf("ddd[0]: addr=%p, size=%d\n", &ddd[0], (int32_t)(sizeof(ddd[0])));
	printf("xxx[0]: addr=%p, size=%d\n", &xxx[0], (int32_t)(sizeof(xxx[0])));
	printf("aaaaaa: size=%d\n",                   (int32_t)(sizeof(aaaaaa)));
	printf("   bbb: size=%d\n",                   (int32_t)(sizeof(bbb)));
	printf("   ccc: size=%d\n",                   (int32_t)(sizeof(ccc)));
	printf("   ddd: size=%d\n",                   (int32_t)(sizeof(ddd)));
	printf("   xxx: size=%d\n",                   (int32_t)(sizeof(xxx)));
}

// 输出结果:(由gcc编译)
// aaaaaa: addr=0x7fff2de75bc0, size=4
// bbb[0]: addr=0x7fff2de75c80, size=64
// ccc[0]: addr=0x7fff2de75c00, size=4
// ddd[0]: addr=0x7fff2de75e00, size=64
// xxx[0]: addr=0x7fff2de75c40, size=4
// aaaaaa: size=4
//    bbb: size=384
//    ccc: size=24
//    ddd: size=384
//    xxx: size=24

PS:关于地址对齐更多知识,请稳步【attribute-aligned 编译属性-地址对齐】。

打印与格式

1、打印函数

1.1、打印函数与头文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <stdio.h>
int printf(const char *format, ...);                                   //输出到标准输出
int fprintf(FILE *stream, const char *format, ...);                    //输出到文件
int sprintf(char *str, const char *format, ...);                       //输出到字符串str中
int snprintf(char *str, size_t size, const char *format, ...);         //按size大小输出到字符串str中

#include <stdarg.h>
void va_start(va_list ap, last); //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
int vprintf(const char *format, va_list ap);                           //输出到标准输出
int vfprintf(FILE *stream, const char *format, va_list ap);            //输出到文件
int vsprintf(char *str, const char *format, va_list ap);               //输出到字符串str中
int vsnprintf(char *str, size_t size, const char *format, va_list ap); //按size大小输出到字符串str中
void va_end(va_list ap);         //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

1.2、自定义打印函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

void my_printf(const char *fmt, ...)
{
	va_list valist;
	unsigned int length;
	#define PRINTFBUF_SIZE           1024
	static char printf_buf[PRINTFBUF_SIZE];

	va_start(valist, fmt); //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

	/* 方式一:*/
	length = vsnprintf(printf_buf, sizeof(printf_buf) - 1, fmt, valist);
	/* 方式二: *//*
	switch(*fmt++)
	{
		char *p = NULL;
		int   i = 0;
		char  c = 0;
		case 'd': i =       va_arg(valist, int   ); break; //d% 类型
		case 's': p =       va_arg(valist, char *); break; //s% 类型
		case 'c': c = (char)va_arg(valist, int   ); break; //c% 类型
		……
	}*/

	if (length > PRINTFBUF_SIZE - 1)
	{
		length = PRINTFBUF_SIZE - 1;
	}
	uart_send(printf_buf, length); //通过串口把格式化后的数据发送出去

	va_end(valist);        //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}
2、调试信息

主要包括 __FILE____LINE____FUNCTION____DATE____TIME____VER__ 这些预定义宏;其中 __VER__ 用于标识 IDE 版本信息,数据类型为整型。需注意:部分编译器可能会将上述预定义宏以小写形式定义(例如 __file__)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h> ///////////////////////// <- 使用标准输入输出,如:sprintf(p,...) 等

void test_func(void)
{
	printf("File = %s\nLine = %d\nFunc = %s\nDate = %s\nTime = %s\n", 
	       __FILE__, __LINE__, __FUNCTION__, __DATE__, __TIME__); //在第6行
}

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

	getchar(); //等待键盘输入,
	return 0;  //有输入就退出。
}

// 输出结果:(由VC2010编译)
// File = c:\test\test__func__.cpp
// Line = 6
// Func = test_func
// Date = Sep  7 2012
// Time = 00:15:42
3、变参数宏

主要由 C99 规范提供的__VA_ARGS__宏来实现,注意 C89 不支持!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <stdio.h> ///////////////////////// <- 使用标准输入输出,如:sprintf(p,...) 等
#include <stdint.h> //////////////////////// <- 使用的数据定义,如: int8_t, uint32_t 等

int main(int argc, char *argv[])
{
	#define sensor_printf(...)  printf("sensor: " __VA_ARGS__)  //由C99规范提供支持(C89不支持)
	#define sensor_printf2(...) printf(__VA_ARGS__)             //由C99规范提供支持(C89不支持)

	int32_t temp_val = 25;

	sensor_printf("temp = %d℃\n", temp_val);
	sensor_printf2("temp = %d℃\n", temp_val);
}

// 输出结果:(由gcc编译)
// sensor: temp = 25℃
// temp = 25℃
4、字符编码
1
2
3
4
5
6
7
// 语法:
"ABC"   //以ANSI编码或文件编码决定(即:实际是根据文件编码及系统语言决定,并非一定是ANSI编码)
L"ABC"  //以小端Unicode编码(引号前面的大写字母L)

// 案例:
printf("ABC");
printf(L"ABC");
5、数值格式
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
%d      //输出有符号十进整数。
%u      //输出无符号十进整数。
%o      //输出八进制数整数。
%x      //输出十六进制整数(小写)。
%X      //输出十六进制整数(大写)。
%c      //输出一个字符。
%s      //输出一个字符串。
%f      //输出浮点型。
%e      //输出指数形式实数。
%g      //根据大小自动选f格式或e格式,且不输出无意义的零。
%p      //指针(以十六进制整数打印(包括0x字符),32位机制则打印4字节数值,64位机制则打印8字节数值)

%ld     //输出有符号十进长整数。
%lu     //输出无符号十进长整数。
%lf     //输出双精度浮点型。
%5.3f   //输出宽度为5(包含小数点),小数点后面是三位。比如[3.1415926]在[%5.3f]的情况下会自动转换成3.141。

%2x     //输出2位十六进制整数(小写)....
%2X     //输出2位十六进制整数(大写)....
%2d     //输出2位有符号十进整数, 不足2位时[前面]补空格(即:右对齐) ....其它数据格式类同
%-2d    //输出2位有符号十进整数, 不足2位时[后面]补空格(即:左对齐) ....其它数据格式类同
%02d    //输出2位有符号十进整数, 不足2位时[自动]补0  .................其它数据格式类同

%8.3f   //输出宽度为8(包含小数点),小数点后面是三位。 12.34->"  12.340"
%08.03f //输出宽度为8(包含小数点),小数点后面是三位。 12.34->"0012.340"

%%      //输出"%"符号
\\      //输出"\"符号
\x      //直接填16进制数。比如 \xFF

\r      //回车
\n      //换行
6、断言打印
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 断言判断语句
#define O2O_ASSERT(expression) ((expression) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__))

// 断言打印函数
extern void assert_failed(uint8_t *file, uint32_t line);

void assert_failed(uint8_t *file, uint32_t line)
{
	printf("assert_failed: File = %s, Line = %d\n", file, line);
}

缩写字典

1、常用编程单词缩写字典
字母 英文描述 标准缩写 备注(使用场景/说明)
A Average Avg 平均值计算(如传感器数据均值)
Addition Add 加法运算、数据新增
Accumulator Acc 累加器(硬件/算法场景)
Address Addr 地址(内存地址、网络地址)
Action Act 动作、操作(接口/函数命名)
Active Actv 激活状态(修正原重复缩写,与Action区分)
Amplitude Amp 振幅(传感器、信号处理)
Analog Input AI 模拟输入(硬件I/O)
Analog I/O AIO 模拟输入输出(嵌入式硬件)
All All 全部、所有(参数/变量命名)
Alarm Alm 报警(设备状态提示)
Allocate Alloc 分配(内存、资源分配)
Analog Output AO 模拟输出(硬件I/O)
Apparent App 表观的(如表观功率)
Argument Arg 参数(函数参数)
Arrange Arrng 排列、配置(数据/硬件布局)
Array Arr 数组(简化原全称,代码命名常用)
Assemble Asm 汇编(汇编语言、编译过程)
Attribute Attr 属性(简化原Attrib,更通用)
B Back Bk 后退、背面(如备份、回退功能)
Background Bg 后台(进程、任务)
Break Brk 中断、跳出(循环/程序流程)
Bar Bar 条、棒(图表、硬件组件)
Bit Bit 位(二进制单位)
Block Blk 块(数据块、内存块)
Buffer Buf 缓冲区(数据缓存)
Button Btn 按钮(硬件按键、UI组件)
Bypass Bypass 旁路(硬件电路、流程跳过)
C Calibration Cal 校准(传感器、设备校准)
Calculate Calc 计算(算法、数据处理)
Configuration Cfg 配置(设备、程序配置)
Channel Ch 通道(通信、信号通道)
Change Chg 改变、修改(状态/数据变更)
Check Chk 检查(校验、状态检测)
Clock Clk 时钟(硬件时钟、定时器)
Clear Clr 清除(数据、状态清零)
Clear Screen Cls 清屏(控制台操作)
Command Cmd 命令(指令、控制命令)
Compare Cmp 比较(数据比较、条件判断)
Complete Comp 完成(任务、流程结束)
Count Cnt 计数(数量统计)
Counter Ctr 计数器(硬件/软件计数器)
Column Col 列(表格、数组列)
Communication Comm 通信(设备间通信)
Connect Conn 连接(简化原Con,更规范)
Construct Cons 构造(对象构造、结构创建)
Control Ctrl 控制(控制逻辑、硬件控制)
Context Ctx 上下文(程序上下文、环境变量)
Convert Conv 转换(数据格式、单位转换)
Copy Cp 复制(文件、数据拷贝)
Current Cur 当前(当前值、当前状态)
Cursor Csr 光标(控制台、UI光标)
Control Word CW 控制字(硬件控制寄存器)
Color Clr 颜色(统一缩写,去除歧义)
D Data Data 数据(全称更通用,保留缩写Dat备用)
Date Date 日期(时间相关)
Day Day 日(时间单位)
Day-of-week DOW 星期(时间格式)
Delay Dly 延迟(定时延迟、等待时间)
Debounce Deb 消抖(简化原全称,硬件按键消抖常用)
Decrease Dec 递减(数值减少)
Decimal Dec 十进制(数值格式,与Decrease根据上下文区分)
Decode Decode 解码(数据、信号解码)
Define Def 定义(宏定义、常量定义)
Degree Deg 度(角度、温度单位)
Delete Del 删除(数据、文件删除)
Destination Dst 目标(目标地址、目标设备)
Descriptor Desc 描述符(数据描述、设备描述符)
Device Dev 设备(硬件设备)
Discrete Input DI 离散输入(硬件I/O)
Digit Dig 数字(位、数字量)
Discrete I/O DIO 离散输入输出(嵌入式硬件)
Discrete Output DO 离散输出(硬件I/O)
Disable Dis 禁用(功能、设备禁用)
Display Disp 显示(屏幕、显示模块)
Discovery Disc 发现(设备发现、服务发现)
Division Div 除法、除数(运算相关)
Document Doc 文档(文件、说明文档)
Down Down 向下、关闭(状态、方向)
Dummy Dummy 占位、虚拟(测试数据、虚拟设备)
Dynamic Dyn 动态(动态配置、动态内存)
E Edge Edge 边沿(信号边沿、触发边沿)
Edit Edt 编辑(数据、文件编辑)
Effective Eff 有效(有效值、有效状态)
Electric Elec 电气(电气设备、电路)
Empty Empty 空(状态判断)
Enable En 启用(功能、设备启用)
Engine Eng 引擎(核心逻辑、驱动引擎)
Enter Enter 进入、输入(操作指令)
Entries Entries 条目(数据条目)
Equivalent Equiv 等效(等效电路、等效值)
Error Err 错误(错误码、异常状态)
Ethernet Eth 以太网(网络接口)
Engineering Units EU 工程单位(传感器测量单位)
Event Event 事件(触发事件、中断事件)
Extension Ext 扩展(功能扩展、文件扩展名)
Exit Exit 退出(程序、函数退出)
Exception Exc 异常(程序异常、异常处理)
Expiration Exp 过期(时间过期)
Exponent Exp 指数(数学运算,与Expiration根据上下文区分)
F Field Fld 字段(数据字段、寄存器字段)
Flag Flg 标志位(状态标志、标志位)
Flush Flush 刷新(缓冲区刷新)
Function Func 函数(简化原Fnct,更通用)
Format Frm 格式(数据格式、文件格式)
Fraction Fract 分数、小数(数值类型)
Free Free 空闲(内存空闲、资源空闲)
Frequency Freq 频率(信号频率、采样频率)
Full Full 满(状态判断)
G Gain Gain 增益(放大器增益、信号增益)
Get Get 获取(数据获取、接口调用)
Generate Gen 生成(数据生成、信号生成)
Grid Grd 网格(布局、数据网格)
Group Grp 组(设备组、数据组)
H Handler Handler 处理器(中断处理、事件处理)
Harmonic Harm 谐波(电力、信号谐波)
Hexadecimal Hex 十六进制(数值格式)
High Hi 高(电平、优先级)
History Hist 历史(历史数据、日志)
Hit Hit 命中(缓存命中、触发命中)
High Priority Task HPT 高优先级任务(实时系统)
Hour Hr 小时(时间单位)
I Identification Id 标识(设备ID、用户ID)
Idle Idle 空闲(设备空闲、线程空闲)
Impulse Imp 脉冲(脉冲信号)
Image Img 图像(图像数据、显示图像)
Increment Inc 递增(数值增加)
Information Info 信息(状态信息、提示信息)
Initialization Init 初始化(程序、设备初始化)
Insert Ins 插入(数据插入、文件插入)
Input In 输入(数据输入、硬件输入)
Instruction Instr 指令(CPU指令、控制指令)
Interrupt Int 中断(硬件中断、软件中断)
Invert Inv 反转(信号反转、状态反转)
Interrupt Service Routine ISR 中断服务程序(嵌入式开发核心)
Index Idx 索引(数组索引、数据索引,修正原Ix更通用)
J Jump Jmp 跳转(程序跳转、指令跳转)
Join Join 连接(线程连接、数据合并)
K Key Key 键(键盘按键、密钥)
Keyboard Kbd 键盘(输入设备,修正原Kdb更规范)
Kilo K 千(单位前缀,如Kb、KHz)
L Label Lab 标签(变量标签、设备标签)
Length Len 长度(数据长度、数组长度)
List Lst 列表(数据列表,统一缩写去除歧义)
Library Lib 库(函数库、静态库)
Limit Lim 限制(上限、下限)
Low Lo 低(电平、优先级)
Lower Lower 小写、下方(状态转换,与Lo区分)
Lock Lock 锁定(资源锁定、互斥锁)
Low Priority Task LTP 低优先级任务(实时系统)
M Manager Mgr 管理器(设备管理器、资源管理器,统一缩写)
Magnitude Mag 幅值(信号幅值、数值大小)
Mantissa Mant 尾数(浮点数值组成部分,与Manual区分)
Manual Man 手动(手动模式、手动控制)
Manufacture Mfg 制造(制造商、生产信息)
Maximum Max 最大值(上限值)
Mailbox Mbox 邮箱(进程间通信、消息队列)
Minimum Min 最小值(下限值)
Mode Mode 模式(工作模式、运行模式)
Month Month 月(时间单位)
Move Mov 移动(数据移动、设备移动)
Message Msg 消息(通信消息、日志消息)
Measure Meas 测量(传感器测量、数据采集)
Mask Msk 掩码(位掩码、权限掩码)
Multiplication Mul 乘法(运算操作)
Multiplex Mux 多路复用(硬件多路器、信号复用)
Make Mk 创建(文件创建、对象创建)
N Negative Neg 负(负数值、负极性)
Number Num 数字、数量(数值标识)
Nesting Nest 嵌套(嵌套结构、嵌套调用,简化原全称)
Neutral Neut 中性(中性状态、零电位)
New New 新建(对象新建、数据新增)
Next Next 下一个(序列、步骤)
O Offset Offset 偏移(地址偏移、数值偏移)
Old Old 旧的(历史数据、旧版本)
Operating System OS 操作系统(系统环境)
Optimize Opt 优化(代码优化、性能优化)
Original Orig 原始(原始数据、原始配置)
Output Out 输出(数据输出、硬件输出)
Overflow Ovf 溢出(内存溢出、数值溢出)
P Password Pwd 密码(身份验证)
Picture Pic 图片(图像数据、显示图片)
Point Pt 点(坐标点、数据点)
Print Prn 打印(文件打印、日志输出)
Program Prog 程序(应用程序、控制程序,简化原Prg更规范)
Package Pkg 包(软件包、设备包)
Parameter Param 参数(函数参数、配置参数)
Pass Pass 通过(校验通过、权限通过)
Performance Perf 性能(系统性能、运行效率)
Period Per 周期(采样周期、运行周期)
Phase Ph 相位(信号相位、电力相位)
Port Port 端口(通信端口、硬件接口)
Position Pos 位置(物理位置、坐标位置)
Positive Pos 正(正数值、正极性,与Position根据上下文区分)
Power Pwr 电源、功率(硬件电源、功率计算)
Previous Prev 上一个(序列、步骤)
Priority Prio 优先级(任务优先级、中断优先级)
Printer Prt 打印机(输出设备)
Process Proc 进程(系统进程、处理流程)
Product Prod 产品、乘积(产品型号、数学运算)
Protocol Prot 协议(通信协议、网络协议)
Pointer Ptr 指针(内存指针、数据指针)
Put Put 放置(数据写入、对象放置)
Q Queue Q 队列(数据队列、任务队列)
Quality Qlty 质量(数据质量、设备质量)
Quarter Qtr 季度(时间单位,简化原Quar更规范)
R Raw Raw 原始(原始数据、未处理数据)
Reactive React 无功(无功功率、反应式)
Recall Rcl 召回(配置召回、数据恢复)
Rectangle Rect 矩形(图形、区域定义)
Read Rd 读取(数据读取、寄存器读取)
Ready Rdy 就绪(设备就绪、任务就绪)
Reference Ref 引用(指针引用、参考值)
Register Reg 寄存器(硬件寄存器、配置寄存器)
Request Req 请求(接口请求、服务请求)
Reset Rst 复位(设备复位、状态重置,简化原全称)
Reserve Resv 保留(资源保留、寄存器位保留)
Resume Resume 恢复(任务恢复、流程继续)
Response Resp 响应(接口响应、设备响应)
Return Rtn 返回(函数返回、数据返回)
Reverse Rev 反转(方向反转、数据反转,简化原Revs)
Ring Ring 环形(环形缓冲区、环形网络)
Row Row 行(表格、数组行)
Repeat Rpt 重复(操作重复、数据重复)
Real-Time RT 实时(实时系统、实时数据)
Running Run 运行(运行状态,简化原全称)
Receive Rx 接收(数据接收、通信接收)
S Server Srv 服务器、服务(网络服务、设备服务,统一缩写)
Source Src 源(源地址、数据源,修正原Sourse拼写错误)
Statistic Stat 统计(统计数据)
String Str 字符串(数据类型)
Sample Smp 采样(传感器采样、数据采样)
Scale Scale 缩放、量程(数据缩放、传感器量程)
Scale Factor SF 比例因子(采样比例、转换因子)
Scaling Scaling 标定、缩放(数据标定过程)
Scan Scan 扫描(设备扫描、数据扫描)
Schedule Sched 调度(任务调度、时间调度)
Screen Scr 屏幕(显示屏幕)
Second Sec 秒(时间单位)
Segment Seg 段(内存段、数据段)
Select Sel 选择(功能选择、数据选择)
Semaphore Sem 信号量(进程同步、资源同步)
Sequence Seq 序列(指令序列、数据序列)
Set Set 设置(参数设置、状态设置)
Setting Sett 设置项(配置项,与Set区分)
Signal Sig 信号(硬件信号、通信信号)
Size Size 大小(文件大小、内存大小)
Seven-segments SS 七段显示(数码管显示)
Start Start 启动(程序启动、设备启动)
Status Stat 状态(设备状态、运行状态,与Statistic区分)
Stack Stk 栈(内存栈、调用栈)
Standard Std 标准(标准接口、标准格式)
Stop Stop 停止(程序停止、设备停止)
Subtraction Sub 减法(运算操作)
Suspend Suspend 挂起(任务挂起、流程暂停)
Switch Sw 开关(硬件开关、功能切换)
Synchronize Sync 同步(数据同步、设备同步,简化原Synch)
System Sys 系统(硬件系统、软件系统,简化原Syst)
T Temperature Temp 温度(传感器测量值)
Text Txt 文本(文本数据、文本文件)
Task Task 任务(系统任务、线程任务)
Table Tbl 表格(数据表格、查找表)
Threshold Thresh 阈值(触发阈值、报警阈值,简化原Th更清晰)
Tick Tick 时钟滴答(定时器滴答、时间片)
Time Time 时间(时间戳、运行时间)
Timer Tmr 定时器(硬件定时器、软件定时器)
Toggle Tgl 切换(状态切换、功能切换)
Total Tot 总计(总和、累计值)
Trigger Trig 触发(事件触发、中断触发)
Time-stamp TS 时间戳(数据时间戳、日志时间戳)
Timeout TO 超时(通信超时、等待超时)
U Unlock Unlock 解锁(资源解锁、互斥锁释放)
User Usr 用户(用户配置、用户权限)
Up Up 向上、开启(状态、方向)
Update Upd 更新(数据更新、配置更新,简化原全称)
Utility Util 工具(工具函数、辅助工具)
Universal Uni 通用(通用接口、通用配置)
V Value Val 值(参数值、测量值)
Vector Vect 向量(数据向量、坐标向量)
Version Ver 版本(软件版本、设备版本)
Variable Var 变量(程序变量、状态变量)
Visible Vis 可见(UI可见、状态可见)
Voltage Volt 电压(硬件电压、测量值,简化原Vol更清晰)
W Watchdog Wdg 看门狗(硬件看门狗、系统监控,统一缩写)
Window Win 窗口(UI窗口、数据窗口,统一缩写)
Write Wr 写入(数据写入、寄存器写入)
Wireless Wifi/Ble 无线(Wi-Fi/Bluetooth,软硬件开发常用)
X Execute Exec 执行(程序执行、指令执行)
Exclusive Excl 独占(独占资源、独占访问)
Y Year Year 年(时间单位)
Yes Y 是(状态判断、选项)
Z Zero Zro 零(零值、零电位)
Zone Zone 区域(控制区域、划分区域)

PS:关于 C 语言编程风格,请稳步【本人C语言编程规范】。