把一段遍历目录的 C 代码从 Linux 搬到 Windows,cl 一编译就报无法打开包括文件 dirent.h。这文件明明写过,就是在 Windows 上不存在。
这个头文件属于谁
dirent.h 是 POSIX 的东西,定义了 DIR 结构、struct dirent,以及 opendir、readdir、closedir 这一组函数。Linux 和 macOS 自带,Windows 的 CRT 里没有对应实现,MSVC 自然找不到。
报错还有一个变体:头文件找到了,但 struct dirent 里没有 d_type 成员。这种情况多半是用了某个第三方兼容层,成员少几个,不要硬读。
Windows 上的两条路
一条是换 API。Windows 原生做法是 FindFirstFile 加 FindNextFile,句柄类型 HANDLE,返回 WIN32_FIND_DATAA,取文件名用 cFileName,判断目录看 dwFileAttributes 里的 FILE_ATTRIBUTE_DIRECTORY 位。改动量集中在一个函数里,可控。
另一条是装兼容层。MSYS2、cygwin 环境里 dirent.h 是齐的,mingw-w64 也带了实现。用 vcpkg 或直接引一个单文件的 dirent 兼容实现,加进 include 路径就能编过。代价是行为跟 Linux 有细微差别,比如 inode 编号是假的,符号链接的 d_type 可能不准。
跨平台代码的写法
常见做法是写一层薄封装,头文件里用宏区分平台,对外只暴露一个迭代函数。
#ifdef _WIN32 #include <windows.h> #else #include <dirent.h> #endif调用方不关心底层,移植时只改这一处。路径分隔符也要一起处理。Linux 用斜杠,Windows 两种都认,但 Windows 的反斜杠在字符串里要额外转义,统一用正斜杠省事。文件名编码是另一个坑:Linux 上多是 UTF-8,Windows 的 ANSI 接口按本地代码页解,中文名会乱码,改用宽字符版本 FindFirstFileW 才稳。
别忽略 errno
readdir 返回 NULL 不一定是遍历结束,也可能是出错。遍历完要再取一次 errno,为 0 才算正常收尾。这一点在 Linux 上容易被忽略,写成跨平台代码后更要补上。