我可以向您指出手册页、网站等,但最终重要的是 C 标准本身。作为标准运行时库的一部分,其用法和行为在 C99-§7.23.2.4 中定义为:
#include <string.h>
char *strncpy(char * restrict s1,
const char * restrict s2,
size_t n);
说明
该strncpy
函数从 s2 指向的数组复制不超过 n 个字符(不复制空字符后的字符)到 s1 指向的数组。如果复制发生在重叠的对象之间,则行为未定义。如果 s2 指向的数组是一个短于 n 个字符的字符串,则将空字符附加到 s1 指向的数组的副本中,直到 n 个字符全部被写入。
返回函数返回 s1
的strncpy
值。
这里有重要的隐含信息,最重要的是:如果源字符串长度(不包括其空字符终止符)达到或超过指定的目标缓冲区长度,则不会以空字符终止您的目标strncpy()
字符串。
strncpy()
此外,尽管在标准中明确规定(见上文),但它继续让我感到困惑,有多少工程师不知道当源字符串n
长度小于目标缓冲区大小。由此得出以下不可避免的结论:
API将strncpy()
始终将n
字符写入目标缓冲区引用的地址。
在您的情况下,因为目标缓冲区只有 10 个字符宽,所以您在可写内存的定义结束后写入了 90 个额外的字符,从而进入了未定义行为的领域。
在这一点上,您必须问自己“那有什么用?” 有一个可以说是基本的用例。它允许您将最多n
字符复制到目标缓冲区,并且可以预测您不会超出过去的n
字符。时期。但最终,您需要一个以 null 结尾的字符串,因此正确的用法是:
char dst[ N ];
strncpy(dst, src, N-1);
dst[N-1] = 0;
其中N
是 chars 中缓冲区的硬长度dst
并且大于或等于1
. 请注意,dst
也可以是动态分配的内存指针:
char *dst = malloc( N * sizeof(char) );
strncpy(dst, src, N-1);
dst[N-1] = 0;
有了上述内容,您将始终在dst
. 如果源字符串长度小于指定的目标缓冲区长度,strncpy()
将用空字符尾部填充缓冲区的其余部分,直到源字符复制 + 尾部填充空字符的总数等于n
,最后的语句是多余的。如果源字符串长度等于或大于目标缓冲区长度,strncpy()
将停止复制一次N-1
到达字符,最后一条语句在缓冲区的末尾设置一个空字符。这会导致原始源的“缩减”前缀字符串,但最重要的是,它确保您不会使用稍后扫描终止符的字符串 API 调用超出目标缓冲区的边界。
上述技术的用处总是值得商榷的。我是一个 C++ 人,所以让std::string
我的快乐自我免于所有这些疯狂。但现实是这样的:有时你会关心是否src
没有将其全部复制到dst
; 有时你不知道。有用性非常依赖于情境。对于在 UI 中呈现字符串数据,这(可能)无关紧要。对于复制用于关键数据的字符串,部分前缀子字符串是不可接受的。当警察向“Joseph Johnson Jr.”发出逮捕令时,当他的父亲(“Joseph Johnson”)被拖入监狱时,将会有一些解释要做,因为逮捕令发布软件的名称缓冲区只有 15 个字符.
综上所述,您的分段错误归结为以下语句:
strncpy(s1.from_str,src, 100); // length parameter is wrong.
回想一下上面的粗体语句:“strncpy()
将始终将n
字符写入目标缓冲区引用的地址。” . 这意味着上面的代码将始终将 100 个字符写入目标缓冲区,在您的情况下,目标缓冲区只有 10 个字符宽,因此是未定义的行为和可能的 ker-boom。
如果目标缓冲区是固定长度的字符数组,请执行以下操作来纠正此问题:
strncpy(s1.from_str,src, sizeof(s1.from_str)/sizeof(s1.from_str[0])-1);
s1.from_str[ sizeof(s1.from_str)/sizeof(s1.from_str[0])-1 ] = 0;
请参阅先前的用法,了解如何对长度为 `N 字符的动态字符串执行此操作。