ZetCode

C setvbuf 函数

最后修改日期:2025 年 4 月 6 日

I/O 缓冲是 C 编程中至关重要的性能优化技术。setvbuf 函数允许对流缓冲行为进行精细控制。本教程将详细介绍 setvbuf,涵盖缓冲类型、使用模式和实际示例。掌握缓冲技术可以显著提高程序的 I/O 效率和响应能力。

什么是 setvbuf?

setvbuf 函数用于控制文件流的缓冲。它接受流指针、缓冲区、模式和大小作为参数。缓冲模式包括全缓冲、行缓冲和无缓冲。该函数必须在打开流之后、执行任何 I/O 操作之前调用。成功时返回零,失败时返回非零值。

基本语法

setvbuf 的函数原型为:

int setvbuf(FILE *stream, char *buffer, int mode, size_t size);

mode 参数可以是 _IOFBF(全缓冲)、_IOLBF(行缓冲)或 _IONBF(无缓冲)。buffer 可以为 NULL,这样函数就会自己分配缓冲区。size 指定缓冲区的字节大小。

全缓冲示例

此示例演示了使用自定义缓冲区进行全缓冲。

full_buffering.c
#include <stdio.h>

int main() {
    FILE *fp = fopen("output.txt", "w");
    char buffer[1024];  // 1KB buffer
    
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }
    
    // Set full buffering with our 1KB buffer
    if (setvbuf(fp, buffer, _IOFBF, sizeof(buffer)) != 0) {
        perror("Failed to set buffer");
        fclose(fp);
        return 1;
    }
    
    // Write data - will be buffered
    for (int i = 0; i < 100; i++) {
        fprintf(fp, "Line %d\n", i);
    }
    
    fclose(fp);
    return 0;
}

在此,我们创建一个 1KB 的缓冲区并设置全缓冲模式 (_IOFBF)。输出会被收集到缓冲区中,直到缓冲区填满或流关闭。这可以减少磁盘 I/O 操作,从而提高大写入量的性能。

行缓冲示例

行缓冲会在遇到换行符时刷新缓冲区。

line_buffering.c
#include <stdio.h>

int main() {
    FILE *fp = fopen("lines.txt", "w");
    
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }
    
    // Set line buffering with automatic buffer
    if (setvbuf(fp, NULL, _IOLBF, BUFSIZ) != 0) {
        perror("Failed to set buffer");
        fclose(fp);
        return 1;
    }
    
    // Each line will be written immediately
    fprintf(fp, "First line\n");
    fprintf(fp, "Second line\n");
    fprintf(fp, "Third line\n");
    
    fclose(fp);
    return 0;
}

使用行缓冲 (_IOLBF) 时,每行在遇到换行符时都会写入文件。这对于希望立即显示输出的交互式程序很有用。我们通过传递 NULL 来让 setvbuf 分配自己的缓冲区。

无缓冲示例

禁用缓冲会导致立即写入输出设备。

no_buffering.c
#include <stdio.h>

int main() {
    FILE *fp = fopen("immediate.txt", "w");
    
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }
    
    // Disable buffering
    if (setvbuf(fp, NULL, _IONBF, 0) != 0) {
        perror("Failed to set buffer");
        fclose(fp);
        return 1;
    }
    
    // Each character is written immediately
    for (char c = 'A'; c <= 'Z'; c++) {
        fputc(c, fp);
    }
    
    fclose(fp);
    return 0;
}

无缓冲模式 (_IONBF) 会导致每个 I/O 操作立即执行。这对于关键消息或需要实时输出的情况很有用。在此模式下,缓冲区和大小参数将被忽略。

更改 stdout 缓冲

您可以修改标准输出的缓冲行为。

stdout_buffering.c
#include <stdio.h>
#include <unistd.h>

int main() {
    // Change stdout to line buffered
    setvbuf(stdout, NULL, _IOLBF, 0);
    
    printf("This will appear immediately");
    sleep(2);
    printf(" when the line is complete.\n");
    
    // Now change to unbuffered
    setvbuf(stdout, NULL, _IONBF, 0);
    printf("This appears immediately");
    sleep(2);
    printf(" even without newline.\n");
    
    return 0;
}

此示例展示了如何更改 stdout 的缓冲行为。最初设置为行缓冲,第一条消息在换行符之后才会出现。切换到无缓冲模式后,输出会立即显示。这对于实时状态更新很有用。

自动缓冲区分配

通过传递 NULL,让 setvbuf 处理缓冲区分配。

auto_buffer.c
#include <stdio.h>

int main() {
    FILE *fp = fopen("auto.txt", "w");
    
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }
    
    // Let setvbuf allocate its own buffer
    if (setvbuf(fp, NULL, _IOFBF, 4096) != 0) {
        perror("Failed to set buffer");
        fclose(fp);
        return 1;
    }
    
    // Write data using the automatically allocated buffer
    for (int i = 0; i < 1000; i++) {
        fprintf(fp, "Record %d\n", i);
    }
    
    fclose(fp);
    return 0;
}

通过将 NULL 作为缓冲区指针传递,我们让 setvbuf 分配自己的 4KB 缓冲区。这通常比管理自己的缓冲区内存更简单。缓冲区会在流关闭时自动释放。

错误处理

妥善的错误处理可确保您的程序行为可预测。

error_handling.c
#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *fp = fopen("test.txt", "w");
    char *buffer = malloc(2048);
    
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }
    
    if (buffer == NULL) {
        perror("Failed to allocate buffer");
        fclose(fp);
        return 1;
    }
    
    // Try to set buffer
    if (setvbuf(fp, buffer, _IOFBF, 2048) != 0) {
        perror("Failed to set buffer");
        free(buffer);
        fclose(fp);
        return 1;
    }
    
    // Use the buffered stream
    fprintf(fp, "Testing buffered output\n");
    
    // Clean up
    free(buffer);
    fclose(fp);
    return 0;
}

此示例演示了全面的错误处理。我们检查文件打开错误、缓冲区分配失败以及 setvbuf 错误。在所有错误路径中都会正确释放资源。健壮的错误处理可防止内存泄漏和未定义行为。

缓冲与无缓冲的时序比较

比较缓冲和无缓冲 I/O 的性能。

timing.c
#include <stdio.h>
#include <time.h>

#define ITERATIONS 100000

void test_buffered() {
    FILE *fp = fopen("buffered.txt", "w");
    setvbuf(fp, NULL, _IOFBF, BUFSIZ);
    
    clock_t start = clock();
    for (int i = 0; i < ITERATIONS; i++) {
        fprintf(fp, "Line %d\n", i);
    }
    clock_t end = clock();
    fclose(fp);
    
    printf("Buffered: %.2f seconds\n", 
           (double)(end - start) / CLOCKS_PER_SEC);
}

void test_unbuffered() {
    FILE *fp = fopen("unbuffered.txt", "w");
    setvbuf(fp, NULL, _IONBF, 0);
    
    clock_t start = clock();
    for (int i = 0; i < ITERATIONS; i++) {
        fprintf(fp, "Line %d\n", i);
    }
    clock_t end = clock();
    fclose(fp);
    
    printf("Unbuffered: %.2f seconds\n", 
           (double)(end - start) / CLOCKS_PER_SEC);
}

int main() {
    test_buffered();
    test_unbuffered();
    return 0;
}

此基准测试比较了缓冲和无缓冲写入的性能。缓冲 I/O 通常速度更快,因为它最大限度地减少了系统调用。对于大量数据,这种差异会更加明显。对于性能关键的 I/O 操作,请始终考虑缓冲。

使用 setvbuf 的最佳实践

来源

C setvbuf 文档

本教程探讨了用于控制 C 中 I/O 缓冲的强大 setvbuf 函数。适当的缓冲可以在各种场景下显著提高程序的 I/O 性能和响应能力。

作者

我的名字是 Jan Bodnar,我是一名敬业的程序员,对编码充满热情。自 2007 年以来,我通过 1,400 多篇文章和 8 本电子书分享了我的专业知识。凭借十多年的教学经验,我致力于使编程变得易于理解和引人入胜。

列表 C 标准库