跳到主要内容
🔍

代码示例:5.1.4 open/read/write/lseek 与 errno

全部示例均标注编译命令、适用环境与实测状态;完整验证记录见《03.5-代码编译运行验证记录》。

  • examples/ex1-open-read.c · mycp:open/read/write 最小文件拷贝

    /* ex1-open-read.c —— open/read/write 最小文件拷贝(mycp)
     * 适用环境:Linux(推荐);Windows 下用 MinGW-w64 的 POSIX 兼容层可编译运行
     * 标准:POSIX(C99 亦可) 编译:gcc ex1-open-read.c -o mycp -Wall -Wextra
     * 运行:./mycp 源文件 目标文件
     * 【平台差异】Windows CRT 的文件描述符默认是"文本模式",写文件会把 \n 转成 \r\n,
     *            必须加 O_BINARY 才是纯二进制拷贝;Linux 没有文本/二进制之分,O_BINARY 不存在。
     */
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <fcntl.h>
    #include <unistd.h>
     
    #ifdef _WIN32
    #define O_BIN O_BINARY     /* MinGW-w64 提供的二进制模式标志 */
    #else
    #define O_BIN 0            /* Linux 下无此标志 */
    #endif
     
    #define BUF_SIZE 1024
     
    int main(int argc, char *argv[])
    {
        if (argc != 3) {
            fprintf(stderr, "用法: %s <源文件> <目标文件>\n", argv[0]);
            return 1;
        }
     
        int fd_in = open(argv[1], O_RDONLY | O_BIN);
        if (fd_in < 0) {
            fprintf(stderr, "open %s 失败: %s (errno=%d)\n",
                    argv[1], strerror(errno), errno);
            return 1;
        }
     
        /* O_CREAT:不存在则创建;O_TRUNC:存在则截断;0644 受 umask 影响 */
        int fd_out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BIN, 0644);
        if (fd_out < 0) {
            fprintf(stderr, "open %s 失败: %s (errno=%d)\n",
                    argv[2], strerror(errno), errno);
            close(fd_in);
            return 1;
        }
     
        char buf[BUF_SIZE];
        ssize_t n;
        while ((n = read(fd_in, buf, sizeof(buf))) > 0) {
            ssize_t off = 0;
            while (off < n) {                          /* 处理"部分写" */
                ssize_t w = write(fd_out, buf + off, (size_t)(n - off));
                if (w < 0) {
                    fprintf(stderr, "write 失败: %s (errno=%d)\n",
                            strerror(errno), errno);
                    close(fd_in);
                    close(fd_out);
                    return 1;
                }
                off += w;
            }
        }
        if (n < 0) {                                   /* read 出错(不是 EOF) */
            fprintf(stderr, "read 失败: %s (errno=%d)\n", strerror(errno), errno);
            close(fd_in);
            close(fd_out);
            return 1;
        }
     
        if (close(fd_in) < 0 || close(fd_out) < 0) {
            fprintf(stderr, "close 失败: %s (errno=%d)\n", strerror(errno), errno);
            return 1;
        }
        printf("拷贝完成: %s -> %s\n", argv[1], argv[2]);
        return 0;
    }
     
  • examples/ex2-lseek-hole.c · lseek 制造空洞文件

    /* ex2-lseek-hole.c —— 用 lseek 制造空洞文件
     * 适用环境:Linux(推荐);Windows 下用 MinGW-w64 的 POSIX 兼容层可编译验证语法
     * 编译:gcc ex2-lseek-hole.c -o hole -Wall -Wextra
     * 运行:./hole hole.dat(随后可用 ls -l 与 du 对比观察"稀疏文件"现象——Linux 专属行为)
     */
    #include <stdio.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <unistd.h>
     
    int main(int argc, char *argv[])
    {
        if (argc != 2) {
            fprintf(stderr, "用法: %s <文件>\n", argv[0]);
            return 1;
        }
     
        int fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0644);
        if (fd < 0) { perror("open"); return 1; }
     
        if (write(fd, "Hello", 5) != 5) { perror("write"); close(fd); return 1; }
     
        /* lseek 修改文件偏移量:对随后的 write 同样生效(本章【纠错】err-7.1-1) */
        off_t pos = lseek(fd, 1024 * 1024, SEEK_SET);   /* 跳到 1MB 处 */
        if (pos < 0) { perror("lseek"); close(fd); return 1; }
        printf("lseek 后文件偏移 = %lld\n", (long long)pos);
     
        if (write(fd, "End", 3) != 3) { perror("write"); close(fd); return 1; }
     
        off_t end = lseek(fd, 0, SEEK_CUR);             /* 取当前偏移 */
        printf("当前偏移 = %lld(= 文件逻辑大小,中间 1MB-5B 为空洞)\n", (long long)end);
     
        if (close(fd) < 0) { perror("close"); return 1; }
        return 0;
    }
     
  • examples/ex3-errno-handling.c · errno/perror/strerror 错误处理演示

    /* ex3-errno-handling.c —— 出错路径与 errno/perror/strerror
     * 适用环境:Linux / Windows(MinGW)
     * 编译:gcc ex3-errno-handling.c -o errdemo -Wall -Wextra
     * 运行:./errdemo 一个不存在的文件
     */
    #include <stdio.h>
    #include <string.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <unistd.h>
     
    int main(int argc, char *argv[])
    {
        const char *path = (argc > 1) ? argv[1] : "/no/such/file.txt";
     
        int fd = open(path, O_RDONLY);
        if (fd < 0) {
            printf("open(\"%s\") 失败:\n", path);
            printf("  errno    = %d\n", errno);
            printf("  strerror = %s\n", strerror(errno));
            perror("  perror   ");
            printf("  规则: errno 只在出错时才有意义;任何成功调用都不会清零它;\n");
            printf("        errno.h 中的常量名必须写对,例如 EACCES(不是 EACCESS)。\n");
            return 1;
        }
        close(fd);
        return 0;
    }