前置知识: C

跨平台编程

00:00
3 min Intermediate 2026/6/14

C语言跨平台开发

概述

C语言的一大优势是可移植性,但不同操作系统硬件平台之间存在差异:文件路径分隔符、线程API、网络接口、字节序等。跨平台编程的目标是编写一套代码,在平台上无需修改即可编译。这需要合理使用条件编译平台抽象层和可移植的函数。

基础概念

平台差异的主要来源

  1. 操作系统API不同(Windows vs Linux vs macOS)
  2. 编译器扩展不同(GCC vs Clang vs MSVC)
  3. 字节序不同(大端 vs 小端)
  4. 数据类型大小不同(32位 vs 64位)
  5. 路径分隔符不同(/ vs \

常用预定义宏

说明
_WIN32Windows(32位和64位都定义
_WIN64Windows 64位
__linux__Linux
__APPLE__macOS/iOS
__unix__Unix-like系统
__GNUC__GCC编译器
__clang__Clang编译器
_MSC_VERMSVC编译器版本
__x86_64__x86-64架构
__aarch64__ARM64架构

快速上手

平台检测与条件编译

#include <stdio.h>

int main(void) {
    // 检测操作系统
#if defined(_WIN32)
    printf("操作系统: Windows\n");
#elif defined(__linux__)
    printf("操作系统: Linux\n");
#elif defined(__APPLE__)
    printf("操作系统: macOS\n");
#elif defined(__unix__)
    printf("操作系统: Unix\n");
#else
    printf("操作系统: 未知\n");
#endif

    // 检测编译器
#if defined(__GNUC__)
    printf("编译器: GCC %d.%d\n", __GNUC__, __GNUC_MINOR__);
#elif defined(__clang__)
    printf("编译器: Clang %d.%d\n", __clang_major__, __clang_minor__);
#elif defined(_MSC_VER)
    printf("编译器: MSVC %d\n", _MSC_VER);
#endif

    // 检测架构
#if defined(__x86_64__) || defined(_M_X64)
    printf("架构: x86-64\n");
#elif defined(__aarch64__)
    printf("架构: ARM64\n");
#elif defined(__i386__) || defined(_M_IX86)
    printf("架构: x86\n");
#endif

    return 0;
}

跨平台路径处理

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

// 路径分隔符
#ifdef _WIN32
    #define PATH_SEPARATOR '\\'
    #define PATH_SEPARATOR_STR "\\"
#else
    #define PATH_SEPARATOR '/'
    #define PATH_SEPARATOR_STR "/"
#endif

// 跨平台路径拼接
void join_path(char *buf, size_t buf_size, const char *dir, const char *file) {
    size_t dir_len = strlen(dir);

    // 去除末尾的分隔符
    while (dir_len > 0 && (dir[dir_len - 1] == '/' || dir[dir_len - 1] == '\\')) {
        dir_len--;
    }

    snprintf(buf, buf_size, "%.*s%c%s", (int)dir_len, dir, PATH_SEPARATOR, file);
}

int main(void) {
    char path[256];
    join_path(path, sizeof(path), "/home/user/", "document.txt");
    printf("路径: %s\n", path);

    join_path(path, sizeof(path), "C:\\Users\\test", "file.txt");
    printf("路径: %s\n", path);

    return 0;
}

详细用法

跨平台休眠函数

#include <stdio.h>

#ifdef _WIN32
    #include <windows.h>
#else
    #include <unistd.h>
#endif

// 跨平台毫秒级休眠
void sleep_ms(unsigned int milliseconds) {
#ifdef _WIN32
    Sleep(milliseconds);
#else
    usleep(milliseconds * 1000);
#endif
}

int main(void) {
    printf("休眠1秒...\n");
    sleep_ms(1000);
    printf("休眠结束\n");
    return 0;
}

跨平台目录创建

#include <stdio.h>
#include <sys/stat.h>

#ifdef _WIN32
    #include <direct.h>
    #define mkdir(path, mode) _mkdir(path)
#endif

// 跨平台创建目录
int create_directory(const char *path) {
#ifdef _WIN32
    if (_mkdir(path) == 0) {
        return 0;
    }
    // 目录已存在不算错误
    return errno == EEXIST ? 0 : -1;
#else
    if (mkdir(path, 0755) == 0) {
        return 0;
    }
    return errno == EEXIST ? 0 : -1;
#endif
}

int main(void) {
    if (create_directory("test_dir") == 0) {
        printf("目录创建成功\n");
    }
    return 0;
}

跨平台字节序检测

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

// 检测字节序
int is_little_endian(void) {
    uint16_t test = 0x0001;
    return *(uint8_t *)&test == 1;
}

// 字节序转换
uint16_t swap16(uint16_t val) {
    return (val << 8) | (val >> 8);
}

uint32_t swap32(uint32_t val) {
    return ((val & 0xFF000000) >> 24) |
           ((val & 0x00FF0000) >> 8)  |
           ((val & 0x0000FF00) << 8)  |
           ((val & 0x000000FF) << 24);
}

// 主机序转网络序(小端系统需要转换)
uint32_t host_to_network32(uint32_t val) {
    if (is_little_endian()) {
        return swap32(val);
    }
    return val;
}

int main(void) {
    printf("字节序: %s\n", is_little_endian() ? "小端" : "大端");

    uint32_t test = 0x12345678;
    printf("原始: 0x%08X\n", test);
    printf("转换: 0x%08X\n", host_to_network32(test));
    return 0;
}

跨平台动态库加载

#include <stdio.h>

#ifdef _WIN32
    #include <windows.h>
    typedef HMODULE lib_handle_t;
#else
    #include <dlfcn.h>
    typedef void *lib_handle_t;
#endif

// 加载动态库
lib_handle_t lib_load(const char *path) {
#ifdef _WIN32
    return LoadLibraryA(path);
#else
    return dlopen(path, RTLD_LAZY);
#endif
}

// 获取函数指针
void *lib_get_func(lib_handle_t handle, const char *name) {
#ifdef _WIN32
    return (void *)GetProcAddress(handle, name);
#else
    return dlsym(handle, name);
#endif
}

// 关闭动态库
void lib_close(lib_handle_t handle) {
#ifdef _WIN32
    FreeLibrary(handle);
#else
    dlclose(handle);
#endif
}

int main(void) {
#ifdef _WIN32
    lib_handle_t lib = lib_load("user32.dll");
#else
    lib_handle_t lib = lib_load("libm.so.6");
#endif

    if (lib) {
        printf("动态库加载成功\n");
        lib_close(lib);
    } else {
        printf("动态库加载失败\n");
    }

    return 0;
}

常见场景

场景一:跨平台日志系统

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

#ifdef _WIN32
    #include <windows.h>
#else
    #include <sys/time.h>
#endif

// 获取毫秒级时间戳
long long get_timestamp_ms(void) {
#ifdef _WIN32
    FILETIME ft;
    GetSystemTimeAsFileTime(&ft);
    long long t = ((long long)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
    return (t - 116444736000000000LL) / 10000;
#else
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (long long)tv.tv_sec * 1000 + tv.tv_usec / 1000;
#endif
}

// 跨平台日志函数
void log_msg(const char *level, const char *fmt, ...) {
    long long ts = get_timestamp_ms();
    printf("[%s] [%lld] ", level, ts);

    va_list ap;
    va_start(ap, fmt);
    vprintf(fmt, ap);
    va_end(ap);

    printf("\n");
}

int main(void) {
    log_msg("INFO", "程序启动");
    log_msg("DEBUG", "变量 x = %d", 42);
    log_msg("ERROR", "文件 %s 不存在", "config.txt");
    return 0;
}

场景二:跨平台互斥锁

#include <stdio.h>

#ifdef _WIN32
    #include <windows.h>
    typedef CRITICAL_SECTION mutex_t;

    void mutex_init(mutex_t *m)    { InitializeCriticalSection(m); }
    void mutex_lock(mutex_t *m)    { EnterCriticalSection(m); }
    void mutex_unlock(mutex_t *m)  { LeaveCriticalSection(m); }
    void mutex_destroy(mutex_t *m) { DeleteCriticalSection(m); }
#else
    #include <pthread.h>
    typedef pthread_mutex_t mutex_t;

    void mutex_init(mutex_t *m)    { pthread_mutex_init(m, NULL); }
    void mutex_lock(mutex_t *m)    { pthread_mutex_lock(m); }
    void mutex_unlock(mutex_t *m)  { pthread_mutex_unlock(m); }
    void mutex_destroy(mutex_t *m) { pthread_mutex_destroy(m); }
#endif

int shared_counter = 0;
mutex_t counter_mutex;

void increment_counter(void) {
    mutex_lock(&counter_mutex);
    shared_counter++;
    mutex_unlock(&counter_mutex);
}

int main(void) {
    mutex_init(&counter_mutex);

    increment_counter();
    increment_counter();
    printf("计数器: %d\n", shared_counter);

    mutex_destroy(&counter_mutex);
    return 0;
}

注意事项

避免在头文件中使用平台特定代码

头文件提供统一的接口平台特定实现放在 .c 文件中:

// platform.h - 统一接口
void sleep_ms(unsigned int ms);
int create_directory(const char *path);

// platform_linux.c - Linux实现
// platform_windows.c - Windows实现

使用标准库优先

C标准函数本身就是跨平台的,优先使用标准函数:

// 优先使用标准函数
FILE *fp = fopen("file.txt", "r");  // 跨平台
size_t len = strlen(str);            // 跨平台

// 避免直接使用平台API
// HANDLE h = CreateFile(...);  // 仅Windows
// int fd = open(...);           // 仅POSIX

编译器差异

不同编译器对C标准的支持不同,使用 _Static_assert 检查编译器特性

// 检查int大小
_Static_assert(sizeof(int) == 4, "int必须是4字节");

// 检查指针大小
#if UINTPTR_MAX == 0xFFFFFFFF
    // 32位平台
#elif UINTPTR_MAX == 0xFFFFFFFFFFFFFFFF
    // 64位平台
#endif

进阶用法

构建平台抽象层

// platform.h - 平台抽象层头文件
#ifndef PLATFORM_H
#define PLATFORM_H

#include <stdint.h>

// 固定宽度类型
typedef uint8_t  u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;

// 平台信息
#if defined(_WIN32)
    #define PLATFORM_NAME "Windows"
#elif defined(__linux__)
    #define PLATFORM_NAME "Linux"
#elif defined(__APPLE__)
    #define PLATFORM_NAME "macOS"
#endif

// 导出函数声明
#ifdef _WIN32
    #define EXPORT __declspec(dllexport)
#else
    #define EXPORT __attribute__((visibility("default")))
#endif

// 对齐属性
#ifdef _WIN32
    #define ALIGNED(x) __declspec(align(x))
#else
    #define ALIGNED(x) __attribute__((aligned(x)))
#endif

// 线程局部存储
#ifdef _WIN32
    #define THREAD_LOCAL __declspec(thread)
#else
    #define THREAD_LOCAL __thread
#endif

// 平台API
void    plat_sleep_ms(u32 ms);
u64     plat_timestamp_ms(void);
int     plat_create_dir(const char *path);

#endif // PLATFORM_H

使用CMake管理跨平台构建

cmake_minimum_required(VERSION 3.20)
project(CrossPlatformApp C)

set(CMAKE_C_STANDARD 17)

# 根据平台选择源文件
set(PLATFORM_SOURCES)
if(WIN32)
    list(APPEND PLATFORM_SOURCES src/platform_windows.c)
else()
    list(APPEND PLATFORM_SOURCES src/platform_linux.c)
endif()

add_executable(myapp
    src/main.c
    ${PLATFORM_SOURCES}
)

# 平台特定的链接库
if(WIN32)
    target_link_libraries(myapp PRIVATE ws2_32)
else()
    target_link_libraries(myapp PRIVATE pthread dl)
endif()

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式