You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确返回字符数组指针?优化单字节文件读取代码

单字节IO优化:用缓冲区加速文件读取的正确实现

我有一段从文件读取内容的代码,它采用单字节系统调用I/O,速度相当慢:

int readc(io61_file* f) {
    unsigned char buf[1];
    ssize_t nr = read(f->fd, buf, 1);
    if (nr == 1) {
        return buf[0];
    } else if (nr == 0) {
        errno = 0; // 清除`errno`表示EOF
        return -1;
    } else {
        assert(nr == -1 && errno > 0);
        return -1;
    }
}

以下是该函数的使用方式:

// io_read(f, buf, sz)
//    从`f`中读取最多`sz`字节到`buf`。成功时返回读取的字节数。
//    如果在读取任何字节前遇到EOF,返回0;如果在读取任何字节前遇到错误,返回-1。
//
//    注意:如果在读取完所有`sz`字节前遇到EOF或错误,返回值可能为正但小于`sz`,
//    这称为“短读”。
ssize_t io_read(io61_file* f, unsigned char* buf, size_t sz) {
    size_t nread = 0;
    while (nread != sz) {
        int ch = readc(f);
        if (ch == EOF) {
            break;
        }
        buf[nread] = ch;
        ++nread;
    }
    if (nread != 0 || sz == 0 || errno == 0) {
        return nread;
    } else {
        return -1;
    }
}

我想要加速这段代码,第一步打算将readc函数内部的buf大小增加至10,但尝试了下面的写法后没达到预期效果,请问我哪里出错了?该如何正确实现?

我的尝试代码:

unsigned char* io_readc2(io61_file* f) {
    static unsigned char buf[1];
    ssize_t nr = read(f->fd, buf, 1);
    if (nr > 0 && nr < 2) {
        return buf;
    } else if (nr == 0) {
        errno = 0; // 清除`errno`表示EOF
        static unsigned char t[1];
        t[0] = -1;
        return t;
    } else {
        assert(nr == -1 && errno > 0);
        static unsigned char t[1];
        t[0] = -1;
        return t;
    }
}
ssize_t io_read(io61_file* f, unsigned char* buf, size_t sz) {
    size_t nread = 0;
    while (nread != sz) {
        unsigned char* chars = io_readc2(f);
        int ch = *chars;
        if (ch == EOF) {
            break;
        }
        buf[nread] = ch;
        ++nread;
    }
    if (nread != 0 || sz == 0 || errno == 0) {
        return nread;
    } else {
        return -1;
    }
}

你的代码为什么没效果?

  1. 缓冲区大小根本没改:你在io_readc2里还是用了static unsigned char buf[1],读取长度也还是1,本质还是单字节读取,完全没用到10字节的缓冲区,自然不会有加速效果。
  2. 返回静态数组的设计不合理:你用多个静态数组返回值,不仅线程不安全,而且每次调用还是只返回一个字节,没有利用缓冲区缓存多个字节来减少系统调用次数——这才是加速的核心。
  3. EOF判断逻辑有问题:你把t[0] = -1来表示EOF,但unsigned char的取值范围是0~255,-1会被转换成255,和EOF(通常是-1,int类型)比较时会出问题:*chars是unsigned char,转成int后是255,不等于EOF(-1),导致EOF判断失效。

正确的实现思路:给每个文件句柄关联缓冲区

要实现带缓冲区的单字节读取,核心是给每个io61_file结构体增加缓冲区相关字段(比如缓冲区数组、当前读取位置、缓冲区剩余字节数),这样每次调用readc时,先从缓冲区取字节,缓冲区空了再一次性读取一批(比如10字节)到缓冲区,减少系统调用次数。

修改后的代码实现

首先,修改io61_file结构体(假设原结构体无缓冲区字段):

#define BUF_SIZE 10

typedef struct io61_file {
    int fd;
    unsigned char buf[BUF_SIZE]; // 10字节缓冲区
    size_t buf_pos; // 当前读取到缓冲区的位置
    size_t buf_remaining; // 缓冲区中剩余的未读取字节数
} io61_file;

然后实现带缓冲区的readc函数:

int readc(io61_file* f) {
    // 如果缓冲区为空,先读取一批数据到缓冲区
    if (f->buf_remaining == 0) {
        ssize_t nr = read(f->fd, f->buf, BUF_SIZE);
        if (nr == 0) {
            errno = 0;
            return -1; // EOF
        } else if (nr == -1) {
            assert(errno > 0);
            return -1; // 错误
        }
        f->buf_pos = 0;
        f->buf_remaining = nr;
    }
    // 从缓冲区取一个字节
    unsigned char ch = f->buf[f->buf_pos];
    f->buf_pos++;
    f->buf_remaining--;
    return ch;
}

io_read函数无需修改,因为它调用的readc已经通过缓冲区减少了系统调用次数。

为什么这样能加速?

原来的代码每次readc都调用一次read系统调用,读取1字节;修改后,每调用10次readc才会触发一次read系统调用(读取10字节),系统调用次数减少为原来的1/10——而系统调用是IO操作中耗时的核心部分,因此能显著提升速度。

额外注意事项

  • 如果io61_file是动态创建的,初始化时要把buf_pos和buf_remaining设为0。
  • 该实现线程不安全,若要在多线程环境使用,需加锁保护缓冲区字段。
  • 可以把BUF_SIZE设得更大(比如4096,对应系统页大小),加速效果会更明显。

内容的提问来源于stack exchange,提问作者indigo153

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 10:05:23