重叠记忆最佳实践

overlapping memmove best practice

我对操作字符串时要记忆的第三个参数有疑问。 strlen(string)strlen(string) + 1 似乎产生相同的结果。我认为 +1 部分会 包括一个终止 [=15=] 但一切似乎都很好 两个都。这里有最佳实践要考虑吗?我已经看到了这两个例子,但我不确定该怎么做?

考虑以下 c 程序:

#include    <stdio.h>
#include    <string.h>
#include    <stdlib.h>
#include    <errno.h>

int main()
{
   char string1[20]="Hello World";
   char string2[20]="Hello World";
   printf("\nstring1=\"%s\"\n",string1);

   memmove(string1,string1+6,strlen(string1) + 1);

   printf("\nstring1 after memmove \"%s\" using strlen(string1) + 1\n",string1);

   printf("\nstring2=\"%s\"\n",string2);

   memmove(string2,string2+6,strlen(string2));

   printf("\nstring2 after memmove \"%s\" using strlen(sting2)\n",string2);

   return 0;
}

输出:

string1="Hello World"

string1 after memmove "World" using strlen(string1) + 1

string2="Hello World"

string2 after memmove "World" using strlen(sting2)

你的长度逻辑完全不对,它之所以起作用,是因为缓冲区是 over-sized,而它的其余部分无论如何都包含空字节。而不是 strlen(string1) vs strlen(string1)+1,它应该是 strlen(string1+6) vs strlen(string1+6)+1。在那种情况下,您会发现差异确实很重要,正如您所期望的那样。

由于您是从索引 6 开始的,因此 strlen(...)strlen(...) + 1 都过大了,并且正在复制超出字符串末尾的额外 NUL 字节。它恰好可以工作,因为你制作了超大的 char[20] 数组,所以确实有额外的 NUL。额外的数组槽用零填充,就像你写的一样:

char string1[20] = "Hello World[=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=]";

你应该从两者中减去 6。如果这样做,您会发现 strlen(... + 6) + 1strlen(...) + 1 - 6 是您想要的。从其中任何一个中删除 + 1,它不会复制 NUL 终止符,从而导致不同的结果:

string1 == "World[=11=]World[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]"  // with + 1, NUL copied
string2 == "World World[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]"   // without + 1, NUL missing

字符串的定义包括终止 '[=10=]' 字符。

未明确复制该终止字符但仍将结果视为 null-terminated 字符串是未定义的行为。

除了不依赖未定义的行为外,这里没有"best practice"。

您正在移动额外的空字节。 right-hand边的长度是strlen(string1)-6。当您移动 strlen(string1) 时,您将在字符串右侧之后移动额外的 6 个 nul 字节,它们在那里是因为使用 char string1[20]="Hello World";,未初始化的字节被初始化为 [=13= ].