【发布时间】:2011-10-22 16:00:15
【问题描述】:
当我想将src_str 复制到dst_arr 时应该使用什么,为什么?
char dst_arr[10];
char *src_str = "hello";
PS:在阅读了很多关于 good 或 bad 是strncpy 和strlcpy 的内容后,我的头脑比我的计算机磁盘旋转得更快。
注意:我知道strlcpy 并非无处不在。这不是这里的问题。
【问题讨论】:
当我想将src_str 复制到dst_arr 时应该使用什么,为什么?
char dst_arr[10];
char *src_str = "hello";
PS:在阅读了很多关于 good 或 bad 是strncpy 和strlcpy 的内容后,我的头脑比我的计算机磁盘旋转得更快。
注意:我知道strlcpy 并非无处不在。这不是这里的问题。
【问题讨论】:
strncpy 是永远的正确答案。 strncpy 是一个旨在与非终止 fixed-width 字符串一起使用的函数。更准确地说,它的目的是将零终止的字符串转换为非终止的固定宽度字符串(通过复制)。换句话说,strncpy 在这里没有意义。
你真正的选择是在strlcpy和普通strcpy之间。
当您想要执行“安全”(即可能被截断)复制到 dst_arr 时,正确使用的函数是 strlcpy。
至于dst_ptr...没有“复制到dst_ptr”之类的东西。您可以复制到dst_ptr 指向的内存,但首先您必须确保它指向某个位置并分配该内存。有很多不同的方法可以做到这一点。
例如,您可以让dst_ptr 指向dst_arr,这种情况下的答案与前一种情况相同——strlcpy。
或者您可以使用malloc 分配内存。如果保证您分配的内存量足以容纳字符串(即至少分配了strlen(src_str) + 1 字节),那么您可以使用普通的strcpy 甚至memcpy 来复制字符串。在这种情况下没有必要也没有理由使用strlcpy,尽管有些人可能更喜欢使用它,因为它在某种程度上给了他们额外的安全感。
如果你有意分配更少的内存(即你希望你的字符串被截断),那么strlcpy 将成为正确使用的函数。
【讨论】:
strncpy 是一个 conversion 函数,它将以零结尾的源字符串转换为非终止的固定宽度目标字符串。
char dst_arr[10];,那么它不是以空值结尾的。那我应该用strlcpy对吗?
dst_arr 最初是什么并不重要。最初它只是一块未初始化的原始内存。问题是你想在dst_arr 中创建什么。我假设您想在dst_arr 中创建一个标准的普通零结尾字符串(因为您说您想要复制 一个普通的以零结尾的字符串)。如果是这样,请使用strlcpy。如果您想在dst_arr 中创建一个“固定宽度字符串”(这很不寻常,因为现在大多数时候没有人使用固定宽度字符串),请使用strncpy。
strncpy 不需要源字符串以空值结尾。如果在 n 个字符中没有找到 nul,它将停止查找。但如果它在前 n 个字符中找到它,它会将 nul 视为终止。不像strlcpy,它总是需要在s的末尾找到nul,这样才能返回strlen(s)。
strlcpy() 比 strncpy() 更安全,所以你不妨使用它。
没有它的系统通常会有一个 s_strncpy() 来做同样的事情。
注意:你不能将任何东西复制到dst_ptr,除非它指向某个东西
【讨论】:
我不知道 strlcpy。我刚刚发现here:
strlcpy() 和 strlcat() 函数复制和连接字符串 分别。它们旨在更安全、更一致且 strncpy(3) 和 strncat(3) 的更不容易出错的替换。
所以 strlcpy 接缝更安全。
编辑:可通过here进行完整讨论。
编辑2:
我意识到我上面写的内容并没有回答您问题的“在您的情况下”部分。如果您了解 strncpy 的局限性,我想您可以使用它并围绕它编写好的代码来避免它的陷阱;但如果您不确定自己对它的限制的理解,请使用 strlcpy。
我对 strncpy 和 strlcpy 的局限性的理解是,你可以用 strncpy 做一些非常糟糕的事情(缓冲区溢出),而用 strlcpy 做的最糟糕的事情就是在这个过程中丢失一个字符。
【讨论】:
strlcpy 中丢失一个字符?你能解释一下吗?
'\0'。如果您使用 strlcpy,'\0' 将在那里,但缓冲区中将少一个字符。因此,如果有人在应该使用 memcpy 时使用了 strncpy,那么使用 strlcpy 而不是 strncpy 会使他失去一个 char。
strlcpy 时,您唯一需要注意的是,您应该为目的地提供(src_length + 1 for null)。对吗?
src_length + 1,则副本中不会丢失任何字符。也许您希望 src 被截断,如果第三个参数小于src_length + 1,就会发生这种情况。但要小心,要将 str 截断为 5 个字符,第三个参数应该是 6,并且 dst 大小必须至少为 6 以避免缓冲区溢出。
strlcpy 的重点是避免输出缓冲区溢出。这意味着size 参数是destination 缓冲区的大小,而不是源缓冲区...准确地说,它是目标缓冲区中剩余的空间量。如果你“一直提供 src_length + 1”,你的代码将充满缓冲区溢出错误。
您应该始终使用标准函数,在本例中为 C11 strcpy_s() 函数。不是strncpy(),因为不保证零终止是不安全的。而不是仅限 OpenBSD 的 strlcpy(),因为它也不安全,而且 OpenBSD 总是提出自己的发明,通常不会使其成为任何标准。
看 http://en.cppreference.com/w/c/string/byte/strcpy
函数 strcpy_s 与 BSD 函数 strlcpy 类似,不同之处在于 strlcpy 截断源字符串以适应目标(这是一个安全风险)
如果您的 C 库没有 strcpy_s,请使用 safec 库。
https://rurban.github.io/safeclib/doc/safec-3.1/df/d8e/strcpy__s_8c.html
【讨论】:
首先,您的 dst_ptr 没有分配空间,并且您没有将其设置为指向其他空间,因此分配任何内容可能会导致分段错误。
Strncpy 应该可以正常工作 - 只需这样做:
strncpy(dst_arr, src_str, sizeof(dst_arr));
而且你知道你不会溢出 dst_arr。如果您使用更大的 src_str,您可能必须在 dst_arr 的末尾放置自己的空终止符,但在这种情况下,您的源是
这在任何地方都有效,而且很安全,所以我不会看其他任何东西,除非它具有求知欲。
还请注意,最好为 10 使用非幻数,这样您就知道它的大小与 strncpy 的大小匹配:)
【讨论】:
strncpy 几乎从来都不是正确的事情,因为它会零填充但不会零终止。 OP 想要strlcpy,它以零终止且不填零。此外,谈论“在这种情况下”是错误的,因为这个问题当然只是一个说明......真正的代码不会将像“hello”这样的固定字符串复制到已知更大的固定大小的缓冲区.
您不应为此使用 strncpy 而不是 strlcpy。更好用
*dst_arr=0; strncat(dst_arr,src_arr,(sizeof dst_arr)-1);
或者没有初始化
sprintf(dst_arr,"%.*s",(sizeof dst_arr)-1,src_arr);
这里的dst_arr 必须是数组而不是指针。
【讨论】:
strlcpy 可用于 OP 的需求。
sizeof (int) 与sizeof (size_t) 不同时,sprintf(dst_arr,"%.*s",(sizeof dst_arr)-1,src_arr); 是 UB。 printf(dst_arr,"%.*s",(int) (sizeof dst_arr - 1),src_arr); 是朝着正确方向迈出的一步。