write 函数简介
write 是一个标准的 Unix/Linux 系统调用,用于将数据从一个缓冲区写入到一个文件描述符中,文件描述符是一个整数,代表一个打开的 I/O 对象,比如文件、管道、套接字等。

(图片来源网络,侵删)
函数原型
#include <unistd.h> // 必须包含的头文件 ssize_t write(int fd, const void *buf, size_t count);
参数详解
-
int fd: 文件描述符。- 这是一个整数,代表你要写入的目标。
0: 标准输入1: 标准输出 - 这是你最常用来打印信息的地方,就像printf一样。2: 标准错误 - 通常用于输出错误信息。3及以上: 通过open()函数打开的文件、socket()创建的套接字等。
-
const void *buf: 指向要写入数据的缓冲区的指针。const关键字表示write函数不会修改这个缓冲区里的内容。- 它是一个
void*类型的指针,意味着它可以指向任何类型的数据(如char*,int*等),最常见的是指向一个字符串的指针。
-
size_t count: 要写入的字节数。- 指定了你要从
buf缓冲区中取出多少个字节进行写入。 - 对于字符串,通常使用
strlen()函数来计算其长度。
- 指定了你要从
返回值
- 成功时: 返回实际写入的字节数,这个值可能会小于你请求的
count,尤其是在写入到网络套接字时,可能需要多次调用write才能写完所有数据。 - 出错时: 返回
-1,并且会设置errno全局变量来指明具体的错误原因。
write 与 printf 的关系
初学者常常会混淆 write 和 printf。

(图片来源网络,侵删)
printf: 是 C 标准库函数(属于高级 I/O),它会先格式化你的字符串(处理%d,%s等占位符),然后将其输出到标准输出(通常是你的终端)。printf是缓冲的,意味着数据可能先暂存内存中,直到遇到换行符\n或缓冲区满了才会真正发送到终端。write: 是系统调用(属于底层 I/O),它只是将你内存中的一块数据原封不动地复制到指定的文件描述符中,它不关心数据内容,不做任何格式化。write通常是无缓冲的,调用即写入(除非文件描述符被设置为全缓冲)。
printf 是一个智能的、格式化的打印工具;write 是一个原始的、快速的内存复制工具。
"writecmd" 的实践示例
下面我们通过几个例子来展示如何使用 write 函数,这些例子可以看作是 "write command" 的具体实现。
示例 1:向标准输出写入字符串 (类似 printf 的基本功能)
这个例子演示了如何使用 write 来打印 "Hello, World!"。
#include <unistd.h>
#include <string.h>
int main() {
const char *message = "Hello, World!\n";
// 计算字符串长度,包括 '\0' 之前的所有字符
size_t len = strlen(message);
// fd=1 表示标准输出
// write 会返回写入的字节数,如果出错则返回-1
ssize_t bytes_written = write(1, message, len);
if (bytes_written == -1) {
// 如果写入失败,write 会设置 errno
// 这里简单处理,实际应用中应该检查 perror
write(2, "Error writing to stdout\n", 25);
return 1; // 返回非零表示错误
}
return 0; // 成功
}
编译和运行:

(图片来源网络,侵删)
gcc write_example.c -o write_example ./write_example
输出:
Hello, World!
示例 2:向标准错误输出写入错误信息
这是一个非常常见的用法,用于将错误信息与普通输出分离开。
#include <unistd.h>
#include <string.h>
int main() {
const char *error_message = "This is an error message.\n";
size_t len = strlen(error_message);
// fd=2 表示标准错误
write(2, error_message, len);
return 0;
}
编译和运行:
gcc error_example.c -o error_example ./error_example
输出 (会直接显示在你的终端上,即使你重定向标准输出):
This is an error message.
你可以试试重定向标准输出,错误信息依然会显示:
./error_example > output.log
终端上仍然会打印 "This is an error message.",而 output.log 文件是空的。
示例 3:向文件写入内容 (文件操作)
这是 write 的一个核心用途,创建或修改文件内容。
#include <unistd.h>
#include <fcntl.h> // 用于 open 函数
#include <string.h>
#include <stdio.h> // 用于 perror
int main() {
const char *filename = "my_log.txt";
const char *log_entry = "A new log entry was created.\n";
size_t len = strlen(log_entry);
// 1. 打开文件,O_WRONLY 表示只写,O_CREAT 表示如果文件不存在则创建
// 0644 是文件的权限 (所有者读写,组和其他用户只读)
int fd = open(filename, O_WRONLY | O_CREAT, 0644);
if (fd == -1) {
perror("Error opening file"); // perror 会打印 "Error opening file: " 并附上具体错误原因
return 1;
}
// 2. 使用 write 向文件描述符写入数据
ssize_t bytes_written = write(fd, log_entry, len);
if (bytes_written == -1) {
perror("Error writing to file");
close(fd); // 发生错误也要记得关闭文件
return 1;
}
// 3. 关闭文件描述符,释放资源
close(fd);
printf("Successfully wrote to %s\n", filename);
return 0;
}
编译和运行:
gcc file_example.c -o file_example ./file_example
结果:
- 终端输出:
Successfully wrote to my_log.txt - 当前目录下会创建一个
my_log.txt文件,内容为:A new log entry was created.
安全性考虑:write vs writev
当需要写入多个不连续的内存块时(一个字符串和几个变量),一个高效且安全的方法是使用 writev (write vector)。
write: 需要将所有数据先拼接到一个连续的缓冲区中,然后再写入,如果数据量很大,可能会浪费内存和时间。writev: 允许你提供一个 "iovec" 结构体数组,每个结构体指向一个独立的缓冲区和长度,系统调用会按顺序将所有缓冲区的内容写入目标,而不需要额外的内存拷贝。
writev 的优势:
- 高效: 避免了数据拷贝。
- 原子性 (在某些情况下): 对于网络套接字,
writev可以保证将所有数据作为一个整体发送出去,不会被其他线程或进程的数据打断。
| 特性 | write |
printf |
|---|---|---|
| 类型 | 系统调用 (底层) | 标准库函数 (高级) |
| 功能 | 原始数据复制 | 格式化输出 |
| 缓冲 | 通常无缓冲 | 行缓冲或全缓冲 |
| 参数 | fd, buf, count |
format, (可变参数) |
| 灵活性 | 可写入任何文件描述符 (文件、管道、socket) | 主要面向标准输入输出流 |
当你需要进行底层的文件操作、网络编程,或者需要极致的性能和直接控制 I/O 流时,write 是你的不二之选,对于大多数常规的控制台打印任务,printf 更为方便和易读。
