C 备忘
C 语言中一些不太常用, 但又有用的语法等备忘;
资料来源:
<>
更新
1
22024.04.06 初始
2024.11.16 添加一些内容
导语
C 语言中一些不太常用, 但又有用的语法等备忘;
- C11 可用; 编译器指定为 GCC;
C 语言
可变参数
<stdarg.h>
;fun(int x,…)
需要一个固定参数作为锚点;- 通过一组宏来传递可变参数;
va_arg(args, int);
必须知道参数类型; - 没有任何编译检查; 没有任何编译检查;
1 | // 初始化va_list |
编译
静态断言 (Static Assertion)
- 编译时的断言检查, 检查失败会阻止编译进行;
- 检查 结构体长度 / 类型兼容 等等
1 |
|
结构体/联合体
匿名结构体和联合体 (Anonymous Structures and Unions)
- 可以在 结构体或联合体中使用匿名结构体和匿名联合体;
- 相当于减少了一层间接访问, 使用上更方便, 特别是层层嵌套下
1 | struct OuterStruct { |
结构体位字段 (Bit-fields)
- 为结构体成员指定精确 几个 bit;
- 用好了非常节省空间; 但有不同平台移植问题;
- 非资源严重受限,一般很少用吧;
1 | struct bitfield { |
灵活数组成员 (Flexible Array Member)
- 声明不定长的数组, 必须是结构体最后一个成员, c99 引入;
- 声明时不指定大小, 通常需要 malloc;
1 | struct flexible_array_struct { |
指定初始化器 (Designated Initializers)
- 初始化结构体时指定成员值
1 | struct point { |
复合字面量 (Compound Literals)
- 直接定义并初始化一个 匿名实例; c99 引入
- 说人话就是,随时方便 初始化任何 (结构体/联合体).
- 和 指定初始化器 有点像;
1 | // (type) { initializer_list } |
数据结构标记 (Tagged Union): 不是语法, 是惯用模式;
- union 定义多组类型; struct 包含 union 和 一个类型变量;
1 | typedef enum { |
宏
使用宏简洁…但是但是,但是 出问题其调试真不是一般麻烦;
类型通用宏 (Type Generic Macros)
- 根据不同类型,选择不同的 宏展开; c11 引入;
- default 是类型不符时的默认展开
1 | // _Generic( expression, type1 : expression1, type2 : expression2, …) |
X-Macros: 不是语法, 是惯用模式;
- 宏的宏;
- 但是在一些库 经常见到; 特别是 hash 或者 链表 库;
1 | // 定义X-Macros |
可变参数宏: 允许宏接受可变数量的参数
__VA_ARGS__
- 一般配合接收可变参数的函数;;;
1 |
|
#
与 ##
#
是 把参数转换成字符串##
是连接两个参数- 类似任意生成源码, 玩出❀来…
宏的间接展开:
- 宏之间嵌套, 复杂参数处理, 确保宏正确的展开顺序.
- 相当于引入了一个中间层;
1 |
CONCAT(FIRST(1, 2), SECOND(3, 4))
预期是得到 14
- 但是宏展开原则: 由左到右,由外到内;
- 这里会直接尝试组装字面上的
FIRST(1, 2)
和SECOND(3, 4)
而不是这两个宏展开的结果.
1 |
CONCAT_INDIRECT(FIRST(1, 2), SECOND(3, 4))
这里就不一样了, 会先取FIRST(1, 2)
和SECOND(3, 4)
的结果,最后才会进入CONCAT
宏,得到正确的结果.
具体问题
判断传入的参数数量
1 | // count the number of arguments |
1 | // COUNT_ARGS(a,b,c) |
这里就给出了一个例子:
- 通过 可变参数宏 接收任意数量, 在
COUNT_ARGS_IMPL
展开成 (参数 + 10 到 1) COUNT_ARGS_IMPL
内部, 参数占据几个席位 就会把 N 挤到第几个席位. 如上所示, 将参数对齐就一目了然了.
C11-threads.h
这个是 c 语言的跨平台多线程库;
线程管理 与 条件变量 省略;
线程局部存储 (Thread-Local Storage)
- tls; 线程专有变量声明;
_Thread_local
; - 类似老项目改造时, 依赖的全局变量全部 tls 化
1 | _Thread_local int threadLocalVar = 0; |
call_once
多线程下保证只调用一次;
- 有些函数只能放在多线程中执行,但是偏偏只能全局执行一次…(奇怪的需求)
- 多线程全局单例模式初始化;
1 | // 一次性初始化标志 |
使用
- 定义标志位
- 传入的函数要包装为
void (*func)(void)
call_once
1 | // 定义全局once_flag |
Gcc
属性 __attribute__
: 告诉编译器一些特定属性;
- 可用于函数 变量 类型等
1 | void my_exit() __attribute__((noreturn)); |
typeof: typeof(表达式)
, 编译器将 表达式的类型来代替整个 typeof
构造
- 在宏中特别特别常用; 有些泛型的意思了;
1 |
|
transparent union: 类型安全的方式将多种不同类型的参数传递给函数,不需要进行显式的类型转换.
- 其实是个
__attribute__
的特例; - 注意: 无法假定 函数内收到的到底是什么类型, 只能通过其他方法取得类型标记;
1 | typedef union { |