重叠记忆最佳实践
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) + 1
或 strlen(...) + 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= ]
.
我对操作字符串时要记忆的第三个参数有疑问。 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) + 1
或 strlen(...) + 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= ]
.