临时文件: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,只有当前用户能读写。这很合理。

知识结构图

临时文件创建与安全 tmpfile tmpnam(已废弃) mkstemp(推荐) 自动创建 + 自动删除 C标准,可移植性好 部分系统不支持 仅生成文件名 竞态条件漏洞 C11已废弃 创建 + 打开一步完成 需手动 unlink POSIX标准 核心建议 优先使用 mkstemp(POSIX)或 tmpfile(C标准) 永远不要用 tmpnam,它不安全

总结

临时文件虽小,但用不好会出大问题。我个人习惯是:能用 mkstemp 就用 mkstemp,它既安全又灵活。如果代码需要跨平台,我会考虑 tmpfile,但会提前确认目标系统是否支持。至于 tmpnam……嗯,就当它不存在吧。

记住一句话:临时文件不是“随便”文件。该有的检查、清理、权限控制,一个都不能少。


专注资料整理