Skip to content

C/C++ 与系统底层编程


Bjarne Stroustrup — C++ 之父

C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off.

C 让你轻松地朝自己的脚开枪;C++ 让这变得更难,但一旦开枪,整条腿都没了。

疾旋鼬觉得这句话太可怕了……可是为了搞安全,必须直面枪林弹雨!


第一部分:C 语言基础

程序结构与编译

// jxy.c — 疾旋鼬的第一个 C 程序
#include <stdio.h>      // 标准输入输出
#include <stdlib.h>     // 标准库(malloc, free, exit 等)

// 程序入口
int main(int argc, char *argv[]) {
    // argc: 命令行参数个数
    // argv: 参数字符串数组

    printf("疾旋鼬登场!\n");
    printf("传入了 %d 个参数\n", argc);
    for (int i = 0; i < argc; i++) {
        printf("  argv[%d] = %s\n", i, argv[i]);
    }

    return 0;   // 返回 0 表示正常退出
}
# 编译与运行
gcc -o jxy jxy.c -Wall -Wextra -g    # -Wall: 警告, -g: 调试信息
./jxy hello world
# 输出:
# 疾旋鼬登场!
# 传入了 3 个参数
#   argv[0] = ./jxy
#   argv[1] = hello
#   argv[2] = world

编译流程

源代码到可执行文件经历四个阶段:

jxy.c → [预处理] → jxy.i → [编译] → jxy.s → [汇编] → jxy.o → [链接] → jxy
# 分步查看
gcc -E jxy.c -o jxy.i       # 预处理: 展开宏和 #include
gcc -S jxy.c -o jxy.s       # 编译: C → 汇编
gcc -c jxy.c -o jxy.o       # 汇编: 汇编 → 机器码(目标文件)
gcc jxy.o -o jxy             # 链接: 合并库函数,生成可执行文件

基本数据类型

#include <stdio.h>
#include <limits.h>
#include <float.h>
#include <stdint.h>

int main(void) {
    // ===== 整数类型 =====
    char            c = 'A';           // 1 字节, 范围: -128 ~ 127
    unsigned char   uc = 255;          // 1 字节, 范围: 0 ~ 255
    short           s = -32768;        // 2 字节
    unsigned short  us = 65535;        // 2 字节
    int             i = 2147483647;    // 4 字节(典型)
    unsigned int    ui = 4294967295U;  // 4 字节
    long            l = 2147483647L;   // 4 或 8 字节(取决于平台)
    long long       ll = 9223372036854775807LL;  // 8 字节

    // 定长整数(推荐在安全编程中使用)
    int8_t   i8  = -128;               // 精确 1 字节
    int16_t  i16 = -32768;             // 精确 2 字节
    int32_t  i32 = -2147483648;        // 精确 4 字节
    int64_t  i64 = -9223372036854775807LL - 1;  // 精确 8 字节
    uint32_t u32 = 4294967295U;        // 无符号 4 字节
    size_t   sz  = 0;                  // sizeof 返回值类型,平台相关的无符号整数

    // ===== 浮点类型 =====
    float    f  = 3.14f;               // 4 字节, 约 6-7 位有效数字
    double   d  = 3.141592653589793;   // 8 字节, 约 15-16 位有效数字
    long double ld = 3.141592653589793238L;  // 12 或 16 字节

    // ===== 布尔类型(C99) =====
    #include <stdbool.h>
    bool flag = true;                  // 实际上是 _Bool,true=1, false=0

    // ===== 各类型的大小 =====
    printf("sizeof(char)        = %zu\n", sizeof(char));        // 1
    printf("sizeof(int)         = %zu\n", sizeof(int));         // 4
    printf("sizeof(long)        = %zu\n", sizeof(long));        // 4 (Win64) 或 8 (Linux64)
    printf("sizeof(long long)   = %zu\n", sizeof(long long));   // 8
    printf("sizeof(float)       = %zu\n", sizeof(float));       // 4
    printf("sizeof(double)      = %zu\n", sizeof(double));      // 8
    printf("sizeof(void *)      = %zu\n", sizeof(void *));      // 4 (32位) 或 8 (64位)

    // ===== 整数溢出(安全大敌) =====
    int max_int = INT_MAX;             // 2147483647
    printf("INT_MAX   = %d\n", max_int);
    printf("INT_MAX+1 = %d\n", max_int + 1);  // 未定义行为(通常变为 INT_MIN)
    // 疾旋鼬提醒:整数溢出是 C 程序中最常见的安全漏洞之一!

    return 0;
}

变量、常量与作用域

#include <stdio.h>

// 全局变量(存储在 .data 或 .bss 段)
int g_pal_count = 0;
int g_uninit;              // 未初始化的全局变量,自动为 0(.bss 段)

// const 常量
const int MAX_PAL = 1024;

// 宏常量(预处理阶段替换,无类型)
#define PAL_NAME_LEN 64
#define PI 3.14159265358979

// 枚举(有名字的整数常量)
enum ElementType {
    ELEM_DRAGON = 0,
    ELEM_FIRE   = 1,
    ELEM_WATER  = 2,
    ELEM_GRASS  = 3,
    ELEM_ELECTRIC = 4,
    ELEM_COUNT      // 自动为 5
};

// typedef:给类型起别名
typedef unsigned char      u8;
typedef unsigned int       u32;
typedef struct Pal Pal;    // 前向声明

int main(void) {
    // 局部变量(存储在栈上)
    int hp = 100;

    // static 局部变量:生命周期为整个程序运行期间,但只在本函数内可见
    static int call_count = 0;   // 只初始化一次
    call_count++;
    printf("函数被调用 %d 次\n", call_count);

    // 作用域与遮蔽(shadowing)
    int x = 10;
    {
        int x = 20;       // 遮蔽外层的 x
        printf("%d\n", x); // 20
    }
    printf("%d\n", x);     // 10

    // 使用枚举
    enum ElementType elem = ELEM_DRAGON;
    if (elem == ELEM_DRAGON) {
        printf("疾旋鼬是龙属性帕鲁!\n");
    }

    // 使用 typedef
    u8 level = 50;
    u32 exp = 1234567;

    return 0;
}

控制流

#include <stdio.h>

int main(void) {
    int hp = 75;

    // ===== if-else =====
    if (hp >= 80) {
        printf("疾旋鼬状态良好!\n");
    } else if (hp >= 50) {
        printf("疾旋鼬有点累了……\n");
    } else {
        printf("疾旋鼬危险了!快治疗!\n");
    }

    // switch-case(注意 fall-through)
    enum ElementType elem = ELEM_DRAGON;
    switch (elem) {
        case ELEM_DRAGON:
            printf("龙属性\n");
            break;          // 必须 break,否则 fall-through
        case ELEM_FIRE:
            printf("火属性\n");
            break;
        case ELEM_WATER:
            printf("水属性\n");
            break;
        default:
            printf("未知属性\n");
            break;
    }

    // ===== for 循环 =====
    for (int i = 0; i < 5; i++) {
        printf("第 %d 回合\n", i + 1);
    }

    // ===== while 循环 =====
    int countdown = 5;
    while (countdown > 0) {
        printf("%d...\n", countdown--);
    }
    printf("疾旋鼬发射!\n");

    // ===== do-while(至少执行一次) =====
    int input;
    do {
        printf("请输入指令 (0=退出): ");
        // scanf("%d", &input);
        input = 0;  // 模拟
    } while (input != 0);

    // ===== break 与 continue =====
    for (int i = 0; i < 100; i++) {
        if (i == 10) break;         // 立即退出循环
        if (i % 3 == 0) continue;   // 跳过本次迭代
        printf("%d ", i);           // 1 2 4 5 7 8
    }
    printf("\n");

    // ===== goto(慎用,但在错误处理中有用) =====
    int *buffer = NULL;
    buffer = malloc(1024);
    if (!buffer) goto cleanup;

    // ... 使用 buffer ...

cleanup:
    free(buffer);
    return 0;
}

函数

#include <stdio.h>
#include <stdarg.h>

// ===== 基本函数 =====
// 函数声明(原型)
int add(int a, int b);
void greet(const char *name);

// 函数定义
int add(int a, int b) {
    return a + b;
}

void greet(const char *name) {
    printf("你好,%s!疾旋鼬向你问好!\n", name);
}

// ===== 值传递 vs 指针传递 =====
void try_modify(int x) {
    x = 999;    // 只修改了副本,原值不变
}

void real_modify(int *x) {
    *x = 999;   // 通过指针修改原值
}

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

// ===== 数组参数退化为指针 =====
// arr[] 等价于 *arr,丢失了长度信息
void print_array(int arr[], int len) {
    for (int i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

// 更明确的写法
void print_array_ptr(int *arr, int len) {
    for (int i = 0; i < len; i++) {
        printf("%d ", arr[i]);      // arr[i] 等价于 *(arr + i)
    }
    printf("\n");
}

// ===== 可变参数函数 =====
int sum(int count, ...) {
    va_list args;
    va_start(args, count);

    int total = 0;
    for (int i = 0; i < count; i++) {
        total += va_arg(args, int);
    }

    va_end(args);
    return total;
}

// ===== 函数指针 =====
// 函数指针类型定义
typedef int (*MathFunc)(int, int);

int multiply(int a, int b) { return a * b; }
int divide(int a, int b)   { return b ? a / b : 0; }

// 使用函数指针实现策略模式
int apply_op(int a, int b, MathFunc op) {
    return op(a, b);
}

int main(void) {
    greet("桃旋鼬");

    // 值传递 vs 指针传递
    int val = 42;
    try_modify(val);
    printf("try_modify 后: %d\n", val);     // 42(不变)

    real_modify(&val);
    printf("real_modify 后: %d\n", val);    // 999(被修改)

    // swap
    int x = 10, y = 20;
    swap(&x, &y);
    printf("交换后: x=%d, y=%d\n", x, y);   // x=20, y=10

    // 可变参数
    printf("sum(3, 10, 20, 30) = %d\n", sum(3, 10, 20, 30));  // 60

    // 函数指针
    MathFunc ops[] = {add, multiply, divide};
    const char *names[] = {"加", "乘", "除"};
    for (int i = 0; i < 3; i++) {
        printf("%s: %d\n", names[i], apply_op(12, 4, ops[i]));
    }

    // 回调函数(qsort 使用函数指针进行比较)
    #include <stdlib.h>
    int nums[] = {5, 2, 8, 1, 9, 3};
    int n = sizeof(nums) / sizeof(nums[0]);

    // 比较函数:升序
    int cmp_asc(const void *a, const void *b) {
        return (*(int *)a) - (*(int *)b);
    }
    qsort(nums, n, sizeof(int), cmp_asc);
    print_array(nums, n);  // 1 2 3 5 8 9

    return 0;
}

数组与字符串

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void) {
    // ===== 数组 =====
    int scores[5] = {95, 87, 100, 76, 88};
    // int auto_size[] = {1, 2, 3};  // 编译器自动推断大小为 3

    // 遍历
    for (int i = 0; i < 5; i++) {
        printf("疾旋鼬第 %d 战得分: %d\n", i + 1, scores[i]);
    }

    // 二维数组
    int matrix[3][4] = {
        {1,  2,  3,  4},
        {5,  6,  7,  8},
        {9, 10, 11, 12}
    };
    printf("matrix[1][2] = %d\n", matrix[1][2]);  // 7

    // ===== 字符串(以 '\0' 结尾的字符数组)=====
    char name[20] = "疾旋鼬";         // 自动添加 '\0'
    char greeting[] = "Hello";        // 编译器自动计算大小(6,含 '\0')
    char buf[64];

    // 字符串长度 vs 数组大小
    printf("strlen(name) = %zu\n", strlen(name));   // 字符串长度(不含 '\0')
    printf("sizeof(name) = %zu\n", sizeof(name));   // 数组大小(含 '\0',20)

    // 常用字符串函数(<string.h>)
    strcpy(buf, name);                // 拷贝(危险:无长度检查!)
    strncpy(buf, name, sizeof(buf) - 1);  // 安全拷贝
    buf[sizeof(buf) - 1] = '\0';     // 确保 null 结尾

    strcat(buf, " 最强!");            // 拼接(危险:无长度检查!)
    strncat(buf, "!!", sizeof(buf) - strlen(buf) - 1);  // 安全拼接

    int cmp = strcmp("疾旋鼬", "桃旋鼬");  // 比较: <0, 0, >0
    char *found = strstr("疾旋鼬love工管", "love");  // 子串搜索

    // 格式化字符串
    sprintf(buf, "疾旋鼬 HP=%d ATK=%d", 120, 85);
    snprintf(buf, sizeof(buf), "疾旋鼬 HP=%d", 120);  // 安全版本

    printf("%s\n", buf);

    // ===== 字符串转数字 =====
    int n = atoi("42");              // 字符串 → int
    long l = strtol("12345", NULL, 10);  // 更安全的转换
    double d = atof("3.14");         // 字符串 → double

    return 0;
}

缓冲区溢出——C 语言最经典的安全漏洞

// 危险代码:缓冲区溢出
char name[8];
strcpy(name, "疾旋鼬AAAABBBBCCCCDDDD");  // 超过 8 字节,溢出!

// 攻击者可以覆盖栈上的返回地址,劫持程序控制流
// 这就是 "栈溢出攻击" 的原理

// 防御:始终使用带长度限制的函数
snprintf(name, sizeof(name), "%s", input);

指针

指针是 C 语言最强大也最危险的特性,理解指针是理解系统底层和安全漏洞的基础。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
    // ===== 指针基础 =====
    int hp = 120;
    int *p_hp = &hp;           // p_hp 存储 hp 的地址

    printf("hp 的值:    %d\n", hp);        // 120
    printf("hp 的地址:  %p\n", (void *)&hp);  // 0x7ffd...
    printf("p_hp 的值:  %p\n", (void *)p_hp); // 同上
    printf("*p_hp 的值: %d\n", *p_hp);        // 120(解引用)

    // 通过指针修改值
    *p_hp = 200;
    printf("修改后 hp = %d\n", hp);  // 200

    // ===== 指针算术 =====
    int arr[] = {10, 20, 30, 40, 50};
    int *p = arr;    // 数组名退化为指向首元素的指针

    printf("p[0] = %d, *(p+1) = %d, *(p+2) = %d\n",
           p[0], *(p + 1), *(p + 2));   // 10, 20, 30

    // p + i 等价于 &arr[i]
    // *(p + i) 等价于 arr[i]

    // ===== 指针与数组的区别 =====
    int a[5] = {1, 2, 3, 4, 5};
    int *ptr = a;

    printf("sizeof(a)   = %zu\n", sizeof(a));    // 20(整个数组的大小)
    printf("sizeof(ptr) = %zu\n", sizeof(ptr));   // 8(指针本身的大小)

    // ===== void 指针(通用指针)=====
    void *vp = &hp;
    // void * 可以指向任何类型,但不能直接解引用
    printf("hp = %d\n", *(int *)vp);  // 需要先转换类型

    // malloc 就返回 void *
    void *mem = malloc(1024);
    memset(mem, 0, 1024);
    free(mem);

    // ===== 多级指针 =====
    int val = 42;
    int *p1 = &val;       // 一级指针
    int **p2 = &p1;       // 二级指针
    int ***p3 = &p2;      // 三级指针

    printf("***p3 = %d\n", ***p3);  // 42

    // ===== 函数指针 =====
    int (*operation)(int, int);
    int add(int a, int b) { return a + b; }
    operation = add;
    printf("result = %d\n", operation(3, 4));  // 7

    // ===== NULL 指针 =====
    int *np = NULL;
    // *np = 42;  // 段错误!解引用 NULL 是未定义行为
    if (np != NULL) {
        printf("安全访问\n");
    }

    // ===== 指针与 const =====
    int x = 10, y = 20;
    const int *pc = &x;       // 指向常量的指针:不能通过 pc 修改 *pc
    // *pc = 99;               // 编译错误
    pc = &y;                   // 但可以改变 pc 本身

    int *const cp = &x;       // 常量指针:不能改变 cp 本身
    *cp = 99;                  // 但可以修改 *cp
    // cp = &y;               // 编译错误

    const int *const cpc = &x; // 都不能改

    return 0;
}

内存管理

理解 C 的内存布局是理解安全漏洞(堆溢出、UAF、double free)的关键。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 全局变量
int g_init = 42;        // .data 段(已初始化)
int g_uninit;           // .bss 段(未初始化,自动为 0)
const char *g_msg = "疾旋鼬";  // .rodata 段(只读)

int main(void) {
    /*
    C 程序内存布局(从低地址到高地址):

    ┌─────────────┐ 低地址
    │  .text       │  代码段(只读,存放机器指令)
    ├─────────────┤
    │  .rodata     │  只读数据(字符串字面量等)
    ├─────────────┤
    │  .data       │  已初始化的全局/静态变量
    ├─────────────┤
    │  .bss        │  未初始化的全局/静态变量(零初始化)
    ├─────────────┤
    │  Heap ↑      │  堆(向高地址增长,malloc/free 管理)
    │     ...      │
    │  ↓ Stack     │  栈(向低地址增长,函数调用帧)
    ├─────────────┤
    │  内核空间     │
    └─────────────┘ 高地址
    */

    // ===== 栈内存 =====
    int local = 100;             // 栈上的局部变量
    char stack_buf[64];          // 栈上的数组
    // 栈上的变量在函数返回时自动释放

    // ===== 堆内存(手动管理)=====
    // malloc: 分配指定字节数的内存(不初始化)
    int *arr = (int *)malloc(10 * sizeof(int));
    if (arr == NULL) {
        fprintf(stderr, "内存分配失败!\n");
        return 1;
    }

    // calloc: 分配并初始化为零
    int *zeros = (int *)calloc(10, sizeof(int));

    // realloc: 重新分配大小
    arr = (int *)realloc(arr, 20 * sizeof(int));
    // 注意:realloc 可能返回新地址,原内存被释放

    // 使用堆内存
    for (int i = 0; i < 10; i++) {
        arr[i] = i * 10;
    }

    // free: 释放堆内存
    free(arr);
    free(zeros);

    // ===== 悬垂指针(Dangling Pointer) =====
    int *dangle = (int *)malloc(sizeof(int));
    *dangle = 42;
    free(dangle);
    // dangle 仍然指向已被释放的内存!
    // *dangle = 99;  // 未定义行为(UAF: Use After Free)
    dangle = NULL;    // 好习惯:释放后置 NULL

    // ===== Double Free =====
    int *p = (int *)malloc(sizeof(int));
    free(p);
    // free(p);       // 未定义行为:Double Free(可被利用)

    // ===== 内存泄漏 =====
    int *leak = (int *)malloc(1024);
    // 如果忘记 free(leak),内存就泄漏了
    // 程序结束时 OS 会回收,但长期运行的程序会耗尽内存
    free(leak);

    // ===== 堆溢出 =====
    char *buf = (char *)malloc(16);
    // strcpy(buf, "疾旋鼬AAAABBBBCCCCDDDD");  // 写入超过 16 字节 → 堆溢出!
    // 堆溢出可以覆盖相邻的堆元数据,导致任意代码执行
    free(buf);

    return 0;
}

结构体与联合体

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// ===== 结构体定义 =====
#define MAX_NAME_LEN 64
#define MAX_SKILLS 4

typedef struct {
    char name[MAX_NAME_LEN];
    int element;        // 属性:0=龙, 1=火, 2=水 ...
    int hp;
    int attack;
    int defense;
    int speed;
    char *description;  // 堆上的字符串
} Pal;

// 结构体内存对齐
struct AlignmentDemo {
    char  a;    // 1 字节 + 3 字节填充
    int   b;    // 4 字节
    char  c;    // 1 字节 + 7 字节填充
    long  d;    // 8 字节
};  // sizeof = 24,不是 14!

// pragma pack 可以取消对齐(但会影响性能)
#pragma pack(push, 1)
struct PackedStruct {
    char  a;    // 1 字节
    int   b;    // 4 字节
    char  c;    // 1 字节
    long  d;    // 8 字节
};  // sizeof = 14(紧凑排列)
#pragma pack(pop)

// ===== 位域(Bit Fields) =====
typedef struct {
    unsigned int is_alive   : 1;   // 1 bit: 0 或 1
    unsigned int is_tamed   : 1;   // 1 bit
    unsigned int element    : 4;   // 4 bits: 0-15
    unsigned int level      : 7;   // 7 bits: 0-127
    unsigned int rarity     : 3;   // 3 bits: 0-7
} PalFlags;

// ===== 联合体(Union) =====
// 所有成员共享同一块内存,大小等于最大成员的大小
typedef union {
    uint32_t raw;           // 原始 32 位
    struct {
        uint8_t r;
        uint8_t g;
        uint8_t b;
        uint8_t a;
    } color;                // RGBA 分量
} Color;

// ===== 链表 =====
typedef struct Node {
    Pal *pal;
    struct Node *next;      // 自引用结构
} Node;

// 创建节点
Node *create_node(Pal *pal) {
    Node *node = (Node *)malloc(sizeof(Node));
    if (!node) return NULL;
    node->pal = pal;
    node->next = NULL;
    return node;
}

// 插入链表头部
void list_prepend(Node **head, Pal *pal) {
    Node *node = create_node(pal);
    if (!node) return;
    node->next = *head;
    *head = node;
}

// 遍历链表
void list_print(Node *head) {
    Node *cur = head;
    while (cur) {
        printf("  %s (HP=%d, ATK=%d)\n",
               cur->pal->name, cur->pal->hp, cur->pal->attack);
        cur = cur->next;
    }
}

// 释放链表
void list_free(Node *head) {
    while (head) {
        Node *next = head->next;
        free(head->pal->description);
        free(head->pal);
        free(head);
        head = next;
    }
}

int main(void) {
    // 创建帕鲁
    Pal *jxy = (Pal *)malloc(sizeof(Pal));
    strncpy(jxy->name, "疾旋鼬", MAX_NAME_LEN - 1);
    jxy->element = 0;   // 龙
    jxy->hp = 120;
    jxy->attack = 85;
    jxy->defense = 70;
    jxy->speed = 95;
    jxy->description = strdup("蜷缩身体高速旋转的龙属性帕鲁");

    // 结构体访问
    printf("名字: %s\n", jxy->name);
    printf("HP:   %d\n", jxy->hp);

    // 结构体指针访问(箭头操作符)
    Pal *p = jxy;
    printf("ATK:  %d\n", p->attack);   // 等价于 (*p).attack

    // 内存对齐
    printf("sizeof(AlignmentDemo) = %zu\n", sizeof(struct AlignmentDemo));  // 24
    printf("sizeof(PackedStruct)  = %zu\n", sizeof(struct PackedStruct));   // 14

    // 位域
    PalFlags flags = {0};
    flags.is_alive = 1;
    flags.element = 0;    // 龙
    flags.level = 50;
    flags.rarity = 4;
    printf("sizeof(PalFlags) = %zu\n", sizeof(PalFlags));  // 4 字节

    // 联合体
    Color c;
    c.color.r = 255;
    c.color.g = 100;
    c.color.b = 50;
    c.color.a = 255;
    printf("颜色值: 0x%08X\n", c.raw);  // 可以用原始 32 位读取

    // 链表
    Node *team = NULL;
    // 创建更多帕鲁...
    Pal *tmd = (Pal *)malloc(sizeof(Pal));
    strncpy(tmd->name, "捣蛋猫", MAX_NAME_LEN - 1);
    tmd->hp = 80;
    tmd->attack = 60;
    tmd->description = strdup("调皮的猫型帕鲁");

    list_prepend(&team, tmd);
    list_prepend(&team, jxy);

    printf("疾旋鼬的队伍:\n");
    list_print(team);

    list_free(team);
    return 0;
}

预处理器

// ===== 宏定义 =====
#define MAX(a, b)       ((a) > (b) ? (a) : (b))
#define MIN(a, b)       ((a) < (b) ? (a) : (b))
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

// 带副作用的宏参数可能出错
// MAX(i++, j++) 会展开为 ((i++) > (j++) ? (i++) : (j++))

// 安全的宏(使用 GCC 语句表达式)
#define SAFE_MAX(a, b) ({         \
    __typeof__(a) _a = (a);       \
    __typeof__(b) _b = (b);       \
    _a > _b ? _a : _b;           \
})

// ===== 条件编译 =====
#define DEBUG 1

void log_msg(const char *msg) {
    #if DEBUG
        fprintf(stderr, "[DEBUG] %s\n", msg);
    #endif
}

// 头文件保护(防止重复包含)
#ifndef PAL_H
#define PAL_H

typedef struct { /* ... */ } Pal;

#endif // PAL_H

// #pragma once  // 更简洁的替代方案(非标准但广泛支持)

// ===== 字符串化与连接 =====
#define STR(x) #x                  // 将参数字符串化
#define CONCAT(a, b) a##b          // 将两个 token 连接

int CONCAT(my_, var) = 42;         // 展开为: int my_var = 42;
printf("%s\n", STR(疾旋鼬));        // 输出: "疾旋鼬"

// ===== 预定义宏 =====
printf("文件: %s\n", __FILE__);    // 当前文件名
printf("行号: %d\n", __LINE__);    // 当前行号
printf("函数: %s\n", __func__);    // 当前函数名(C99)
printf("日期: %s\n", __DATE__);    // 编译日期
printf("时间: %s\n", __TIME__);    // 编译时间

// 自定义断言宏
#define ASSERT(cond)                                    \
    do {                                                \
        if (!(cond)) {                                  \
            fprintf(stderr, "断言失败: %s\n"            \
                "  文件: %s, 行: %d\n"                  \
                "  函数: %s\n",                         \
                #cond, __FILE__, __LINE__, __func__);   \
            abort();                                    \
        }                                               \
    } while (0)

文件 I/O 与系统调用

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>

int main(void) {
    // ===== 标准 I/O(缓冲) =====
    FILE *fp = fopen("pal_data.txt", "w");
    if (!fp) {
        perror("fopen 失败");   // 自动打印错误原因
        return 1;
    }
    fprintf(fp, "疾旋鼬,龙,120,85\n");
    fprintf(fp, "捣蛋猫,无,80,60\n");
    fclose(fp);

    // 读取
    fp = fopen("pal_data.txt", "r");
    char line[256];
    while (fgets(line, sizeof(line), fp)) {
        // 去除末尾换行符
        line[strcspn(line, "\n")] = '\0';
        printf("读取: %s\n", line);
    }
    fclose(fp);

    // 二进制读写
    typedef struct { int id; char name[32]; int hp; } PalRecord;

    PalRecord records[] = {
        {1, "疾旋鼬", 120},
        {2, "捣蛋猫", 80},
    };
    int count = sizeof(records) / sizeof(records[0]);

    // 写二进制文件
    FILE *bin = fopen("pal.dat", "wb");
    fwrite(&count, sizeof(int), 1, bin);
    fwrite(records, sizeof(PalRecord), count, bin);
    fclose(bin);

    // 读二进制文件
    bin = fopen("pal.dat", "rb");
    fread(&count, sizeof(int), 1, bin);
    PalRecord loaded[count];
    fread(loaded, sizeof(PalRecord), count, bin);
    fclose(bin);

    // ===== 系统 I/O(无缓冲,直接系统调用)=====
    int fd = open("raw_data.bin", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd < 0) {
        perror("open 失败");
        return 1;
    }
    const char *data = "疾旋鼬原始数据";
    ssize_t written = write(fd, data, strlen(data));
    close(fd);

    // 读取
    fd = open("raw_data.bin", O_RDONLY);
    char buf[256];
    ssize_t nread = read(fd, buf, sizeof(buf) - 1);
    buf[nread] = '\0';
    close(fd);
    printf("读到: %s\n", buf);

    // ===== 文件信息 =====
    struct stat st;
    if (stat("pal_data.txt", &st) == 0) {
        printf("文件大小: %ld 字节\n", (long)st.st_size);
        printf("权限: %o\n", st.st_mode & 0777);
        printf("修改时间: %ld\n", (long)st.st_mtime);
    }

    // 删除文件
    unlink("raw_data.bin");

    return 0;
}

第二部分:C++ 面向对象程序设计

C++ 在 C 的基础上增加了类、继承、多态、模板等特性,同时保持了对底层内存的直接控制。

类与对象

#include <iostream>
#include <string>
#include <vector>
#include <memory>

// ===== 基本类 =====
class Pal {
private:
    // 私有成员:外部不可访问
    std::string name_;
    int hp_;
    int max_hp_;
    int attack_;
    int level_;

protected:
    // 保护成员:本类和子类可访问
    int experience_ = 0;

public:
    // 构造函数
    Pal(const std::string& name, int hp, int attack)
        : name_(name), hp_(hp), max_hp_(hp), attack_(attack), level_(1)
    {
        std::cout << name_ << " 诞生了!" << std::endl;
    }

    // 委托构造函数
    Pal() : Pal("未知帕鲁", 100, 50) {}

    // 拷贝构造函数
    Pal(const Pal& other)
        : name_(other.name_ + "(克隆体)"), hp_(other.hp_),
          max_hp_(other.max_hp_), attack_(other.attack_), level_(other.level_)
    {
        std::cout << name_ << " 被克隆了!" << std::endl;
    }

    // 移动构造函数(C++11)
    Pal(Pal&& other) noexcept
        : name_(std::move(other.name_)), hp_(other.hp_),
          max_hp_(other.max_hp_), attack_(other.attack_), level_(other.level_)
    {
        other.hp_ = 0;
        std::cout << name_ << " 被移动了!" << std::endl;
    }

    // 析构函数
    virtual ~Pal() {
        std::cout << name_ << " 退出战斗……" << std::endl;
    }

    // 赋值运算符
    Pal& operator=(const Pal& other) {
        if (this != &other) {   // 防止自赋值
            name_ = other.name_;
            hp_ = other.hp_;
            max_hp_ = other.max_hp_;
            attack_ = other.attack_;
            level_ = other.level_;
        }
        return *this;
    }

    // getter 方法(const 成员函数:不修改对象状态)
    const std::string& name() const { return name_; }
    int hp() const { return hp_; }
    int level() const { return level_; }
    bool is_alive() const { return hp_ > 0; }

    // 普通成员函数
    void take_damage(int damage) {
        hp_ -= damage;
        if (hp_ < 0) hp_ = 0;
        std::cout << name_ << " 受到 " << damage << " 点伤害!"
                  << " (HP: " << hp_ << "/" << max_hp_ << ")" << std::endl;
    }

    int attack_target(Pal& target) {
        int damage = attack_ + level_ * 2;
        std::cout << name_ << " 对 " << target.name() << " 发动攻击!" << std::endl;
        target.take_damage(damage);
        return damage;
    }

    // 虚函数:允许子类重写
    virtual std::string special_move() const {
        return name_ + " 发动了普通攻击!";
    }

    // 纯虚函数(使 Pal 成为抽象类,不能直接实例化)
    // virtual std::string element_name() const = 0;

    // 友元函数:可以访问私有成员
    friend std::ostream& operator<<(std::ostream& os, const Pal& pal);

    // 静态成员
    static int total_pals;
    static int get_total() { return total_pals; }
};

int Pal::total_pals = 0;

// 运算符重载(友元函数)
std::ostream& operator<<(std::ostream& os, const Pal& pal) {
    os << "[" << pal.name_ << "] HP:" << pal.hp_
       << " LV:" << pal.level_ << " ATK:" << pal.attack_;
    return os;
}

继承与多态

// ===== 继承 =====
class DragonPal : public Pal {
private:
    int dragon_power_;

public:
    DragonPal(const std::string& name, int hp, int attack, int dragon_power)
        : Pal(name, hp, attack), dragon_power_(dragon_power)
    {
        ++total_pals;
    }

    ~DragonPal() override {
        --total_pals;
    }

    // 重写虚函数
    std::string special_move() const override {
        return name() + " 发动了龙之怒!(威力:" + std::to_string(dragon_power_) + ")";
    }

    // 子类独有方法
    void dragon_roar() const {
        std::cout << name() << ": 嗷呜——!龙威震慑全场!" << std::endl;
    }
};

class FirePal : public Pal {
private:
    int fire_power_;

public:
    FirePal(const std::string& name, int hp, int attack, int fire_power)
        : Pal(name, hp, attack), fire_power_(fire_power)
    {
        ++total_pals;
    }

    std::string special_move() const override {
        return name() + " 发动了烈焰风暴!(威力:" + std::to_string(fire_power_) + ")";
    }
};

// ===== 多态的使用 =====
void battle(Pal& a, Pal& b) {
    // 通过基类引用调用虚函数,运行时根据实际类型分派
    std::cout << a.special_move() << std::endl;
    std::cout << b.special_move() << std::endl;
    a.attack_target(b);
    b.attack_target(a);
}

// ===== 多重继承 =====
class Flyable {
public:
    virtual ~Flyable() = default;
    virtual void fly() const {
        std::cout << "在天空中飞翔!" << std::endl;
    }
};

class Swimmable {
public:
    virtual ~Swimmable() = default;
    virtual void swim() const {
        std::cout << "在水中畅游!" << std::endl;
    }
};

class WaterDragonPal : public DragonPal, public Flyable, public Swimmable {
public:
    WaterDragonPal(const std::string& name, int hp, int attack, int dp)
        : DragonPal(name, hp, attack, dp) {}

    void fly() const override {
        std::cout << name() << " 展开龙翼,腾空而起!" << std::endl;
    }
    void swim() const override {
        std::cout << name() << " 潜入深海,水龙翻腾!" << std::endl;
    }
};

int main() {
    // 创建帕鲁
    DragonPal jxy("疾旋鼬", 120, 85, 50);
    FirePal    fire("火绒狐", 90, 95, 60);

    // 多态
    battle(jxy, fire);

    // 动态多态(通过指针)
    std::vector<std::unique_ptr<Pal>> team;
    team.push_back(std::make_unique<DragonPal>("疾旋鼬", 120, 85, 50));
    team.push_back(std::make_unique<FirePal>("火绒狐", 90, 95, 60));

    for (const auto& pal : team) {
        // 运行时多态:根据实际类型调用对应的 special_move
        std::cout << pal->special_move() << std::endl;
    }

    // 多重继承
    WaterDragonPal wjxy("海龙疾旋鼬", 150, 100, 70);
    wjxy.fly();
    wjxy.swim();
    wjxy.dragon_roar();

    return 0;
}

模板与泛型编程

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <stdexcept>

// ===== 函数模板 =====
template<typename T>
T safe_max(T a, T b) {
    return (a > b) ? a : b;
}

// 模板特化
template<>
const char* safe_max<const char*>(const char* a, const char* b) {
    return (std::strcmp(a, b) > 0) ? a : b;
}

// ===== 类模板 =====
template<typename T, size_t N>
class FixedArray {
private:
    T data_[N];
    size_t size_ = 0;

public:
    // 添加元素
    void push_back(const T& value) {
        if (size_ >= N) {
            throw std::overflow_error("FixedArray 已满!疾旋鼬塞不下了!");
        }
        data_[size_++] = value;
    }

    // 访问元素(带边界检查)
    T& at(size_t index) {
        if (index >= size_) {
            throw std::out_of_range("索引越界!疾旋鼬跑出地图了!");
        }
        return data_[index];
    }

    const T& at(size_t index) const {
        if (index >= size_) {
            throw std::out_of_range("索引越界!");
        }
        return data_[index];
    }

    // 下标访问(不检查边界,性能优先)
    T& operator[](size_t index) { return data_[index]; }
    const T& operator[](size_t index) const { return data_[index]; }

    size_t size() const { return size_; }
    size_t capacity() const { return N; }

    // 迭代器支持
    T* begin() { return data_; }
    T* end() { return data_ + size_; }
    const T* begin() const { return data_; }
    const T* end() const { return data_ + size_; }
};

// ===== 可变参数模板(C++11)=====
template<typename T>
void print(const T& value) {
    std::cout << value << std::endl;
}

template<typename T, typename... Args>
void print(const T& first, const Args&... rest) {
    std::cout << first << " ";
    print(rest...);   // 递归展开
}

// ===== 模板元编程(编译期计算)=====
template<int N>
struct Factorial {
    static constexpr int value = N * Factorial<N - 1>::value;
};

template<>
struct Factorial<0> {
    static constexpr int value = 1;
};

int main() {
    // 函数模板
    std::cout << safe_max(3, 7) << std::endl;        // 7
    std::cout << safe_max(3.14, 2.71) << std::endl;  // 3.14

    // 类模板
    FixedArray<std::string, 3> pals;
    pals.push_back("疾旋鼬");
    pals.push_back("捣蛋猫");
    pals.push_back("桃旋鼬");
    // pals.push_back("火绒狐");  // 抛出异常

    for (const auto& pal : pals) {
        std::cout << pal << " ";
    }
    std::cout << std::endl;

    // 可变参数模板
    print("疾旋鼬", "等级:", 50, "HP:", 120);
    // 输出: 疾旋鼬 等级: 50 HP: 120

    // 编译期计算
    static_assert(Factorial<5>::value == 120, "5! 应该等于 120");
    std::cout << "5! = " << Factorial<5>::value << std::endl;

    return 0;
}

第三部分:现代 C++(C++11/14/17/20)

自动类型推导与范围 for

#include <iostream>
#include <vector>
#include <map>
#include <string>

int main() {
    // auto:让编译器推导类型
    auto x = 42;              // int
    auto pi = 3.14;           // double
    auto name = std::string("疾旋鼬");
    auto nums = std::vector<int>{1, 2, 3, 4, 5};

    // 范围 for(C++11)
    for (const auto& n : nums) {
        std::cout << n << " ";
    }
    std::cout << std::endl;

    // 结构化绑定(C++17)
    std::map<std::string, int> pal_hp = {
        {"疾旋鼬", 120},
        {"捣蛋猫", 80},
        {"桃旋鼬", 100},
    };

    for (const auto& [name, hp] : pal_hp) {
        std::cout << name << " HP=" << hp << std::endl;
    }

    // decltype:获取表达式的类型
    int a = 10;
    decltype(a) b = 20;   // b 的类型与 a 相同(int)

    return 0;
}

智能指针(内存安全的关键)

#include <iostream>
#include <memory>
#include <vector>
#include <string>

class Pal {
public:
    std::string name;
    int hp;

    Pal(const std::string& n, int h) : name(n), hp(h) {
        std::cout << name << " 被创建" << std::endl;
    }
    ~Pal() {
        std::cout << name << " 被销毁" << std::endl;
    }

    void status() const {
        std::cout << name << " HP=" << hp << std::endl;
    }
};

int main() {
    // ===== unique_ptr:独占所有权 =====
    // 同一时刻只有一个 unique_ptr 指向对象
    auto jxy = std::make_unique<Pal>("疾旋鼬", 120);
    jxy->status();

    // auto jxy2 = jxy;              // 编译错误!不能拷贝
    auto jxy2 = std::move(jxy);     // 可以移动,jxy 变为 nullptr
    // jxy->status();                // 未定义行为(jxy 已为空)
    jxy2->status();

    // unique_ptr 适合放在容器中
    std::vector<std::unique_ptr<Pal>> team;
    team.push_back(std::make_unique<Pal>("疾旋鼬", 120));
    team.push_back(std::make_unique<Pal>("捣蛋猫", 80));
    team.push_back(std::make_unique<Pal>("桃旋鼬", 100));

    for (const auto& pal : team) {
        pal->status();
    }
    // 离开作用域时自动释放所有帕鲁

    // ===== shared_ptr:共享所有权 =====
    // 多个 shared_ptr 可以指向同一对象,引用计数管理生命周期
    auto shared_jxy = std::make_shared<Pal>("疾旋鼬", 120);
    std::cout << "引用计数: " << shared_jxy.use_count() << std::endl;  // 1

    {
        auto copy = shared_jxy;    // 拷贝,引用计数 +1
        std::cout << "引用计数: " << shared_jxy.use_count() << std::endl;  // 2
        copy->status();
    }   // copy 离开作用域,引用计数 -1

    std::cout << "引用计数: " << shared_jxy.use_count() << std::endl;  // 1
    // 最后一个 shared_ptr 销毁时,对象被释放

    // ===== weak_ptr:弱引用(不增加引用计数)=====
    // 用于打破循环引用
    std::weak_ptr<Pal> weak_ref = shared_jxy;
    std::cout << "weak_ptr expired? " << weak_ref.expired() << std::endl;  // 0 (false)

    if (auto locked = weak_ref.lock()) {
        locked->status();   // 安全访问
    }

    shared_jxy.reset();     // 释放对象
    std::cout << "weak_ptr expired? " << weak_ref.expired() << std::endl;  // 1 (true)

    // ===== 自定义删除器 =====
    auto file_deleter = [](FILE* fp) {
        if (fp) {
            std::cout << "关闭文件" << std::endl;
            fclose(fp);
        }
    };
    std::unique_ptr<FILE, decltype(file_deleter)> file(
        fopen("data.txt", "w"), file_deleter);
    if (file) {
        fprintf(file.get(), "疾旋鼬写入的数据\n");
    }
    // 离开作用域时自动调用 file_deleter 关闭文件

    return 0;
}

RAII 与智能指针

RAII(Resource Acquisition Is Initialization)是 C++ 的核心理念:资源的获取即初始化,资源的释放即析构。智能指针是 RAII 的典型应用——它将堆内存的生命周期绑定到栈上对象的生命周期,当对象离开作用域时自动释放内存,从而杜绝内存泄漏、悬垂指针和 double free。

在安全编程中,永远优先使用智能指针而不是裸 new/delete

Lambda 表达式与函数式编程

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <numeric>

int main() {
    std::vector<int> nums = {5, 2, 8, 1, 9, 3, 7, 4, 6};

    // ===== Lambda 基础 =====
    // [捕获列表](参数列表) -> 返回类型 { 函数体 }
    auto add = [](int a, int b) -> int {
        return a + b;
    };
    std::cout << add(3, 4) << std::endl;  // 7

    // 捕获方式
    int base = 100;
    auto by_value   = [base](int x) { return base + x; };     // 值捕获(拷贝)
    auto by_ref     = [&base](int x) { base += x; };          // 引用捕获
    auto by_all_val = [=](int x) { return base + x; };        // 所有变量值捕获
    auto by_all_ref = [&]() { base++; };                       // 所有变量引用捕获
    auto mutable_lam = [base](int x) mutable {
        base++;         // 值捕获默认是 const,加 mutable 才能修改副本
        return base + x;
    };

    // ===== 与 STL 算法配合 =====
    // sort
    std::sort(nums.begin(), nums.end(), [](int a, int b) {
        return a > b;   // 降序
    });

    // for_each
    std::for_each(nums.begin(), nums.end(), [](int n) {
        std::cout << n << " ";
    });
    std::cout << std::endl;

    // transform
    std::vector<int> doubled(nums.size());
    std::transform(nums.begin(), nums.end(), doubled.begin(),
                   [](int n) { return n * 2; });

    // count_if
    auto big_count = std::count_if(nums.begin(), nums.end(),
                                   [](int n) { return n > 5; });
    std::cout << "大于5的数: " << big_count << " 个" << std::endl;

    // accumulate(带自定义操作)
    int product = std::accumulate(nums.begin(), nums.end(), 1,
                                  [](int a, int b) { return a * b; });

    // any_of / all_of / none_of
    bool has_even = std::any_of(nums.begin(), nums.end(),
                                [](int n) { return n % 2 == 0; });
    bool all_positive = std::all_of(nums.begin(), nums.end(),
                                    [](int n) { return n > 0; });

    // ===== std::function(通用函数包装器)=====
    std::function<int(int, int)> op;
    op = [](int a, int b) { return a + b; };
    std::cout << "加法: " << op(3, 4) << std::endl;

    op = [](int a, int b) { return a * b; };
    std::cout << "乘法: " << op(3, 4) << std::endl;

    return 0;
}

移动语义与完美转发

#include <iostream>
#include <utility>
#include <vector>
#include <string>
#include <cstring>

// 移动语义的核心:避免不必要的拷贝
class Buffer {
private:
    char* data_;
    size_t size_;

public:
    // 构造函数
    explicit Buffer(size_t size) : data_(new char[size]), size_(size) {
        std::memset(data_, 0, size);
        std::cout << "构造: 分配 " << size << " 字节" << std::endl;
    }

    // 拷贝构造(深拷贝:昂贵)
    Buffer(const Buffer& other) : data_(new char[other.size_]), size_(other.size_) {
        std::memcpy(data_, other.data_, size_);
        std::cout << "拷贝构造: 深拷贝 " << size_ << " 字节" << std::endl;
    }

    // 移动构造(窃取资源:廉价)
    Buffer(Buffer&& other) noexcept : data_(other.data_), size_(other.size_) {
        other.data_ = nullptr;   // 将源对象置空,防止 double free
        other.size_ = 0;
        std::cout << "移动构造: 窃取资源" << std::endl;
    }

    // 拷贝赋值
    Buffer& operator=(const Buffer& other) {
        if (this != &other) {
            delete[] data_;
            size_ = other.size_;
            data_ = new char[size_];
            std::memcpy(data_, other.data_, size_);
            std::cout << "拷贝赋值" << std::endl;
        }
        return *this;
    }

    // 移动赋值
    Buffer& operator=(Buffer&& other) noexcept {
        if (this != &other) {
            delete[] data_;
            data_ = other.data_;
            size_ = other.size_;
            other.data_ = nullptr;
            other.size_ = 0;
            std::cout << "移动赋值" << std::endl;
        }
        return *this;
    }

    ~Buffer() {
        delete[] data_;
    }

    size_t size() const { return size_; }
};

// std::move 将左值转为右值引用,启用移动语义
Buffer create_buffer() {
    Buffer buf(1024);
    return buf;   // 编译器可能应用 NRVO(命名返回值优化),直接在调用者内存构造
}

// 完美转发:保持参数的左/右值属性
template<typename T>
void wrapper(T&& arg) {
    // std::forward 保持 arg 的原始值类别
    // 如果传入左值,forward 后仍为左值;如果传入右值,forward 后仍为右值
    actual_function(std::forward<T>(arg));
}

int main() {
    // 移动 vs 拷贝
    Buffer a(1024);
    Buffer b = a;                // 拷贝构造
    Buffer c = std::move(a);     // 移动构造(a 不再拥有数据)
    // a.size() 此时为 0

    // vector 的移动优化
    std::vector<std::string> names;
    std::string long_name = "疾旋鼬AAAAAA很长很长的名字";
    names.push_back(long_name);             // 拷贝(long_name 之后还要用)
    names.push_back(std::move(long_name));  // 移动(long_name 被掏空)
    // long_name 此时为空

    // emplace_back: 在容器内就地构造,避免临时对象
    std::vector<Buffer> buffers;
    buffers.push_back(Buffer(256));          // 构造临时对象 → 移动到 vector
    buffers.emplace_back(256);              // 直接在 vector 内存中构造(更高效)

    return 0;
}

右值引用与引用折叠

#include <iostream>
#include <type_traits>

// C++ 中的引用类型:
// T&   左值引用(绑定到左值,即有名字的对象)
// T&&  右值引用(绑定到右值,即临时对象或将要销毁的对象)

void process(int& x) {
    std::cout << "左值引用: " << x << std::endl;
}

void process(int&& x) {
    std::cout << "右值引用: " << x << std::endl;
}

// 引用折叠规则(模板中 T&& 的推导):
// T& &   → T&
// T& &&  → T&
// T&& &  → T&
// T&& && → T&&
// 即:只要出现左值引用,结果就是左值引用

// 万能引用(Universal Reference / Forwarding Reference)
template<typename T>
void forward_example(T&& arg) {
    // T&& 在模板中是万能引用,可以绑定到左值或右值
    process(std::forward<T>(arg));  // 保持原始值类别
}

int main() {
    int x = 42;
    process(x);              // 调用左值引用版本
    process(42);             // 调用右值引用版本
    process(std::move(x));   // 调用右值引用版本

    forward_example(x);      // T = int&,  arg = int&
    forward_example(42);     // T = int,   arg = int&&

    return 0;
}

并发编程基础

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <future>
#include <vector>
#include <queue>
#include <functional>
#include <chrono>

// ===== 基本线程 =====
void pal_worker(int id) {
    std::cout << "帕鲁 " << id << " 开始工作……" << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::cout << "帕鲁 " << id << " 完成工作!" << std::endl;
}

// ===== 互斥锁 =====
std::mutex g_print_mutex;

void safe_print(const std::string& msg) {
    std::lock_guard<std::mutex> lock(g_print_mutex);  // RAII 风格加锁
    std::cout << msg << std::endl;
}   // 离开作用域自动解锁

// ===== 数据竞争示例 =====
int g_shared_counter = 0;
std::mutex g_counter_mutex;

void increment_unsafe(int times) {
    for (int i = 0; i < times; i++) {
        g_shared_counter++;    // 数据竞争!多线程同时读写
    }
}

void increment_safe(int times) {
    for (int i = 0; i < times; i++) {
        std::lock_guard<std::mutex> lock(g_counter_mutex);
        g_shared_counter++;
    }
}

// ===== 原子变量(无锁线程安全)=====
std::atomic<int> g_atomic_counter{0};

void increment_atomic(int times) {
    for (int i = 0; i < times; i++) {
        g_atomic_counter++;    // 原子操作,无需锁
    }
}

// ===== 条件变量 =====
std::queue<int> g_task_queue;
std::mutex g_queue_mutex;
std::condition_variable g_cv;
bool g_done = false;

void producer() {
    for (int i = 0; i < 10; i++) {
        {
            std::lock_guard<std::mutex> lock(g_queue_mutex);
            g_task_queue.push(i);
            std::cout << "生产任务: " << i << std::endl;
        }
        g_cv.notify_one();
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
    }
    {
        std::lock_guard<std::mutex> lock(g_queue_mutex);
        g_done = true;
    }
    g_cv.notify_all();
}

void consumer(const std::string& name) {
    while (true) {
        int task;
        {
            std::unique_lock<std::mutex> lock(g_queue_mutex);
            // wait: 释放锁并等待,被唤醒后重新获取锁
            g_cv.wait(lock, [] {
                return !g_task_queue.empty() || g_done;
            });
            if (g_task_queue.empty() && g_done) break;
            task = g_task_queue.front();
            g_task_queue.pop();
        }
        std::cout << name << " 处理任务: " << task << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

// ===== async / future =====
int compute_damage(int base, int level) {
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
    return base + level * 2;
}

// ===== 简易线程池 =====
class ThreadPool {
private:
    std::vector<std::thread> workers_;
    std::queue<std::function<void()>> tasks_;
    std::mutex mutex_;
    std::condition_variable cv_;
    bool stop_ = false;

public:
    explicit ThreadPool(size_t num_threads) {
        for (size_t i = 0; i < num_threads; i++) {
            workers_.emplace_back([this] {
                while (true) {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(mutex_);
                        cv_.wait(lock, [this] {
                            return stop_ || !tasks_.empty();
                        });
                        if (stop_ && tasks_.empty()) return;
                        task = std::move(tasks_.front());
                        tasks_.pop();
                    }
                    task();
                }
            });
        }
    }

    ~ThreadPool() {
        {
            std::lock_guard<std::mutex> lock(mutex_);
            stop_ = true;
        }
        cv_.notify_all();
        for (auto& w : workers_) {
            if (w.joinable()) w.join();
        }
    }

    template<typename F>
    void enqueue(F&& f) {
        {
            std::lock_guard<std::mutex> lock(mutex_);
            tasks_.emplace(std::forward<F>(f));
        }
        cv_.notify_one();
    }
};

int main() {
    // 基本线程
    std::thread t1(pal_worker, 1);
    std::thread t2(pal_worker, 2);
    t1.join();   // 等待线程结束
    t2.join();

    // 原子计数器
    std::thread t3(increment_atomic, 10000);
    std::thread t4(increment_atomic, 10000);
    t3.join();
    t4.join();
    std::cout << "原子计数器: " << g_atomic_counter << std::endl;  // 正确: 20000

    // 生产者-消费者
    std::thread prod(producer);
    std::thread cons1(consumer, "疾旋鼬");
    std::thread cons2(consumer, "桃旋鼬");
    prod.join();
    cons1.join();
    cons2.join();

    // async / future
    auto future1 = std::async(std::launch::async, compute_damage, 85, 50);
    auto future2 = std::async(std::launch::async, compute_damage, 60, 30);
    // 主线程可以做其他事情……
    int dmg1 = future1.get();   // 阻塞等待结果
    int dmg2 = future2.get();
    std::cout << "疾旋鼬伤害: " << dmg1 << ", 捣蛋猫伤害: " << dmg2 << std::endl;

    // 线程池
    {
        ThreadPool pool(4);
        for (int i = 0; i < 8; i++) {
            pool.enqueue([i] {
                safe_print("任务 " + std::to_string(i) + " 执行中……");
            });
        }
    }  // pool 析构时等待所有任务完成

    return 0;
}

C++17/20 新特性

#include <iostream>
#include <optional>
#include <variant>
#include <any>
#include <string_view>
#include <filesystem>
#include <tuple>
#include <format>       // C++20

// ===== std::optional(C++17):可能有值,可能没有 =====
std::optional<int> find_pal_hp(const std::string& name) {
    if (name == "疾旋鼬") return 120;
    if (name == "捣蛋猫") return 80;
    return std::nullopt;    // 未找到
}

// ===== std::variant(C++17):类型安全的 union =====
using PalValue = std::variant<int, double, std::string>;

void print_value(const PalValue& v) {
    std::visit([](const auto& val) {
        std::cout << val << std::endl;
    }, v);
}

// ===== std::string_view(C++17):非拥有的字符串引用 =====
// 避免不必要的字符串拷贝
void greet(std::string_view name) {
    std::cout << "你好," << name << "!" << std::endl;
}

// ===== 结构化绑定(C++17)=====
std::tuple<std::string, int, int> get_pal_info() {
    return {"疾旋鼬", 120, 85};
}

// ===== constexpr if(C++17)=====
template<typename T>
auto get_value(T&& t) {
    if constexpr (std::is_pointer_v<std::decay_t<T>>) {
        return *t;      // 解引用
    } else {
        return t;       // 直接返回
    }
}

// ===== Concepts(C++20)=====
#include <concepts>

// 定义概念:可以相加的类型
template<typename T>
concept Addable = requires(T a, T b) {
    { a + b } -> std::convertible_to<T>;
};

// 使用概念约束模板参数
template<Addable T>
T safe_add(T a, T b) {
    return a + b;
}

// Ranges(C++20)
#include <ranges>
#include <vector>

int main() {
    // optional
    auto hp = find_pal_hp("疾旋鼬");
    if (hp.has_value()) {
        std::cout << "疾旋鼬 HP: " << hp.value() << std::endl;
    }
    // 或使用 value_or 提供默认值
    int default_hp = find_pal_hp("未知帕鲁").value_or(50);

    // variant
    PalValue v1 = 42;
    PalValue v2 = 3.14;
    PalValue v3 = std::string("疾旋鼬");
    print_value(v1);    // 42
    print_value(v3);    // 疾旋鼬

    // string_view
    std::string long_name = "疾旋鼬AAAAAA";
    greet(long_name);           // 从 std::string 构造
    greet("桃旋鼬");             // 从字面量构造(零拷贝)

    // 结构化绑定
    auto [name, hp_stat, atk] = get_pal_info();
    std::cout << name << " HP=" << hp_stat << " ATK=" << atk << std::endl;

    // Ranges (C++20)
    std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    // 管道操作:筛选偶数 → 平方 → 取前3个
    auto result = nums
        | std::views::filter([](int n) { return n % 2 == 0; })
        | std::views::transform([](int n) { return n * n; })
        | std::views::take(3);

    for (int n : result) {
        std::cout << n << " ";   // 4 16 36
    }
    std::cout << std::endl;

    // filesystem (C++17)
    namespace fs = std::filesystem;
    for (const auto& entry : fs::directory_iterator(".")) {
        std::cout << entry.path() << std::endl;
    }

    return 0;
}

第四部分:操作系统编程

进程与系统调用

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>

int main(void) {
    // ===== 进程信息 =====
    printf("PID:  %d\n", getpid());     // 当前进程 ID
    printf("PPID: %d\n", getppid());    // 父进程 ID
    printf("UID:  %d\n", getuid());     // 用户 ID

    // ===== fork:创建子进程 =====
    pid_t pid = fork();

    if (pid < 0) {
        // fork 失败
        perror("fork 失败");
        return 1;
    } else if (pid == 0) {
        // 子进程
        printf("[子进程 %d] 疾旋鼬的分身!父进程是 %d\n", getpid(), getppid());

        // exec:替换当前进程的映像
        // 常见用法:子进程 fork 后 exec 执行新程序
        char *args[] = {"ls", "-la", NULL};
        // execvp("ls", args);   // 搜索 PATH 执行 ls

        // execl 也可以
        // execl("/bin/ls", "ls", "-la", NULL);

        exit(0);  // 子进程退出
    } else {
        // 父进程
        printf("[父进程 %d] 创建了子进程 %d\n", getpid(), pid);

        // waitpid:等待子进程结束
        int status;
        pid_t child_pid = waitpid(pid, &status, 0);

        if (WIFEXITED(status)) {
            printf("子进程正常退出,返回值: %d\n", WEXITSTATUS(status));
        } else if (WIFSIGNALED(status)) {
            printf("子进程被信号 %d 终止\n", WTERMSIG(status));
        }
    }

    // ===== 管道(Pipe) =====
    int pipefd[2];  // [0]=读端, [1]=写端
    if (pipe(pipefd) < 0) {
        perror("pipe 失败");
        return 1;
    }

    pid = fork();
    if (pid == 0) {
        // 子进程:写入管道
        close(pipefd[0]);   // 关闭读端
        const char *msg = "疾旋鼬发来消息!";
        write(pipefd[1], msg, strlen(msg) + 1);
        close(pipefd[1]);
        exit(0);
    } else {
        // 父进程:从管道读取
        close(pipefd[1]);   // 关闭写端
        char buf[256];
        ssize_t n = read(pipefd[0], buf, sizeof(buf));
        buf[n] = '\0';
        printf("父进程收到: %s\n", buf);
        close(pipefd[0]);
        waitpid(pid, NULL, 0);
    }

    // ===== 环境变量 =====
    const char *path = getenv("PATH");
    printf("PATH = %s\n", path ? path : "(null)");

    setenv("PAL_NAME", "疾旋鼬", 1);
    printf("PAL_NAME = %s\n", getenv("PAL_NAME"));

    return 0;
}

信号处理

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

volatile sig_atomic_t g_interrupted = 0;

void signal_handler(int signum) {
    // 信号处理函数中只能使用 async-signal-safe 的函数
    // write() 是安全的,printf() 不是(但实践中常用)
    const char *msg;
    switch (signum) {
        case SIGINT:
            msg = "\n疾旋鼬被 Ctrl+C 打断了!再按一次退出。\n";
            g_interrupted = 1;
            break;
        case SIGSEGV:
            msg = "\n疾旋鼬检测到段错误!可能是内存越界。\n";
            _exit(1);   // 不要用 exit()(不安全)
            break;
        case SIGTERM:
            msg = "\n疾旋鼬收到终止信号,优雅退出……\n";
            _exit(0);
            break;
        default:
            msg = "\n收到未知信号\n";
            break;
    }
    write(STDERR_FILENO, msg, strlen(msg));
}

int main(void) {
    // 注册信号处理函数
    signal(SIGINT, signal_handler);    // Ctrl+C
    signal(SIGSEGV, signal_handler);   // 段错误
    signal(SIGTERM, signal_handler);   // kill 命令

    // 忽略信号
    signal(SIGPIPE, SIG_IGN);          // 忽略管道断裂

    printf("疾旋鼬正在运行……按 Ctrl+C 试试!\n");

    while (!g_interrupted) {
        sleep(1);
        printf("疾旋鼬在旋转中……\n");
    }

    printf("疾旋鼬安全退出!\n");
    return 0;
}

内存映射(mmap)

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <string.h>

int main(void) {
    const char *filename = "pal_mmap.dat";
    const char *data = "疾旋鼬通过 mmap 写入的数据!AAAABBBBCCCCDDDD";
    size_t data_len = strlen(data) + 1;

    // ===== 创建并写入文件 =====
    int fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
    ftruncate(fd, data_len);   // 设置文件大小

    // 内存映射文件
    void *mapped = mmap(NULL, data_len, PROT_READ | PROT_WRITE,
                        MAP_SHARED, fd, 0);
    if (mapped == MAP_FAILED) {
        perror("mmap 失败");
        close(fd);
        return 1;
    }

    // 通过内存操作写入文件(像操作数组一样)
    memcpy(mapped, data, data_len);
    printf("已写入: %s\n", (char *)mapped);

    // 修改映射内存 → 直接修改文件
    char *p = (char *)mapped;
    p[0] = 'X';  // 修改第一个字节
    printf("修改后: %s\n", (char *)mapped);

    // 同步到磁盘
    msync(mapped, data_len, MS_SYNC);

    // 解除映射
    munmap(mapped, data_len);
    close(fd);

    // ===== 只读映射(用于读取大文件)=====
    fd = open(filename, O_RDONLY);
    struct stat st;
    fstat(fd, &st);

    mapped = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (mapped != MAP_FAILED) {
        printf("读取: %s\n", (char *)mapped);
        // 可以直接通过指针读取,无需 read() 系统调用
        munmap(mapped, st.st_size);
    }
    close(fd);

    unlink(filename);  // 清理
    return 0;
}

mmap 与安全

mmap 在安全领域有重要应用: - Shellcode 执行mmap 分配可读写可执行(RWX)内存,用于注入 shellcode - 文件分析:大文件通过 mmap 映射后直接指针访问,避免内存拷贝 - 共享内存:进程间通信的基础机制 - 漏洞利用:ROP/ret2libc 等技术常涉及内存页权限操作(mprotect

虚拟内存与页表操作

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdint.h>
#include <signal.h>

// ===== 自定义信号处理器捕获 SIGSEGV =====
// 这可以实现"按需分配"的虚拟内存
void *fault_handler_addr = NULL;

void segv_handler(int sig, siginfo_t *info, void *context) {
    (void)sig;
    void *fault_addr = info->si_addr;

    if (fault_addr == fault_handler_addr) {
        // 按需分配物理页面
        void *page = mmap(fault_addr, 4096,
                          PROT_READ | PROT_WRITE,
                          MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
                          -1, 0);
        if (page == MAP_FAILED) {
            _exit(1);
        }
        // 初始化内容
        memset(page, 0, 4096);
        strcpy((char *)page, "疾旋鼬按需分配的页面!");
    } else {
        // 真正的段错误
        write(STDERR_FILENO, "真正的段错误!\n", 15);
        _exit(1);
    }
}

int main(void) {
    // ===== 查看虚拟地址空间 =====
    printf("页面大小: %ld\n", sysconf(_SC_PAGESIZE));   // 通常 4096

    // ===== 自定义页错误处理 =====
    struct sigaction sa;
    sa.sa_sigaction = segv_handler;
    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGSEGV, &sa, NULL);

    // 先不分配物理内存,只保留虚拟地址
    fault_handler_addr = mmap(NULL, 4096,
                              PROT_NONE,    // 无权限 → 访问必触发 SIGSEGV
                              MAP_PRIVATE | MAP_ANONYMOUS,
                              -1, 0);

    printf("虚拟地址已保留: %p(尚无物理内存)\n", fault_handler_addr);

    // 第一次访问 → 触发 SIGSEGV → 信号处理器分配物理页面
    printf("内容: %s\n", (char *)fault_handler_addr);

    // 后续访问正常(物理页面已分配)
    printf("再次访问: %s\n", (char *)fault_handler_addr);

    // ===== mprotect:修改页面权限 =====
    void *code_page = mmap(NULL, 4096,
                           PROT_READ | PROT_WRITE,
                           MAP_PRIVATE | MAP_ANONYMOUS,
                           -1, 0);

    // 写入机器码
    // mov eax, 42; ret
    unsigned char shellcode[] = {0xb8, 0x2a, 0x00, 0x00, 0x00, 0xc3};
    memcpy(code_page, shellcode, sizeof(shellcode));

    // 将页面设为可读可执行(去掉可写权限)
    mprotect(code_page, 4096, PROT_READ | PROT_EXEC);

    // 将内存中的机器码转为函数指针并调用
    typedef int (*func_t)(void);
    func_t func = (func_t)code_page;
    int result = func();
    printf("执行结果: %d\n", result);  // 42

    munmap(fault_handler_addr, 4096);
    munmap(code_page, 4096);
    return 0;
}

第五部分:安全应用实践

栈溢出原理演示

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/*
栈帧布局(x86-64,从高地址到低地址):

高地址
┌───────────────────┐
│   返回地址 (8B)    │  ← 覆盖这里可以劫持控制流
├───────────────────┤
│   保存的 RBP (8B)  │  ← 帧指针
├───────────────────┤
│   局部变量         │  ← buffer[16]
│   buffer[0..15]    │
├───────────────────┤
低地址

如果写入超过 buffer 的大小,会覆盖 RBP 和返回地址。
*/

// 疾旋鼬的安全检查程序
void secret_function(void) {
    printf("🎉 疾旋鼬发现了隐藏关卡!\n");
    printf("这就是栈溢出劫持控制流的原理!\n");
}

void vulnerable_function(const char *input) {
    char buffer[16];     // 只有 16 字节的缓冲区
    printf("buffer 地址: %p\n", (void *)buffer);
    printf("返回地址应该在: %p 附近\n", __builtin_return_address(0));

    // 危险!没有长度检查!
    // 如果 input 超过 16 字节,会溢出 buffer
    strcpy(buffer, input);
    printf("你输入了: %s\n", buffer);
}

void safe_function(const char *input) {
    char buffer[16];
    // 安全版本:使用 strncpy 并确保 null 结尾
    strncpy(buffer, input, sizeof(buffer) - 1);
    buffer[sizeof(buffer) - 1] = '\0';
    printf("安全输入: %s\n", buffer);
}

int main(int argc, char *argv[]) {
    printf("=== 疾旋鼬的栈溢出演示 ===\n\n");

    // 正常输入
    printf("--- 正常输入 ---\n");
    vulnerable_function("疾旋鼬");
    printf("\n");

    // 超长输入(会溢出,但在现代系统中会触发栈保护)
    printf("--- 超长输入(演示溢出概念) ---\n");
    // 为了安全,这里只是概念演示,不会真正执行溢出攻击
    // 在没有栈保护(-fno-stack-protector)的程序中:
    // vulnerable_function("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");

    // 安全输入
    printf("--- 安全输入 ---\n");
    safe_function("疾旋鼬AAAABBBBCCCCDDDD");
    printf("\n");

    // ===== 整数溢出演示 =====
    printf("=== 整数溢出演示 ===\n");
    int size = 2147483647;  // INT_MAX
    int new_size = size + 1;   // 溢出!变为 INT_MIN(负数)
    printf("原大小: %d\n", size);
    printf("加1后:  %d\n", new_size);   // -2147483648

    if (new_size > 0) {
        // 如果没有检查溢出,会分配一个极小的缓冲区
        printf("分配 %d 字节(太小了!)\n", new_size);
    } else {
        printf("检测到整数溢出!拒绝分配。\n");
    }

    // ===== 格式化字符串漏洞概念 =====
    printf("\n=== 格式化字符串概念 ===\n");
    char *user_input = "疾旋鼬";

    // 正常用法
    printf("正常: %s\n", user_input);

    // 如果攻击者控制了格式化字符串:
    // printf(user_input);        // 危险!
    // printf("%x %x %x %x");    // 可以读取栈上的数据
    // printf("%n");              // 可以写入内存!

    // 安全做法:始终使用格式化字符串
    printf("安全: %s\n", user_input);   // 格式化字符串是常量

    return 0;
}

堆利用技术概念

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
堆利用技术概述(概念性,非实战代码):

1. Use After Free (UAF)
   - free(ptr) 后继续使用 ptr
   - 攻击者可抢先分配同一块内存,控制其内容

2. Double Free
   - 对同一指针调用 free() 两次
   - 可以破坏堆元数据,实现任意地址写

3. Heap Overflow
   - 写入超过分配大小
   - 可以覆盖相邻的堆块元数据或数据

4. House of Spirit
   - 伪造堆块,让 free() 释放攻击者控制的内存

5. Unlink Exploit
   - 利用双向链表的 unlink 操作实现任意地址写
*/

// ===== Use After Free 演示 =====
typedef struct {
    char name[32];
    int  is_admin;
    void (*action)(void);
} User;

void normal_action(void) {
    printf("普通用户操作\n");
}

void admin_action(void) {
    printf("🔥 管理员操作:获得 root 权限!\n");
}

void uaf_demo(void) {
    printf("=== UAF 概念演示 ===\n");

    // 步骤 1: 分配一个 User
    User *user1 = (User *)malloc(sizeof(User));
    strcpy(user1->name, "疾旋鼬");
    user1->is_admin = 0;
    user1->action = normal_action;

    printf("user1: %s, admin=%d\n", user1->name, user1->is_admin);
    user1->action();

    // 步骤 2: 释放 user1
    free(user1);
    printf("user1 已被释放\n");

    // 步骤 3: 攻击者分配一块相同大小的内存
    // (在真实攻击中,攻击者会精心控制这次分配)
    User *attacker = (User *)malloc(sizeof(User));
    strcpy(attacker->name, "攻击者帕鲁");
    attacker->is_admin = 1;
    attacker->action = admin_action;

    // 步骤 4: 如果程序继续使用已释放的 user1(UAF)
    // 在某些分配器实现中,user1 和 attacker 可能指向同一内存
    if ((void *)user1 == (void *)attacker) {
        printf("user1 和 attacker 指向同一内存!\n");
        user1->action();  // 触发了攻击者注入的函数!
    } else {
        printf("内存地址不同(现代分配器有防护)\n");
        printf("user1: %p, attacker: %p\n", (void *)user1, (void *)attacker);
    }

    free(attacker);
}

// ===== 堆布局概念 =====
void heap_layout_demo(void) {
    printf("\n=== 堆布局概念 ===\n");

    // 连续分配会产生相邻的堆块
    void *a = malloc(32);
    void *b = malloc(32);
    void *c = malloc(32);

    printf("块 A: %p\n", a);
    printf("块 B: %p\n", b);
    printf("块 C: %p\n", c);
    printf("A 和 B 的距离: %ld 字节\n", (char *)b - (char *)a);
    printf("B 和 C 的距离: %ld 字节\n", (char *)c - (char *)b);

    // 如果溢出块 A,可以覆盖块 B 的元数据或内容
    // 这就是堆溢出攻击的基础

    free(a);
    free(b);
    free(c);
}

int main(void) {
    uaf_demo();
    heap_layout_demo();
    return 0;
}

加密算法实现

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>

// ===== XOR 加密(最简单的加密)=====
void xor_encrypt(uint8_t *data, size_t len, const uint8_t *key, size_t key_len) {
    for (size_t i = 0; i < len; i++) {
        data[i] ^= key[i % key_len];
    }
}

// ===== Base64 编码 =====
static const char base64_table[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

char *base64_encode(const uint8_t *data, size_t len) {
    size_t out_len = 4 * ((len + 2) / 3);
    char *out = (char *)malloc(out_len + 1);
    if (!out) return NULL;

    size_t i = 0, j = 0;
    while (i < len) {
        uint32_t octet_a = i < len ? data[i++] : 0;
        uint32_t octet_b = i < len ? data[i++] : 0;
        uint32_t octet_c = i < len ? data[i++] : 0;
        uint32_t triple = (octet_a << 16) | (octet_b << 8) | octet_c;

        out[j++] = base64_table[(triple >> 18) & 0x3F];
        out[j++] = base64_table[(triple >> 12) & 0x3F];
        out[j++] = (i > len + 1) ? '=' : base64_table[(triple >> 6) & 0x3F];
        out[j++] = (i > len)     ? '=' : base64_table[triple & 0x3F];
    }
    out[j] = '\0';
    return out;
}

// ===== SHA-256 概念结构 =====
// 完整实现太长,这里展示结构和核心操作
typedef struct {
    uint32_t state[8];       // 哈希状态
    uint64_t count;          // 已处理的位数
    uint8_t  buffer[64];     // 缓冲区
} sha256_ctx;

// 循环右移
#define ROTR(x, n) (((x) >> (n)) | ((x) << (32 - (n))))

// SHA-256 基本函数
#define CH(x, y, z)  (((x) & (y)) ^ (~(x) & (z)))
#define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define EP0(x)        (ROTR(x, 2)  ^ ROTR(x, 13) ^ ROTR(x, 22))
#define EP1(x)        (ROTR(x, 6)  ^ ROTR(x, 11) ^ ROTR(x, 25))

// ===== 简单 CRC32 =====
uint32_t crc32(const uint8_t *data, size_t len) {
    uint32_t crc = 0xFFFFFFFF;
    for (size_t i = 0; i < len; i++) {
        crc ^= data[i];
        for (int j = 0; j < 8; j++) {
            crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1));
        }
    }
    return ~crc;
}

int main(void) {
    // XOR 加密
    const char *plaintext = "疾旋鼬的机密数据";
    size_t len = strlen(plaintext);
    uint8_t *data = (uint8_t *)malloc(len);
    memcpy(data, plaintext, len);

    const uint8_t key[] = {0x42, 0x37, 0xA5, 0x9F};
    size_t key_len = sizeof(key);

    printf("原文: %s\n", (char *)data);
    xor_encrypt(data, len, key, key_len);
    printf("加密: ");
    for (size_t i = 0; i < len; i++) printf("%02X ", data[i]);
    printf("\n");

    xor_encrypt(data, len, key, key_len);  // 再次 XOR 解密
    printf("解密: %s\n", (char *)data);

    // Base64
    char *encoded = base64_encode((uint8_t *)"疾旋鼬", 9);
    printf("Base64: %s\n", encoded);
    free(encoded);

    // CRC32
    uint32_t hash = crc32((uint8_t *)"疾旋鼬", 9);
    printf("CRC32: 0x%08X\n", hash);

    free(data);
    return 0;
}

网络编程(Socket)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

// ===== TCP 服务器 =====
int start_server(int port) {
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) {
        perror("socket 失败");
        return -1;
    }

    // 允许端口复用
    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    struct sockaddr_in addr = {
        .sin_family = AF_INET,
        .sin_port = htons(port),
        .sin_addr.s_addr = INADDR_ANY,
    };

    if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("bind 失败");
        close(server_fd);
        return -1;
    }

    if (listen(server_fd, 5) < 0) {
        perror("listen 失败");
        close(server_fd);
        return -1;
    }

    printf("疾旋鼬服务器监听端口 %d ……\n", port);

    while (1) {
        struct sockaddr_in client_addr;
        socklen_t client_len = sizeof(client_addr);
        int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);
        if (client_fd < 0) {
            perror("accept 失败");
            continue;
        }

        char client_ip[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip));
        printf("连接来自 %s:%d\n", client_ip, ntohs(client_addr.sin_port));

        // 接收数据
        char buf[1024];
        ssize_t n = recv(client_fd, buf, sizeof(buf) - 1, 0);
        if (n > 0) {
            buf[n] = '\0';
            printf("收到: %s\n", buf);

            // 发送响应
            const char *response = "疾旋鼬已收到消息!";
            send(client_fd, response, strlen(response), 0);
        }

        close(client_fd);
    }

    close(server_fd);
    return 0;
}

// ===== TCP 客户端 =====
int connect_to_server(const char *host, int port) {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket 失败");
        return -1;
    }

    struct sockaddr_in addr = {
        .sin_family = AF_INET,
        .sin_port = htons(port),
    };
    inet_pton(AF_INET, host, &addr.sin_addr);

    if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("connect 失败");
        close(sockfd);
        return -1;
    }

    // 发送数据
    const char *msg = "你好,疾旋鼬服务器!";
    send(sockfd, msg, strlen(msg), 0);

    // 接收响应
    char buf[1024];
    ssize_t n = recv(sockfd, buf, sizeof(buf) - 1, 0);
    if (n > 0) {
        buf[n] = '\0';
        printf("服务器响应: %s\n", buf);
    }

    close(sockfd);
    return 0;
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("用法:\n");
        printf("  %s server <port>\n", argv[0]);
        printf("  %s client <host> <port>\n", argv[0]);
        return 1;
    }

    if (strcmp(argv[1], "server") == 0) {
        int port = argc > 2 ? atoi(argv[2]) : 8080;
        start_server(port);
    } else if (strcmp(argv[1], "client") == 0) {
        const char *host = argc > 2 ? argv[2] : "127.0.0.1";
        int port = argc > 3 ? atoi(argv[3]) : 8080;
        connect_to_server(host, port);
    }

    return 0;
}

ELF 文件解析

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>

// ELF 魔数
#define ELFMAG      "\x7fELF"
#define ELFMAG_LEN  4

// ELF 类型
#define ET_NONE     0
#define ET_REL      1   // 可重定位文件 (.o)
#define ET_EXEC     2   // 可执行文件
#define ET_DYN      3   // 共享目标文件 (.so)

// 机器类型
#define EM_386      3   // x86
#define EM_X86_64   62  // x86-64
#define EM_ARM      40  // ARM
#define EM_AARCH64  183 // ARM64

// 简化的 ELF64 文件头
typedef struct {
    uint8_t  e_ident[16];    // ELF 标识
    uint16_t e_type;         // 文件类型
    uint16_t e_machine;      // 目标架构
    uint32_t e_version;      // 版本
    uint64_t e_entry;        // 入口点地址
    uint64_t e_phoff;        // 程序头表偏移
    uint64_t e_shoff;        // 节头表偏移
    uint32_t e_flags;        // 标志
    uint16_t e_ehsize;       // 文件头大小
    uint16_t e_phentsize;    // 程序头条目大小
    uint16_t e_phnum;        // 程序头条目数
    uint16_t e_shentsize;    // 节头条目大小
    uint16_t e_shnum;        // 节头条目数
    uint16_t e_shstrndx;     // 节名字符串表索引
} Elf64_Ehdr;

// 简化的 ELF64 节头
typedef struct {
    uint32_t sh_name;        // 节名(字符串表索引)
    uint32_t sh_type;        // 节类型
    uint64_t sh_flags;       // 节标志
    uint64_t sh_addr;        // 虚拟地址
    uint64_t sh_offset;      // 文件偏移
    uint64_t sh_size;        // 节大小
    uint32_t sh_link;        // 链接
    uint32_t sh_info;        // 信息
    uint64_t sh_addralign;   // 对齐
    uint64_t sh_entsize;     // 条目大小
} Elf64_Shdr;

// 节类型名称
const char *section_type_name(uint32_t type) {
    switch (type) {
        case 0:  return "NULL";
        case 1:  return "PROGBITS";
        case 2:  return "SYMTAB";
        case 3:  return "STRTAB";
        case 4:  return "RELA";
        case 5:  return "HASH";
        case 6:  return "DYNAMIC";
        case 7:  return "NOTE";
        case 8:  return "NOBITS";
        case 9:  return "REL";
        case 11: return "DYNSYM";
        default: return "UNKNOWN";
    }
}

// 架构名称
const char *machine_name(uint16_t machine) {
    switch (machine) {
        case EM_386:     return "x86 (IA-32)";
        case EM_X86_64:  return "x86-64";
        case EM_ARM:     return "ARM";
        case EM_AARCH64: return "AArch64 (ARM64)";
        default:         return "Unknown";
    }
}

// 文件类型名称
const char *elf_type_name(uint16_t type) {
    switch (type) {
        case ET_NONE: return "NONE (无类型)";
        case ET_REL:  return "REL (可重定位)";
        case ET_EXEC: return "EXEC (可执行)";
        case ET_DYN:  return "DYN (共享库)";
        default:      return "Unknown";
    }
}

int analyze_elf(const char *filepath) {
    int fd = open(filepath, O_RDONLY);
    if (fd < 0) {
        perror("打开文件失败");
        return 1;
    }

    struct stat st;
    fstat(fd, &st);
    size_t filesize = st.st_size;

    void *mapped = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0);
    if (mapped == MAP_FAILED) {
        perror("mmap 失败");
        close(fd);
        return 1;
    }

    const uint8_t *data = (const uint8_t *)mapped;

    // 检查 ELF 魔数
    if (memcmp(data, ELFMAG, ELFMAG_LEN) != 0) {
        printf("不是有效的 ELF 文件!\n");
        munmap(mapped, filesize);
        close(fd);
        return 1;
    }

    // 解析 ELF 类
    uint8_t elf_class = data[4];   // 1=32位, 2=64位
    uint8_t endian = data[5];      // 1=小端, 2=大端

    printf("=== 疾旋鼬的 ELF 分析器 ===\n\n");
    printf("文件: %s\n", filepath);
    printf("大小: %zu 字节\n", filesize);
    printf("ELF 类: %s\n", elf_class == 2 ? "ELF64" : "ELF32");
    printf("字节序: %s\n", endian == 1 ? "小端" : "大端");

    if (elf_class != 2) {
        printf("仅支持 ELF64 分析\n");
        munmap(mapped, filesize);
        close(fd);
        return 1;
    }

    // 解析文件头
    const Elf64_Ehdr *ehdr = (const Elf64_Ehdr *)data;

    printf("\n=== 文件头 ===\n");
    printf("类型:     %s\n", elf_type_name(ehdr->e_type));
    printf("架构:     %s\n", machine_name(ehdr->e_machine));
    printf("入口点:   0x%016lx\n", ehdr->e_entry);
    printf("程序头:   偏移 0x%lx, %d 个条目\n", ehdr->e_phoff, ehdr->e_phnum);
    printf("节头:     偏移 0x%lx, %d 个条目\n", ehdr->e_shoff, ehdr->e_shnum);

    // 解析节头表
    if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
        printf("\n=== 节头表 ===\n");
        const Elf64_Shdr *shdr_table = (const Elf64_Shdr *)(data + ehdr->e_shoff);

        // 获取节名字符串表
        const Elf64_Shdr *shstrtab = &shdr_table[ehdr->e_shstrndx];
        const char *shstrtab_data = (const char *)(data + shstrtab->sh_offset);

        printf("%-20s %-12s %-18s %-18s %s\n",
               "名称", "类型", "地址", "大小", "标志");
        printf("%-20s %-12s %-18s %-18s %s\n",
               "----", "----", "----", "----", "----");

        for (int i = 0; i < ehdr->e_shnum; i++) {
            const Elf64_Shdr *sh = &shdr_table[i];
            const char *name = shstrtab_data + sh->sh_name;

            // 构建标志字符串
            char flags[16] = "";
            int fi = 0;
            if (sh->sh_flags & 0x1)  flags[fi++] = 'W';  // 可写
            if (sh->sh_flags & 0x2)  flags[fi++] = 'A';  // 分配
            if (sh->sh_flags & 0x4)  flags[fi++] = 'X';  // 可执行
            flags[fi] = '\0';

            printf("%-20s %-12s 0x%016lx 0x%016lx %s\n",
                   name, section_type_name(sh->sh_type),
                   sh->sh_addr, sh->sh_size, flags);
        }

        // 查找并打印 .text 段大小(代码段)
        for (int i = 0; i < ehdr->e_shnum; i++) {
            const char *name = shstrtab_data + shdr_table[i].sh_name;
            if (strcmp(name, ".text") == 0) {
                printf("\n.text 段: 0x%lx 字节\n", shdr_table[i].sh_size);
                break;
            }
        }
    }

    munmap(mapped, filesize);
    close(fd);
    return 0;
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("用法: %s <elf-file>\n", argv[0]);
        printf("示例: %s /bin/ls\n", argv[0]);
        return 1;
    }
    return analyze_elf(argv[1]);
}

安全编码实践清单

/*
 * 疾旋鼬的安全编码清单(C/C++)
 *
 * 1. 缓冲区安全
 *    ✗ strcpy, strcat, sprintf, gets
 *    ✓ strncpy, strncat, snprintf, fgets
 *    ✓ 始终检查边界,确保 null 结尾
 *
 * 2. 整数安全
 *    ✗ 有符号/无符号混用
 *    ✓ 使用 <stdint.h> 定长类型
 *    ✓ 加法/乘法前检查是否溢出
 *    ✓ size_t 是无符号的:size_t a - size_t b 当 a < b 时会下溢
 *
 * 3. 指针安全
 *    ✗ 解引用前不检查 NULL
 *    ✓ free 后置 NULL
 *    ✓ 使用智能指针(C++)
 *    ✓ 避免悬垂指针
 *
 * 4. 格式化字符串
 *    ✗ printf(user_input)
 *    ✓ printf("%s", user_input)
 *
 * 5. 内存管理
 *    ✗ malloc 不检查返回值
 *    ✓ 所有 malloc/calloc/realloc 都检查返回值
 *    ✓ 避免 double free
 *    ✓ 避免内存泄漏(使用 RAII / 智能指针)
 *
 * 6. 编译器保护
 *    -fstack-protector-all    栈保护(Canary)
 *    -D_FORTIFY_SOURCE=2      运行时缓冲区检查
 *    -fPIE -pie               地址无关可执行(ASLR)
 *    -Wl,-z,relro,-z,now      RELRO(保护 GOT)
 *    -Wl,-z,noexecstack       不可执行栈(NX)
 */

// 安全的内存分配宏
#define SAFE_MALLOC(ptr, size) do {                     \
    (ptr) = malloc(size);                               \
    if (!(ptr)) {                                       \
        fprintf(stderr, "内存分配失败: %s:%d\n",        \
                __FILE__, __LINE__);                    \
        abort();                                        \
    }                                                   \
} while (0)

// 安全的字符串拷贝
#define SAFE_STRCPY(dst, src, size) do {                \
    strncpy(dst, src, (size) - 1);                      \
    (dst)[(size) - 1] = '\0';                           \
} while (0)

// 安全的字符串拼接
#define SAFE_STRCAT(dst, src, size) do {                \
    size_t _len = strlen(dst);                          \
    if (_len < (size) - 1) {                            \
        strncat(dst, src, (size) - _len - 1);           \
    }                                                   \
} while (0)

int main(void) {
    // 使用安全宏
    char name[32];
    SAFE_STRCPY(name, "疾旋鼬AAAABBBBCCCCDDDD", sizeof(name));
    printf("名字: %s\n", name);

    int *nums;
    SAFE_MALLOC(nums, 10 * sizeof(int));
    for (int i = 0; i < 10; i++) nums[i] = i;
    free(nums);
    nums = NULL;  // 置 NULL 防止悬垂指针

    return 0;
}

附录:常用工具与编译选项

GCC 编译选项速查

# 基本编译
gcc -o output source.c -Wall -Wextra -std=c11

# C++ 编译
g++ -o output source.cpp -Wall -Wextra -std=c++20

# 调试编译(生成调试信息)
gcc -g -O0 -o output source.c

# 安全加固编译
gcc -o secure source.c \
    -Wall -Wextra -Werror \
    -fstack-protector-all \
    -D_FORTIFY_SOURCE=2 \
    -fPIE -pie \
    -Wl,-z,relro,-z,now \
    -Wl,-z,noexecstack \
    -O2

# 查看安全特性是否启用
checksec --file=output     # 需要 pwntools

# 反汇编
objdump -d output          # 反汇编所有代码段
objdump -d -M intel output # Intel 格式

# 查看符号
nm output                  # 查看符号表
readelf -s output          # ELF 符号信息

# 查看段信息
readelf -S output          # 节头表
readelf -l output          # 程序头表

# strip:去除调试信息和符号
strip output

GDB 调试速查

# 启动 GDB
gdb ./program
gdb -q ./program           # 安静模式

# 运行
(gdb) run                  # 运行程序
(gdb) run arg1 arg2        # 带参数运行

# 断点
(gdb) break main           # 在 main 函数设断点
(gdb) break file.c:42      # 在指定行设断点
(gdb) break *0x401234      # 在指定地址设断点
(gdb) delete 1             # 删除断点 1
(gdb) info breakpoints     # 列出所有断点

# 执行
(gdb) step                 # 单步进入函数
(gdb) next                 # 单步跳过函数
(gdb) continue             # 继续执行
(gdb) finish               # 执行到当前函数返回

# 查看信息
(gdb) info registers       # 查看所有寄存器
(gdb) info registers rax rip  # 查看指定寄存器
(gdb) x/10x $rsp          # 查看栈上 10 个字(十六进制)
(gdb) x/s 0x401234         # 查看字符串
(gdb) x/20i $rip           # 查看接下来 20 条指令
(gdb) info stack           # 查看调用栈
(gdb) backtrace            # 同上

# 修改
(gdb) set $rax = 42        # 修改寄存器
(gdb) set *(int *)0x601000 = 1  # 修改内存

# 内存布局
(gdb) info proc mappings   # 查看内存映射
(gdb) vmmap                # 同上(需要 pwndbg/peda)

# pwndbg/peda 插件(推荐安装)
(gdb) pwndbg> heap         # 查看堆信息
(gdb) pwndbg> bins         # 查看 bins(tcache, fastbin 等)

2026.05.11

推荐阅读