临时文件:tmpfile、tmpnam、mkstemp
临时文件,说白了就是程序运行过程中产生的“一次性”文件。用完就扔,不占地方。我刚开始写C程序时,总觉得这玩意儿可有可无。直到有一次,我写了一个数据处理工具,中间结果全往内存里塞,结果数据量一大,程序直接崩了……嗯,从那以后,我再也不敢小看临时文件了。
为什么需要临时文件?
你想想看,有些场景下,程序需要暂存中间数据。比如排序算法处理海量数据时,内存装不下,就得借助磁盘。再比如,多个进程之间需要交换数据,临时文件就是个好中介。说白了,临时文件就是程序的“草稿纸”。
但草稿纸也有讲究——你不能随便乱扔,也不能让别人偷看。这就引出了临时文件的两个核心问题:唯一性和安全性。
标准库的三把刀:tmpfile、tmpnam、mkstemp
C标准库给了我们三个工具。我一个个说,你对比着看。
1. tmpfile:最省心,但有限制
tmpfile 会创建一个临时文件,并以 "wb+" 模式打开。文件是二进制的,可读可写。最关键的是——文件在关闭或程序退出时会自动删除。你根本不用操心清理工作。
#include <stdio.h>
int main() {
FILE *fp = tmpfile();
if (fp == NULL) {
perror("tmpfile failed");
return 1;
}
// 写入一些数据
fprintf(fp, "Hello, temp file!\n");
// 重置文件指针,读取数据
rewind(fp);
char buf[64];
fgets(buf, sizeof(buf), fp);
printf("Read: %s", buf);
// 关闭时自动删除
fclose(fp);
return 0;
}
我的经验:tmpfile 在嵌入式或某些受限系统上可能不可用。我在一个老旧的项目中遇到过,当时不得不换用 mkstemp。所以,如果你写跨平台代码,最好先检查一下。
2. tmpnam:别用了,真的
tmpnam 的作用是生成一个唯一的文件名。但它有个致命缺陷——存在竞态条件。什么意思呢?它只负责生成名字,不负责创建文件。在你拿到名字和真正创建文件之间,别的进程可能已经用了这个名字。
#include <stdio.h>
int main() {
char name[L_tmpnam];
tmpnam(name);
printf("Temp file name: %s\n", name);
// 危险!此时另一个进程可能已经创建了同名文件
FILE *fp = fopen(name, "w+");
// ...
return 0;
}
警告:我曾经在安全审计中见过用 tmpnam 的代码。结果呢?攻击者可以预测文件名,提前创建符号链接,把数据写到别的地方去。说白了,这就是个安全漏洞。C11 标准已经不建议使用 tmpnam 了。你最好也别碰。
3. mkstemp:推荐,但要注意细节
mkstemp 是 POSIX 标准提供的函数。它既生成唯一文件名,又创建并打开文件。一步到位,没有竞态条件。用法也很简单:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main() {
char template[] = "/tmp/myapp_XXXXXX";
int fd = mkstemp(template);
if (fd == -1) {
perror("mkstemp failed");
return 1;
}
// 使用文件描述符
FILE *fp = fdopen(fd, "w+");
fprintf(fp, "Hello from mkstemp!\n");
fclose(fp); // 同时关闭 fd
// 注意:mkstemp 不会自动删除文件
// 你需要手动 unlink
unlink(template);
return 0;
}
关键点:模板字符串中的 "XXXXXX" 会被替换成随机字符。这个字符串必须是可修改的,所以不能传字符串常量。另外,mkstemp 不会自动删除文件,你得自己调用 unlink。
安全对比:一张表说清楚
| 函数 | 唯一性 | 安全性 | 自动删除 | 可移植性 |
|---|---|---|---|---|
| tmpfile | 高 | 高 | 是 | C标准,但部分系统不支持 |
| tmpnam | 中 | 低(竞态条件) | 否 | C标准,但已废弃 |
| mkstemp | 高 | 高 | 否(需手动) | POSIX,Windows 需第三方 |
避坑指南
- 我曾经在 Linux 下用 tmpfile,结果发现 /tmp 目录满了,tmpfile 返回 NULL。所以,记得检查返回值。
- 我曾经用 tmpnam 生成文件名,然后自己 fopen。结果另一个进程抢先创建了同名文件,数据全乱了。从那以后,我只用 mkstemp。
- 注意:mkstemp 返回的是文件描述符,不是 FILE*。如果你习惯用标准 IO 函数,记得用 fdopen 转换一下。
- 安全提示:临时文件最好设置合适的权限。mkstemp 默认是 0600,只有当前用户能读写。这很合理。
知识结构图
总结
临时文件虽小,但用不好会出大问题。我个人习惯是:能用 mkstemp 就用 mkstemp,它既安全又灵活。如果代码需要跨平台,我会考虑 tmpfile,但会提前确认目标系统是否支持。至于 tmpnam……嗯,就当它不存在吧。
记住一句话:临时文件不是“随便”文件。该有的检查、清理、权限控制,一个都不能少。