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

为何fseek以SEEK_CUR偏移写入时出现非空格字符?技术咨询

关于fseek偏移行为的疑问与解答

先看你用来测试的代码:

int main (void) { 
    FILE *fp = fopen("txt2", "r+"); 
    rewind(fp); 
    fprintf(fp, "ab"); 
    fseek(fp,1L,SEEK_CUR); //尝试将偏移量改为1、2、3... 
    fprintf(fp, "cd"); 
    rewind(fp); 
    for(int c;(c=getc(fp))!=EOF;) putchar(c); 
    fclose(fp); 
    return 0; 
}

你做了不同偏移量的测试,得到了这些不符合预期的结果:

  • fseek(fp,0L,SEEK_CUR) → abcd(符合预期)
  • fseek(fp,1L,SEEK_CUR) → abccd(出现'c'而非预期空格)
  • fseek(fp,2L,SEEK_CUR) → abcdcd(额外出现'cd')
  • fseek(fp,3L,SEEK_CUR) → abcdccd(出现重复的'cdc')
  • fseek(fp,100000000000L,SEEK_CUR) → abcdcdcdcdcdcdcdcdcd(重复的'cd'序列)
  • fseek(fp,1000000000000000000000000L,SEEK_CUR) → 提示“integer constant is too large for its type”警告

咱们逐个拆解这些问题:

1. fseek的这种偏移行为原因是什么?

首先要明确三个核心点:

  • r+模式的特性:用r+打开文件是读写模式,但它不会清空原有内容,也不会自动填充文件间隙。写入数据时只会覆盖对应位置的内容,未被覆盖的原有内容会保留。
  • 跨文件长度的偏移是未定义行为:C标准没有规定当fseek将指针移动到当前文件长度之外时,间隙区域的内容是什么。大多数系统中,这些区域要么是磁盘上的残留垃圾数据,要么被视为“空洞”(读取时返回空字符,但不会实际写入磁盘)。
  • 超大偏移量的整数溢出:你用的100000000000L这类超大值,已经超出了很多系统中long类型的取值范围(比如32位long的最大值是2147483647),这会触发整数溢出,实际传入fseek的是溢出后的循环值,导致指针并没有移动到你预期的超大位置,而是回到了文件内的某个位置。

2. 为何会出现重复字符模式?

拿fseek(fp,1L,SEEK_CUR)的例子来说:
写完ab后,文件指针停在偏移量2的位置,如果原文件txt2之前运行过程序,可能已经有abcd的内容——r+模式不会清空它,所以ab只是覆盖了前两个字符,后面的cd还在。当你fseek到偏移量3(2+1),写入cd时,就会覆盖原偏移量3的d为c,偏移量4的位置写入d,最终文件内容变成abccd,这就是你看到的结果。

而超大偏移量的情况,因为整数溢出,fseek的实际偏移量变成了一个较小的重复值,导致你写入的cd不断覆盖文件内的已有内容,或者多次写入到同一区域附近,自然就出现了重复的cd序列。

3. 如何通过fseek实现偏移时填充空格?

C标准库的fseek本身没有自动填充空格的功能,你需要手动处理文件间隙,步骤如下:

  1. 获取当前文件的实际长度;
  2. 计算目标偏移量与当前长度的差值,填充对应数量的空格;
  3. 再写入目标内容。

修改后的示例代码如下:

#include <stdio.h>
#include <stdlib.h> // 用于perror

int main (void) { 
    FILE *fp = fopen("txt2", "r+"); 
    if (!fp) { // 必须添加错误判断,避免空指针操作
        perror("fopen failed");
        return 1;
    }
    rewind(fp); 
    fprintf(fp, "ab"); 

    // 先获取写入ab后的文件长度
    fseek(fp, 0L, SEEK_END);
    long current_len = ftell(fp);
    // 回到写入ab后的指针位置(偏移量2)
    fseek(fp, 2L, SEEK_SET);

    long offset = 1L; // 你想要的偏移量,可修改为2、3等
    long target_pos = ftell(fp) + offset;

    // 如果目标位置超过当前文件长度,填充空格
    if (target_pos > current_len) {
        long spaces_needed = target_pos - current_len;
        for (long i = 0; i < spaces_needed; i++) {
            fputc(' ', fp);
        }
    }

    // 移动到目标位置,写入cd
    fseek(fp, target_pos, SEEK_SET);
    fprintf(fp, "cd"); 

    // 读取并输出文件内容
    rewind(fp); 
    for(int c;(c=getc(fp))!=EOF;) putchar(c); 
    fclose(fp); 
    return 0; 
}

这样当你设置offset=1L时,输出就会是ab cd(符合预期的空格填充效果)。

另外,关于超大偏移量的警告,是因为你写的整数常量超出了long类型的取值范围,你可以使用int64_t(需包含<stdint.h>)这类更大的整数类型,但要注意:移动到超大位置填充空格会生成极大的文件,一般没有实际意义。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 19:27:43