使用memmove操作FILE*删除C文件指定行无效的问题咨询
核心问题答案
memmove完全不能直接访问、修改FILE*关联的磁盘文件内容,更不可能直接删除文件里的信息。memmove是C标准库提供的纯内存操作函数,作用仅为在当前进程的用户态内存空间中,移动指定长度的连续字节数据,它完全不感知磁盘、文件、文件系统这类IO概念。你传给它的所有地址参数,必须是当前进程可访问的合法内存地址,操作的结果也只会修改内存里的数据,不会自动同步到磁盘文件。
另外FILE*本身是C标准库维护的文件流结构体指针,内部仅存储文件描述符、IO缓冲区指针、读写状态标记这类元数据,你通过fgets、fread等函数读到的所有文件内容,都是存放在内存缓冲区中的临时副本,修改这些副本不会对磁盘上的原文件产生任何影响。
你的代码不生效的具体原因
你现在的代码从根逻辑到具体实现都存在问题,自然达不到预期效果:
- 根逻辑错误:全程所有操作都是在修改栈上定义的几个临时数组(
m_sRemovalRow、sReplaceLineStart、sTemp等)里的内存数据,最后调用的memmove也只是把sReplaceLineStart开头的一段内存拷贝到m_sRemovalRow对应的栈空间,没有任何一步把修改后的数据写回文件,文件本身当然不会有任何变化。 - 语法与基础逻辑bug:
- 函数定义漏写返回值类型首字符,写的是
nt RemoveRow(int iRowNum),正确应为int RemoveRow(int iRowNum)。 - 分支括号匹配错误:
if(i == iRowNum)的代码块没有正确闭合,后续判断m_sRemovalRow == NULL的逻辑被错误嵌套在该分支内。 - 空判断逻辑完全失效:
m_sRemovalRow是提前定义的固定长度栈数组,数组名是恒定的栈内存地址,永远不可能等于NULL。你要判断的是fgets的返回值——只有fgets读到文件尾或读失败时才会返回NULL,当前写法永远进不到你写的「删除最后一行」的分支。 memset参数错误:你写的memset(m_sRemovalRow,0,sizeof(m_MaxSizeRow))中,m_MaxSizeRow是表示行最大长度的整数值,sizeof(整数)得到的是int类型的长度(通常为4字节),根本无法把整个数组清零。- 待移动长度计算逻辑混乱:在
i==iRowNum+1和i>iRowNum的分支中,你都是先累加sTemp的长度,再调用fgets往sTemp里读新内容,累加的长度始终是上一次读到的内容长度(初始状态下sTemp全0,第一次累加的长度为0),算出来的RemovalLength完全不是你需要的后续内容总长度。
- 函数定义漏写返回值类型首字符,写的是
关于「跳过临时文件、靠memmove提效」的误区
你想跳过创建临时副本的步骤提升效率的思路是走不通的:
- 删除文件中间某一行的本质,是把待删除行后面的所有内容整体向前移动待删除行的长度,再把文件尾部多余的部分截断。不管你是把内容读到临时文件最后替换原文件,还是在原文件上原地把后面的内容往前写再截断,需要的磁盘IO量是完全一样的,不存在靠
memmove就能减少IO、提升效率的可能。 - 原地修改文件的风险远高于临时文件方案:如果写入过程中出现程序崩溃、磁盘故障、权限异常,原文件会直接损坏丢失数据;而写临时文件最后通过原子重命名替换原文件的方案,不会出现半写坏的原文件,稳定性高得多,这也是业界通用实现都推荐临时副本方案的核心原因。
如果硬要实现原地修改,正确流程为:
- 逐行遍历文件,记录待删除行的起始文件偏移、该行的总长度
- 从待删除行的末尾偏移开始,分块读取后续内容到内存缓冲区,再写回到待删除行的起始偏移位置,直到读到文件尾
- 调用文件截断函数(POSIX下为
ftruncate,Windows下为SetEndOfFile)把文件长度设置为「原文件长度 - 待删除行长度」
整个流程中memmove/memcpy仅能用来操作读入内存的缓冲区数据,根本无法直接操作文件内容。
内容的提问来源于stack exchange,提问作者sasukenebe
相关产品推荐
相关产品推荐

