memmove替换字符串首字符时表现差异及参数、分支逻辑疑问
咱们先从memmove的核心特性说起——它和memcpy最大的区别就是专门处理内存重叠的场景,会根据源地址和目标地址的位置关系自动调整复制方向,这也是你遇到各种表现差异的根本原因。
疑问1:为何memmove在替换字符串首字符时表现不同?
当你替换字符串的首字符(也就是目标区域是字符串的起始位置),大概率遇到的是目标地址 ≤ 源地址的重叠场景。举个你场景里的例子:假设你要把string1中后面的"day"移到开头,这时候源地址是string1 + 5(假设"day"从第5位开始),目标地址是string1,源地址比目标地址大。
这种情况下,memmove会选择从后往前复制:先复制"day"的最后一个字符'y'到目标的第2位,再复制'a'到第1位,最后复制'd'到第0位。这样就不会在复制前面的字节时,覆盖还没来得及复制的源数据。
而如果你替换的是字符串中间或末尾的位置,比如pos=3,源地址可能在目标地址的前面,这时候memmove就会改成从前往后复制,效率更高且不会出错。这就是替换首字符时表现不同的核心——复制方向变了,目的是保证重叠内存的数据安全。
疑问2:pos==0时memmove的参数、字节数与分支差异
先拆解你提到的参数strlen(string1) - strlen(string2+1):假设string2是"day",那string2+1指向的是"ay",strlen(string2+1)就是2。这个参数其实是计算当前场景下需要移动的有效字节数:
strlen(string1) - strlen(string2+1) = strlen(string1) - (strlen(string2)-1),也就是总字符串长度减去string2长度减1。结合你输出dayhousedayhouse的功能来看,这个值刚好对应你需要保留并移动的那部分字符串的字节数——比如原来的"housedayhouse"中,除了string2最后一个字符之外的内容,需要移动到新的位置来腾出空间放string2,同时保证最终输出的拼接效果符合预期。
至于memmove该选多少字节,核心原则是:你要复制的所有有效数据的字节数,包括字符串的终止符'\0'(如果需要保留完整字符串的话)。少了会导致数据截断,多了会越界访问内存,引发未定义行为。
再说说if和else分支的差异:这其实是对应两种不同的内存重叠场景:
- 当
pos==0时,目标地址在源地址的前面(比如要把后面的内容往开头挪,或者把开头的内容往后移腾空间),memmove需要从后往前复制,所以需要计算对应的字节数; - 当
pos!=0时,源地址可能在目标地址的前面(比如替换中间位置,不需要担心前面的数据被覆盖),这时候memmove从前往后复制,字节数的计算逻辑自然和pos=0时不同。
简单来说,两个分支是为了适配不同重叠方向下的复制需求,保证每一次memmove都能安全且正确地完成数据移动。
内容的提问来源于stack exchange,提问作者Sedem

