C 备忘

  • C 语言中一些不太常用, 但又有用的语法等备忘;

  • 资料来源:

    <>

  • 更新

    1
    2024.04.06 初始

导语

C 语言中一些不太常用, 但又有用的语法等备忘;

  • C11 可用; 编译器指定为 GCC;

C 语言

可变参数

  • <stdarg.h>; fun(int x,…) 需要一个固定参数作为锚点;
  • 通过一组宏来传递可变参数; va_arg(args, int); 必须知道参数类型;
  • 没有任何编译检查; 没有任何编译检查;
1
2
3
4
5
6
7
// 初始化va_list
va_list args;
va_start(args, num);
xx
va_arg(args, int);
xx
va_end(args);

线程局部存储 (Thread-Local Storage)

  • tls; 线程专有变量声明; _Thread_local; c11 引入
1
2
_Thread_local int threadLocalVar = 0;
// 每个线程, threadLocalVar 均为独立变量

编译

静态断言 (Static Assertion)

  • 编译时的断言检查, 检查失败会阻止编译进行;
  • 检查 结构体长度 / 类型兼容 等等
1
2
3
4
5
6
#include <assert.h>

_Static_assert(expression, message); // 原型
static_assert(expression, message); // 更常用的宏

static_assert(sizeof(struct MyStruct) == 8, "The size of MyStruct should be 8 bytes");

结构体/联合体

匿名结构体和联合体 (Anonymous Structures and Unions)

  • 可以在 结构体或联合体中使用匿名结构体和匿名联合体;
  • 相当于减少了一层间接访问, 使用上更方便, 特别是层层嵌套下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct OuterStruct {
int common;
union {
struct { // 匿名结构体
int x;
int y;
};
struct { // 匿名结构体
char a;
char b;
};
};
};
struct OuterStruct OPS = {0};
// 这 3 者等同
OPS.common; OPS.x; OPS.a;

结构体位字段 (Bit-fields)

  • 为结构体成员指定精确 几个 bit;
  • 用好了非常节省空间; 但有不同平台移植问题;
  • 非资源严重受限,一般很少用吧;
1
2
3
4
5
6
struct bitfield {
unsigned int field1 : 3; // 分配3位
unsigned int field2 : 1; // 分配1位
unsigned int field3 : 10;// 分配10位
// …
};

灵活数组成员 (Flexible Array Member)

  • 声明不定长的数组, 必须是结构体最后一个成员, c99 引入;
  • 声明时不指定大小, 通常需要 malloc;
1
2
3
4
5
struct flexible_array_struct {
int data;
size_t size;
char array[]; // 灵活数组成员
};

指定初始化器 (Designated Initializers)

  • 初始化结构体时指定成员值
1
2
3
4
5
struct point {
int x;
int y;
};
struct point p = {.y = 5, .x = 10};

复合字面量 (Compound Literals)

  • 直接定义并初始化一个 匿名实例; c99 引入
  • 说人话就是,随时方便 初始化任何 (结构体/联合体).
  • 和 指定初始化器 有点像;
1
2
3
4
5
6
7
// (type) { initializer_list }
typedef struct {
int x;
int y;
} Point;

Point p = (Point){.x = 10, .y = 20};

数据结构标记 (Tagged Union): 不是语法, 是惯用模式;

  • union 定义多组类型; struct 包含 union 和 一个类型变量;
1
2
3
4
5
6
7
8
9
10
11
12
typedef enum {
TYPE_INT,
TYPE_FLOAT,
TYPE_STRING,
TYPE_NONE // 表示联合体不包含任何有效数据
} DataType;

typedef union {
int i;
float f;
char *s;
} DataUnion;

使用宏简洁…但是但是,但是 出问题其调试真不是一般麻烦;

类型通用宏 (Type Generic Macros)

  • 根据不同类型,选择不同的 宏展开; c11 引入;
  • default 是类型不符时的默认展开
1
2
3
4
5
6
// _Generic( expression, type1 : expression1, type2 : expression2, …)
#define getType(x) _Generic((x), \
int: "int", \
float: "float", \
double: "double", \
default: "other")

X-Macros: 不是语法, 是惯用模式;

  • 宏的宏;
  • 但是在一些库 经常见到; 特别是 hash 或者 链表 库;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义X-Macros
#define FRUITS(X) \
X(Apple) \
X(Banana) \
X(Cherry) \
X(Date)
// 再定义 X
#define GENERATE_ENUM(ENUM) ENUM,

// 使用X-Macros生成枚举
enum Fruit {
FRUITS(GENERATE_ENUM)// 就能展开成巨量的字符串;
};

#undef GENERATE_ENUM // 最后别忘记清理宏

可变参数宏: 允许宏接受可变数量的参数

  • __VA_ARGS__
  • 一般配合接收可变参数的函数;;;
1
2
3
4
5
6
7
8
9
10
11

// 定义一个打印日志的可变参数宏
#define LOG(format, …) printf(format, __VA_ARGS__)

int main() {
// 使用宏,传递不同数量的参数
LOG("This is a log message with no additional arguments.\n");
LOG("This is a log message with one argument: %d\n", 1);
LOG("This is a log message with two arguments: %d %s\n", 2, "arguments");
return 0;
}

Gcc

属性 __attribute__: 告诉编译器一些特定属性;

  • 可用于函数 变量 类型等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void my_exit() __attribute__((noreturn));

// 确保 给定类型与其他参数匹配
void my_printf(const char *format, …) __attribute__((format(printf, 1, 2)));
// 取消变量的自然对齐方式
struct my_struct {
char a;
int b;
} __attribute__((packed));

int var __attribute__((aligned(16))) = 0; // var将在16字节的边界对齐

// `cleanup`:指定一个函数,在变量作用域结束时自动调用这个函数来清理
void cleanup_function(int *value) {
// 清理工作
}
int main() {
int __attribute__((cleanup(cleanup_function))) my_var = 0;
// …
}

typeof: typeof(表达式), 编译器将 表达式的类型来代替整个 typeof 构造

  • 在宏中特别特别常用; 有些泛型的意思了;
1
2
3
4
5
6
7
#define max(a, b) \
({ typeof(a) _a = (a); \
typeof(b) _b = (b); \
_a > _b ? _a : _b; })

// 使用 max 宏获取两个整数的最大值
int max_int = max(i, j);

transparent union: 类型安全的方式将多种不同类型的参数传递给函数,不需要进行显式的类型转换.

  • 其实是个 __attribute__ 的特例;
  • 注意: 无法假定 函数内收到的到底是什么类型, 只能通过其他方法取得类型标记;
1
2
3
4
5
6
7
8
9
typedef union {
int i;
float f;
} IntOrFloat __attribute__((transparent_union));

void printIntOrFloat(IntOrFloat x) {
// 这里我们假设传入的是 int 类型
// printf("%d\n", x.i);
}