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
指定缓冲区的字节大小。
全缓冲示例
此示例演示了使用自定义缓冲区进行全缓冲。
#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 操作,从而提高大写入量的性能。
行缓冲示例
行缓冲会在遇到换行符时刷新缓冲区。
#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
分配自己的缓冲区。
无缓冲示例
禁用缓冲会导致立即写入输出设备。
#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 缓冲
您可以修改标准输出的缓冲行为。
#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
处理缓冲区分配。
#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 缓冲区。这通常比管理自己的缓冲区内存更简单。缓冲区会在流关闭时自动释放。
错误处理
妥善的错误处理可确保您的程序行为可预测。
#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 的性能。
#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 的最佳实践
- 尽早调用: 在打开文件后、执行任何 I/O 操作之前,立即设置缓冲。
- 选择合适的模式: 对批量数据使用全缓冲,对交互式输出使用行缓冲。
- 大小很重要: 较大的缓冲区可提高性能,但会消耗更多内存。
- 自动缓冲区: 除非您需要特殊控制,否则让 setvbuf 分配缓冲区。
- 错误检查: 始终检查 setvbuf 的返回值以确保成功。
来源
本教程探讨了用于控制 C 中 I/O 缓冲的强大 setvbuf
函数。适当的缓冲可以在各种场景下显著提高程序的 I/O 性能和响应能力。
作者
列表 C 标准库。