更好地理解 strncpy() 函数行为
On better understanding the strncpy() function behavior
在 strncpy 的 Linux 联机帮助页中,我读到:
If the length of src is less than n, strncpy() writes additional
null bytes to dest to ensure that a total of n bytes are written.
在这种极限情况下(两个字符串末尾都没有 \0),其中 n>4:
char dest[8]="qqqqqqqq";
char src[4] = "abcd";
strncpy(dest, src, 5);
按字符打印目标字符得到“abcdqqqq”。
由于 src 中没有 \0,因此没有 \0 从 src 复制到 dest,但如果我对手册页的理解正确,则无论如何都应该复制其他 4 个字符,它们应该是 \0s。
此外,如果 src 是“abc”(因此它以 NUL 结尾),dest 包含“abc[=21=][=21=]qqq”。
我添加了我用来测试的整个代码(是的,它也会到第 8 个字符来查看它):
#include <stdio.h>
#include <string.h>
int main()
{
char dest[8]="qqqqqqqq";
char src[4] = "abcd"; // "abc"
strncpy(dest, src, 5);
for (int i=0; i<9; i++)
printf("%2x ", dest[i]);
putchar('\n');
return 0;
}
这是一个错误的实现还是我错过了什么?
If the length of src
这就是问题所在,您的 src
不是以空字符结尾的字符串,因此 strncpy
不知道它有多长。联机帮助页还有一个示例代码,显示了 strncpy
的工作原理:它在一个空字节处停止,如果找不到空字节,它会继续读取直到到达 n
。在您的情况下,它读取 src
的 4 个字符并从 dest
中获取第五个字符,因为它在内存中是下一个。 (你可以尝试打印 src[4]
,没有什么能阻止你,你将从 dest
得到 q
。C 不是很好吗?)
您使用 strncpy()
时使用的源参数似乎不是字符串,而是字符数组。那么你为什么期望它表现得明智呢? Linux 上的手册清楚地表明它采用字符串作为源参数,并且它复制它“包括终止空字节”(假定这样一个字节)。请注意,没有“non-terminated 字符串”这样的东西。字符数组只是字符数组,字符串函数不能保证对它起作用。
但是,您观察到的特定行为是预期的并记录在案。
The rationale for strncpy()
in the POSIX standard包含以下句子:
If there is no NUL
character byte in the first n
bytes of the array pointed to by s2
, the result is not null-terminated.
... 其中 s2
就是您所说的 src
.
至少 Ubuntu 和 OpenBSD 上 strncpy()
的手册包含相似的措辞。
让我们从逻辑上看一下,引用 Debian 10 中包含的日期为 2017 年 9 月 15 日的 GNU 手册页:
If the length of src is less than n, strncpy() writes additional null bytes to dest to ensure that a total of n bytes are written.
如您正确陈述(有点),n == 5
。那么“src的长度”是多少?众所周知,C 字符串是null-terminated。手册页对此有提示:
The strcpy() function copies the string pointed to by src, including the terminating null byte ('[=21=]') … The strncpy() function is similar, except that at most n bytes of src are copied.
不过您的字符串不是 null-terminated。因此,在读取了您定义的 4 个字节后,strncpy
尝试读取第五个字节,它找到了什么?显然是 dest
的第一个字节(据我所知,实际上这是 implementation-dependent)。它仍然没有找到终止字符串的空值,所以它继续读取吗?不,因为如上所述:
a total of n bytes are written
和
at most n bytes of src are copied.
因此它将 5 个字节 "abcdq"
复制到 dest
。
你说:
other 4 characters should be copied in any case, and they should be [=24=]s
这意味着总长度为 8 个字节。你从哪里得到这个?这是 dest
、 而不是 src
的“长度”(或至少声明的数组长度),并且在任何情况下都会被 [= =17=].
src
是null-terminated的情况很简单。
所以不,执行没有错误。
其实你很幸运:
The strings may not overlap
他们在这里做,所以,从技术上讲,任何事情都有可能发生。事实上,对这个问题的评论说 Valgrind 对此发出警告。
and the destination string dest must be large enough to receive the copy. Beware of buffer overruns!
这种对字符串长度的粗心应该是一个警告,在您创建缓冲区溢出之前要格外小心。
另一方面,虽然可能像strncpy
这样的函数会有错误,但极不可能.标准实施已在各种环境中进行了全面测试。任何明显的错误更有可能是您自己的代码中的错误,或者对您自己的代码缺乏理解,就像这里的情况一样。
在 strncpy 的 Linux 联机帮助页中,我读到:
If the length of src is less than n, strncpy() writes additional
null bytes to dest to ensure that a total of n bytes are written.
在这种极限情况下(两个字符串末尾都没有 \0),其中 n>4:
char dest[8]="qqqqqqqq";
char src[4] = "abcd";
strncpy(dest, src, 5);
按字符打印目标字符得到“abcdqqqq”。 由于 src 中没有 \0,因此没有 \0 从 src 复制到 dest,但如果我对手册页的理解正确,则无论如何都应该复制其他 4 个字符,它们应该是 \0s。 此外,如果 src 是“abc”(因此它以 NUL 结尾),dest 包含“abc[=21=][=21=]qqq”。 我添加了我用来测试的整个代码(是的,它也会到第 8 个字符来查看它):
#include <stdio.h>
#include <string.h>
int main()
{
char dest[8]="qqqqqqqq";
char src[4] = "abcd"; // "abc"
strncpy(dest, src, 5);
for (int i=0; i<9; i++)
printf("%2x ", dest[i]);
putchar('\n');
return 0;
}
这是一个错误的实现还是我错过了什么?
If the length of src
这就是问题所在,您的 src
不是以空字符结尾的字符串,因此 strncpy
不知道它有多长。联机帮助页还有一个示例代码,显示了 strncpy
的工作原理:它在一个空字节处停止,如果找不到空字节,它会继续读取直到到达 n
。在您的情况下,它读取 src
的 4 个字符并从 dest
中获取第五个字符,因为它在内存中是下一个。 (你可以尝试打印 src[4]
,没有什么能阻止你,你将从 dest
得到 q
。C 不是很好吗?)
您使用 strncpy()
时使用的源参数似乎不是字符串,而是字符数组。那么你为什么期望它表现得明智呢? Linux 上的手册清楚地表明它采用字符串作为源参数,并且它复制它“包括终止空字节”(假定这样一个字节)。请注意,没有“non-terminated 字符串”这样的东西。字符数组只是字符数组,字符串函数不能保证对它起作用。
但是,您观察到的特定行为是预期的并记录在案。
The rationale for strncpy()
in the POSIX standard包含以下句子:
If there is no
NUL
character byte in the firstn
bytes of the array pointed to bys2
, the result is not null-terminated.
... 其中 s2
就是您所说的 src
.
至少 Ubuntu 和 OpenBSD 上 strncpy()
的手册包含相似的措辞。
让我们从逻辑上看一下,引用 Debian 10 中包含的日期为 2017 年 9 月 15 日的 GNU 手册页:
If the length of src is less than n, strncpy() writes additional null bytes to dest to ensure that a total of n bytes are written.
如您正确陈述(有点),n == 5
。那么“src的长度”是多少?众所周知,C 字符串是null-terminated。手册页对此有提示:
The strcpy() function copies the string pointed to by src, including the terminating null byte ('[=21=]') … The strncpy() function is similar, except that at most n bytes of src are copied.
不过您的字符串不是 null-terminated。因此,在读取了您定义的 4 个字节后,strncpy
尝试读取第五个字节,它找到了什么?显然是 dest
的第一个字节(据我所知,实际上这是 implementation-dependent)。它仍然没有找到终止字符串的空值,所以它继续读取吗?不,因为如上所述:
a total of n bytes are written
和
at most n bytes of src are copied.
因此它将 5 个字节 "abcdq"
复制到 dest
。
你说:
other 4 characters should be copied in any case, and they should be [=24=]s
这意味着总长度为 8 个字节。你从哪里得到这个?这是 dest
、 而不是 src
的“长度”(或至少声明的数组长度),并且在任何情况下都会被 [= =17=].
src
是null-terminated的情况很简单。
所以不,执行没有错误。
其实你很幸运:
The strings may not overlap
他们在这里做,所以,从技术上讲,任何事情都有可能发生。事实上,对这个问题的评论说 Valgrind 对此发出警告。
and the destination string dest must be large enough to receive the copy. Beware of buffer overruns!
这种对字符串长度的粗心应该是一个警告,在您创建缓冲区溢出之前要格外小心。
另一方面,虽然可能像strncpy
这样的函数会有错误,但极不可能.标准实施已在各种环境中进行了全面测试。任何明显的错误更有可能是您自己的代码中的错误,或者对您自己的代码缺乏理解,就像这里的情况一样。