tmpfile() 创建的文件退出即消失,因其返回前已 unlink(),仅靠 file* 句柄维持内核引用;进程终止或 fclose() 后自动释放。

tmpfile() 创建的文件为什么一退出就消失?
tmpfile() 在 C 标准库中直接调用系统接口(如 Linux 的 open("/dev/null", ...) 或更常见的是 mkstemp() 配合 unlink())创建一个无路径名的文件,句柄返回后立即被 unlink() —— 这意味着它没有目录项指向,但只要进程还持有 FILE*,内核仍保留其数据。进程终止、或显式 fclose() 后,系统自动回收空间。
常见错误现象:fopen("temp.dat", "w") 生成的文件会残留磁盘;而 tmpfile() 却怎么也找不到对应文件名,甚至 lsof -p $PID 都只看到 (deleted) —— 这不是 bug,是设计使然。
- 它不写入任何目录,
ls /tmp永远看不到它 - 不能用
fseek()随机读写?可以,但得确保文件流是可定位的(tmpfile()返回的是二进制读写流,默认支持fseek) - Windows 下行为一致,但底层用的是类似
GetTempFileName()+DeleteFile()组合
tmpfile() 和 tmpfile_s() 在 C++ 里怎么选?
C++ 程序若混用 C 风格 I/O,优先用 tmpfile();若项目启用了安全增强(如 MSVC 的 SDL 检查),编译器可能报错:"tmpfile() has been deprecated" —— 此时需改用 C11 的 tmpfile_s(),但它不是 C++ 标准函数,且 MSVC 实现要求传入缓冲区大小参数,glibc 则未实现。
使用场景:tmpfile_s() 并非“更安全”,只是把错误码从全局 errno 改为输出参数,避免多线程竞争;但 C++ 项目中更自然的做法是绕过 C FILE*,改用 RAII 方式管理临时文件。
-
tmpfile():POSIX/C99 兼容,所有主流平台可用,返回FILE* -
tmpfile_s(size_t *restrict psize):仅 Windows/MSVC 有完整实现,Linux 上需手动定义或跳过 - 现代 C++ 更推荐
std::filesystem::temp_directory_path()+std::filesystem::create_temporary_file()(C++17 起),它返回真实路径,便于调试
tmpfile() 返回 NULL 怎么排查?
典型错误信息:tmpfile() returned NULL,常见原因不是磁盘满,而是进程级资源限制或权限问题。
- 检查
ulimit -n:如果进程打开文件数已达上限,tmpfile()会失败(即使你刚 fclose 了几个) - 确认
/tmp所在文件系统是否只读(mount | grep "$(df . | tail -1 | awk '{print $1}')”) - 某些容器环境(如 rootless Podman)默认禁用
memfd_create或tmpfs写入,也会静默失败 - 务必检查返回值:
if (!fp) { perror("tmpfile"); return -1; },不要假设它一定成功
用 tmpfile() 做缓存时要注意什么?
有人用 tmpfile() 当做大对象缓冲区(比如下载中间体、图像处理暂存),这可行,但容易忽略两个关键点:内存映射开销和生命周期绑定。
性能影响:Linux 下 tmpfile() 多数走 tmpfs(即内存文件系统),读写快,但会占用 RAM 或 swap;如果写入 500MB,实际就是占了 500MB 可用内存 —— 不是“不占资源”的幻觉。
- 别在循环里反复
tmpfile()+fclose():每次都会触发一次unlink+ 新分配,开销比复用一个流高 - 如果需要多次读写同一块临时数据,
rewind(fp)比重新 fopen 更轻量 - 跨线程传递
FILE*是危险的:C 标准不保证FILE*线程安全,除非用flockfile(),但会拖慢速度;更适合用std::vector<uint8_t></uint8_t>或mmap替代
最常被忽略的一点:tmpfile() 创建的是文本模式流(即使你没指定 b),在 Windows 上换行符会被自动转换;若写二进制数据,必须用 fwrite() 并确认 ferror(),否则可能静默截断。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











