为何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本身没有自动填充空格的功能,你需要手动处理文件间隙,步骤如下:
- 获取当前文件的实际长度;
- 计算目标偏移量与当前长度的差值,填充对应数量的空格;
- 再写入目标内容。
修改后的示例代码如下:
#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
相关产品推荐
相关产品推荐

