C语言进阶知识概览(二)

简介: 教程来源 https://app-ad5sxofh8phd.appmiaoda.com 本节涵盖C语言进阶核心:文件随机访问与内存映射(mmap)、目录遍历与文件锁;预处理高级技巧(可变参数宏、静态断言)与多文件项目组织;进程控制、管道/共享内存IPC;以及循环展开、位运算、缓存友好等性能优化方法。体系完整,实践性强。

四、文件系统与IO高级操作

4.1 文件随机访问与内存映射

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

/**
 * 文件随机访问
 */
void random_file_access() {
    FILE *fp = fopen("data.bin", "wb+");
    if (!fp) return;

    // 写入固定大小的记录
    typedef struct {
        int id;
        char name[32];
        float score;
    } Record;

    Record records[] = {
        {1, "张三", 85.5},
        {2, "李四", 92.0},
        {3, "王五", 78.5},
        {4, "赵六", 88.0}
    };

    fwrite(records, sizeof(Record), 4, fp);

    // 随机访问:直接定位到第3条记录(索引2)
    fseek(fp, 2 * sizeof(Record), SEEK_SET);
    Record r;
    fread(&r, sizeof(Record), 1, fp);
    printf("第3条记录:id=%d, name=%s, score=%.1f\n", r.id, r.name, r.score);

    // 修改第2条记录
    Record newRecord = {2, "李四_updated", 95.0};
    fseek(fp, 1 * sizeof(Record), SEEK_SET);
    fwrite(&newRecord, sizeof(Record), 1, fp);

    fclose(fp);
}

/**
 * 内存映射文件(mmap)
 * 将文件映射到内存,像访问内存一样访问文件
 */
void mmap_file_demo() {
    const char *filename = "mmap_test.txt";
    const char *content = "Hello, Memory Mapped File!\n";

    // 写入文件
    int fd = open(filename, O_RDWR | O_CREAT, 0644);
    if (fd == -1) {
        perror("open");
        return;
    }

    size_t len = strlen(content);
    if (ftruncate(fd, len) == -1) {
        perror("ftruncate");
        close(fd);
        return;
    }

    // 内存映射
    char *mapped = (char*)mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (mapped == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return;
    }

    // 直接通过内存写入
    memcpy(mapped, content, len);

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

    printf("写入成功,映射地址:%p\n", mapped);
    printf("读取内容:%s", mapped);

    // 修改内存映射的内容
    mapped[0] = 'h';
    printf("修改后:%s", mapped);

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

/**
 * 文件锁(防止多进程同时写入)
 */
#include <fcntl.h>

void file_lock_demo() {
    int fd = open("shared.dat", O_RDWR | O_CREAT, 0644);
    if (fd == -1) {
        perror("open");
        return;
    }

    struct flock lock;
    lock.l_type = F_WRLCK;      // 写锁
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0;             // 锁定整个文件
    lock.l_pid = getpid();

    // 尝试获取锁
    if (fcntl(fd, F_SETLK, &lock) == -1) {
        perror("fcntl");
        printf("文件已被其他进程锁定\n");
        close(fd);
        return;
    }

    printf("获取文件锁成功,PID:%d\n", getpid());

    // 执行文件操作
    sleep(5);

    // 释放锁
    lock.l_type = F_UNLCK;
    fcntl(fd, F_SETLK, &lock);
    printf("释放文件锁\n");

    close(fd);
}

4.2 目录操作与文件遍历

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

/**
 * 递归遍历目录
 */
void list_directory(const char *path, int depth) {
    DIR *dir = opendir(path);
    if (!dir) {
        perror("opendir");
        return;
    }

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        // 跳过当前目录和上级目录
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        // 构建完整路径
        char full_path[1024];
        snprintf(full_path, sizeof(full_path), "%s/%s", path, entry->d_name);

        // 获取文件信息
        struct stat st;
        if (stat(full_path, &st) == -1) {
            perror("stat");
            continue;
        }

        // 打印缩进
        for (int i = 0; i < depth; i++) {
            printf("  ");
        }

        // 判断文件类型
        if (S_ISDIR(st.st_mode)) {
            printf("[目录] %s\n", entry->d_name);
            // 递归遍历子目录
            list_directory(full_path, depth + 1);
        } else {
            printf("[文件] %s (%ld 字节)\n", entry->d_name, st.st_size);
        }
    }

    closedir(dir);
}

/**
 * 获取文件详细信息
 */
void file_info(const char *filename) {
    struct stat st;
    if (stat(filename, &st) == -1) {
        perror("stat");
        return;
    }

    printf("文件:%s\n", filename);
    printf("大小:%ld 字节\n", st.st_size);
    printf("权限:%o\n", st.st_mode & 0777);
    printf("所有者UID:%d\n", st.st_uid);
    printf("组GID:%d\n", st.st_gid);
    printf("最后访问时间:%ld\n", st.st_atime);
    printf("最后修改时间:%ld\n", st.st_mtime);
    printf("最后状态改变时间:%ld\n", st.st_ctime);

    // 判断文件类型
    printf("类型:");
    if (S_ISREG(st.st_mode)) printf("普通文件\n");
    else if (S_ISDIR(st.st_mode)) printf("目录\n");
    else if (S_ISLNK(st.st_mode)) printf("符号链接\n");
    else if (S_ISCHR(st.st_mode)) printf("字符设备\n");
    else if (S_ISBLK(st.st_mode)) printf("块设备\n");
    else if (S_ISFIFO(st.st_mode)) printf("管道\n");
    else if (S_ISSOCK(st.st_mode)) printf("套接字\n");
    else printf("未知\n");
}

/**
 * 复制文件
 */
int copy_file(const char *src, const char *dst) {
    FILE *src_fp = fopen(src, "rb");
    if (!src_fp) {
        perror("fopen src");
        return -1;
    }

    FILE *dst_fp = fopen(dst, "wb");
    if (!dst_fp) {
        perror("fopen dst");
        fclose(src_fp);
        return -1;
    }

    char buffer[8192];
    size_t bytes;
    while ((bytes = fread(buffer, 1, sizeof(buffer), src_fp)) > 0) {
        fwrite(buffer, 1, bytes, dst_fp);
    }

    fclose(src_fp);
    fclose(dst_fp);
    return 0;
}

void directory_demo() {
    printf("当前目录内容:\n");
    list_directory(".", 0);

    printf("\n文件信息:\n");
    file_info("test.txt");
}

五、预处理与编译链接

5.1 预处理高级技巧

/**
 * 高级预处理技巧
 */

// 1. 可变参数宏(C99)
#define DEBUG_PRINT(fmt, ...) \
    printf("[DEBUG] %s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)

// 2. 字符串化和连接
#define STRINGIFY(x) #x
#define CONCAT(a, b) a##b

// 3. 条件编译与多平台支持
#if defined(_WIN32) || defined(_WIN64)
    #define PLATFORM "Windows"
    #define PATH_SEPARATOR '\\'
#elif defined(__linux__)
    #define PLATFORM "Linux"
    #define PATH_SEPARATOR '/'
#elif defined(__APPLE__)
    #define PLATFORM "MacOS"
    #define PATH_SEPARATOR '/'
#else
    #define PLATFORM "Unknown"
#endif

// 4. 断言宏
#ifdef DEBUG
    #define ASSERT(expr) \
        do { \
            if (!(expr)) { \
                fprintf(stderr, "Assertion failed: %s at %s:%d\n", \
                        #expr, __FILE__, __LINE__); \
                abort(); \
            } \
        } while (0)
#else
    #define ASSERT(expr) ((void)0)
#endif

// 5. 防止头文件重复包含
#ifndef MY_HEADER_H
#define MY_HEADER_H
// 头文件内容
#endif

// 6. 编译时检查(静态断言)
#define STATIC_ASSERT(expr, msg) \
    typedef char static_assert_##msg[(expr) ? 1 : -1]

STATIC_ASSERT(sizeof(int) == 4, int_size_not_4);

void preprocessor_demo() {
    DEBUG_PRINT("程序启动");
    DEBUG_PRINT("变量值:%d", 100);

    printf("平台:%s\n", PLATFORM);

    int x = 5;
    ASSERT(x > 0);

    printf("STRINGIFY(hello) = %s\n", STRINGIFY(hello));
    int CONCAT(var, 123) = 42;
    printf("var123 = %d\n", var123);
}

5.2 多文件项目组织

/**
 * 多文件项目组织
 * 
 * 项目结构:
 * project/
 * ├── include/
 * │   ├── utils.h      // 公共函数声明
 * │   ├── list.h       // 数据结构声明
 * │   └── config.h     // 配置宏
 * ├── src/
 * │   ├── main.c       // 主程序
 * │   ├── utils.c      // 工具函数实现
 * │   └── list.c       // 数据结构实现
 * └── Makefile         // 编译脚本
 */

// utils.h
#ifndef UTILS_H
#define UTILS_H

// 外部函数声明
void print_message(const char *msg);
int max(int a, int b);
int min(int a, int b);

// 内联函数(定义在头文件)
static inline int square(int x) {
    return x * x;
}

#endif

// utils.c
#include "utils.h"
#include <stdio.h>

void print_message(const char *msg) {
    printf("%s\n", msg);
}

int max(int a, int b) {
    return a > b ? a : b;
}

int min(int a, int b) {
    return a < b ? a : b;
}

// main.c
#include <stdio.h>
#include "utils.h"
#include "list.h"

int main() {
    print_message("Hello, Multi-file Project!");
    printf("square(5) = %d\n", square(5));
    printf("max(10, 20) = %d\n", max(10, 20));

    // 使用链表
    LinkedList list;
    linkedlist_init(&list);
    linkedlist_push_back(&list, 100);
    linkedlist_print(&list);
    linkedlist_free(&list);

    return 0;
}

// Makefile
/*
CC = gcc
CFLAGS = -Wall -Wextra -O2 -Iinclude
SRCDIR = src
OBJDIR = obj
BINDIR = bin

SOURCES = $(wildcard $(SRCDIR)/*.c)
OBJECTS = $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
TARGET = $(BINDIR)/app

$(TARGET): $(OBJECTS)
    mkdir -p $(BINDIR)
    $(CC) $^ -o $@

$(OBJDIR)/%.o: $(SRCDIR)/%.c
    mkdir -p $(OBJDIR)
    $(CC) $(CFLAGS) -c $< -o $@

clean:
    rm -rf $(OBJDIR) $(BINDIR)

.PHONY: clean
*/

六、系统调用与底层编程

6.1 进程控制

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>

/**
 * 进程创建与控制
 */
void process_control() {
    pid_t pid = fork();

    if (pid < 0) {
        perror("fork");
        exit(1);
    } else if (pid == 0) {
        // 子进程
        printf("子进程:PID=%d, 父进程PID=%d\n", getpid(), getppid());
        sleep(2);
        printf("子进程结束\n");
        exit(0);
    } else {
        // 父进程
        printf("父进程:PID=%d, 子进程PID=%d\n", getpid(), pid);

        // 等待子进程结束
        int status;
        pid_t child_pid = wait(&status);

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

/**
 * 进程替换(exec族函数)
 */
void process_exec() {
    pid_t pid = fork();

    if (pid == 0) {
        // 子进程执行ls命令
        execlp("ls", "ls", "-l", "-a", NULL);
        // 如果exec执行失败
        perror("execlp");
        exit(1);
    } else {
        wait(NULL);
        printf("ls命令执行完成\n");
    }
}

/**
 * 信号处理
 */
void signal_handler(int signum) {
    printf("捕获到信号:%d\n", signum);
    if (signum == SIGINT) {
        printf("收到SIGINT,程序即将退出\n");
        exit(0);
    }
}

void signal_demo() {
    // 注册信号处理函数
    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);

    printf("进程PID:%d\n", getpid());
    printf("按Ctrl+C发送SIGINT信号\n");

    while (1) {
        sleep(1);
        printf("运行中...\n");
    }
}

6.2 管道与进程间通信

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

/**
 * 管道(Pipe)通信
 */
void pipe_demo() {
    int pipefd[2];
    char buffer[256];

    if (pipe(pipefd) == -1) {
        perror("pipe");
        exit(1);
    }

    pid_t pid = fork();

    if (pid == 0) {
        // 子进程:写入管道
        close(pipefd[0]);  // 关闭读端
        const char *msg = "Hello from child process!";
        write(pipefd[1], msg, strlen(msg) + 1);
        close(pipefd[1]);
        exit(0);
    } else {
        // 父进程:读取管道
        close(pipefd[1]);  // 关闭写端
        read(pipefd[0], buffer, sizeof(buffer));
        printf("父进程收到:%s\n", buffer);
        close(pipefd[0]);
        wait(NULL);
    }
}

/**
 * 共享内存通信
 */
void shared_memory_demo() {
    key_t key = ftok(".", 'a');
    if (key == -1) {
        perror("ftok");
        exit(1);
    }

    // 创建共享内存
    int shmid = shmget(key, 1024, IPC_CREAT | 0666);
    if (shmid == -1) {
        perror("shmget");
        exit(1);
    }

    pid_t pid = fork();

    if (pid == 0) {
        // 子进程:写入共享内存
        void *shared_mem = shmat(shmid, NULL, 0);
        if (shared_mem == (void*)-1) {
            perror("shmat");
            exit(1);
        }

        strcpy((char*)shared_mem, "Hello from shared memory!");
        printf("子进程写入共享内存\n");

        shmdt(shared_mem);
        exit(0);
    } else {
        sleep(1);

        // 父进程:读取共享内存
        void *shared_mem = shmat(shmid, NULL, 0);
        if (shared_mem == (void*)-1) {
            perror("shmat");
            exit(1);
        }

        printf("父进程读取:%s\n", (char*)shared_mem);

        shmdt(shared_mem);

        // 删除共享内存
        shmctl(shmid, IPC_RMID, NULL);
        wait(NULL);
    }
}

七、性能优化技巧

7.1 代码级优化

#include <stdio.h>
#include <time.h>

/**
 * 性能优化技巧
 */

// 1. 循环展开
void loop_unrolling() {
    int sum = 0;
    int arr[1000];

    // 普通循环
    for (int i = 0; i < 1000; i++) {
        sum += arr[i];
    }

    // 循环展开(每次处理4个元素)
    int i;
    for (i = 0; i < 1000 - 3; i += 4) {
        sum += arr[i] + arr[i+1] + arr[i+2] + arr[i+3];
    }
    for (; i < 1000; i++) {
        sum += arr[i];
    }
}

// 2. 使用寄存器变量
void register_variable() {
    // register关键字建议编译器将变量存储在寄存器中
    register int count = 0;
    for (register int i = 0; i < 1000000; i++) {
        count++;
    }
}

// 3. 位运算优化
int bitwise_optimization(int x, int y) {
    // 乘以2的幂
    int a = x << 3;      // x * 8
    int b = x >> 2;      // x / 4

    // 判断奇偶
    int is_even = (x & 1) == 0;

    // 取绝对值
    int abs_val = (x ^ (x >> 31)) - (x >> 31);

    return a + b;
}

// 4. 使用restrict关键字(C99)
void add_arrays(int *restrict a, int *restrict b, int *restrict c, int n) {
    // restrict告诉编译器数组不重叠,可以优化
    for (int i = 0; i < n; i++) {
        c[i] = a[i] + b[i];
    }
}

// 5. 分支预测优化
int branch_prediction(int x) {
    // likely和unlikely宏(GCC扩展)
    #define likely(x)   __builtin_expect(!!(x), 1)
    #define unlikely(x) __builtin_expect(!!(x), 0)

    if (likely(x > 0)) {
        return x * 2;
    } else {
        return -x;
    }
}

// 6. 缓存友好访问模式
void cache_friendly() {
    int matrix[1000][1000];

    // 缓存友好:按行访问
    for (int i = 0; i < 1000; i++) {
        for (int j = 0; j < 1000; j++) {
            matrix[i][j] = i + j;
        }
    }

    // 缓存不友好:按列访问
    for (int j = 0; j < 1000; j++) {
        for (int i = 0; i < 1000; i++) {
            matrix[i][j] = i + j;
        }
    }
}

八、知识体系图谱

C语言进阶知识
├── 指针深度剖析
│   ├── 多级指针与指针数组
│   ├── 函数指针与回调
│   ├── const与指针
│   └── 复杂声明解析
├── 内存管理
│   ├── 内存布局与对齐
│   ├── 内存池实现
│   └── 内存泄漏检测
├── 数据结构实现
│   ├── 链表(单/双/循环)
│   ├── 栈与队列
│   ├── 树与二叉树
│   └── 哈希表
├── 文件系统
│   ├── 随机访问与内存映射
│   ├── 目录遍历
│   └── 文件锁
├── 预处理与编译
│   ├── 高级宏技巧
│   ├── 多文件组织
│   └── Makefile
├── 系统编程
│   ├── 进程控制
│   ├── 信号处理
│   └── IPC通信
└── 性能优化
    ├── 代码级优化
    ├── 缓存优化
    └── 编译器优化

进阶开发者的标志是能够理解程序在内存中的布局,能够设计高效的数据结构,能够写出可移植的系统级代码。当你能够用C语言实现一个简单的内存分配器,能够理解编译器的优化策略,能够在多进程环境中协调通信时,你已经成为了一名真正的C语言专家。
开源:
https://app-ad5sxofh8phd.appmiaoda.com

相关文章
|
1天前
|
人工智能 JSON 机器人
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
本文带你零成本玩转OpenClaw:学生认证白嫖6个月阿里云服务器,手把手配置飞书机器人、接入免费/高性价比AI模型(NVIDIA/通义),并打造微信公众号“全自动分身”——实时抓热榜、AI选题拆解、一键发布草稿,5分钟完成热点→文章全流程!
10222 33
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
|
14天前
|
人工智能 安全 Linux
【OpenClaw保姆级图文教程】阿里云/本地部署集成模型Ollama/Qwen3.5/百炼 API 步骤流程及避坑指南
2026年,AI代理工具的部署逻辑已从“单一云端依赖”转向“云端+本地双轨模式”。OpenClaw(曾用名Clawdbot)作为开源AI代理框架,既支持对接阿里云百炼等云端免费API,也能通过Ollama部署本地大模型,完美解决两类核心需求:一是担心云端API泄露核心数据的隐私安全诉求;二是频繁调用导致token消耗过高的成本控制需求。
5903 14
|
21天前
|
人工智能 JavaScript Ubuntu
5分钟上手龙虾AI!OpenClaw部署(阿里云+本地)+ 免费多模型配置保姆级教程(MiniMax、Claude、阿里云百炼)
OpenClaw(昵称“龙虾AI”)作为2026年热门的开源个人AI助手,由PSPDFKit创始人Peter Steinberger开发,核心优势在于“真正执行任务”——不仅能聊天互动,还能自动处理邮件、管理日程、订机票、写代码等,且所有数据本地处理,隐私完全可控。它支持接入MiniMax、Claude、GPT等多类大模型,兼容微信、Telegram、飞书等主流聊天工具,搭配100+可扩展技能,成为兼顾实用性与隐私性的AI工具首选。
23113 119
|
7天前
|
人工智能 JavaScript API
解放双手!OpenClaw Agent Browser全攻略(阿里云+本地部署+免费API+网页自动化场景落地)
“让AI聊聊天、写代码不难,难的是让它自己打开网页、填表单、查数据”——2026年,无数OpenClaw用户被这个痛点困扰。参考文章直击核心:当AI只能“纸上谈兵”,无法实际操控浏览器,就永远成不了真正的“数字员工”。而Agent Browser技能的出现,彻底打破了这一壁垒——它给OpenClaw装上“上网的手和眼睛”,让AI能像真人一样打开网页、点击按钮、填写表单、提取数据,24小时不间断完成网页自动化任务。
1883 4

热门文章

最新文章