【问题标题】:strncpy or strlcpy in my casestrncpy 或 strlcpy 在我的情况下
【发布时间】:2011-10-22 16:00:15
【问题描述】:

当我想将src_str 复制到dst_arr 时应该使用什么,为什么?

char dst_arr[10];
char *src_str = "hello";

PS:在阅读了很多关于 goodbadstrncpystrlcpy 的内容后,我的头脑比我的计算机磁盘旋转得更快。

注意:我知道strlcpy 并非无处不在。这不是这里的问题。

【问题讨论】:

    标签: c copy c-strings strncpy


    【解决方案1】:
    当您的目标字符串以零结尾时,

    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 永远不是正确答案”时,您是指源字符串还是目标字符串?
    • @hari:目标字符串。 strncpy 是一个 conversion 函数,它将以零结尾的源字符串转换为非终止的固定宽度目标字符串。
    • 所以,如果我的目标字符串是char dst_arr[10];,那么它不是以空值结尾的。那我应该用strlcpy对吗?
    • @hari:呃……我不明白这个问题。你的dst_arr 最初是什么并不重要。最初它只是一块未初始化的原始内存。问题是你想在dst_arr 中创建什么。我假设您想在dst_arr 中创建一个标准的普通零结尾字符串(因为您说您想要复制 一个普通的以零结尾的字符串)。如果是这样,请使用strlcpy。如果您想在dst_arr 中创建一个“固定宽度字符串”(这很不寻常,因为现在大多数时候没有人使用固定宽度字符串),请使用strncpy
    • 我要补充一点,strncpy 不需要源字符串以空值结尾。如果在 n 个字符中没有找到 nul,它将停止查找。但如果它在前 n 个字符中找到它,它会将 nul 视为终止。不像strlcpy,它总是需要在s的末尾找到nul,这样才能返回strlen(s)
    【解决方案2】:

    strlcpy()strncpy() 更安全,所以你不妨使用它。
    没有它的系统通常会有一个 s_strncpy() 来做同样的事情。

    注意:你不能将任何东西复制到dst_ptr,除非它指向某个东西

    【讨论】:

    • 安全一点,但也不安全。例如。 strlcpy 会截断源字符串以适应目标,这是一个安全风险。
    【解决方案3】:

    我不知道 strlcpy。我刚刚发现here

    strlcpy() 和 strlcat() 函数复制和连接字符串 分别。它们旨在更安全、更一致且 strncpy(3) 和 strncat(3) 的更不容易出错的替换。

    所以 strlcpy 接缝更安全。

    编辑:可通过here进行完整讨论。

    编辑2

    我意识到我上面写的内容并没有回答您问题的“在您的情况下”部分。如果您了解 strncpy 的局限性,我想您可以使用它并围绕它编写好的代码来避免它的陷阱;但如果您不确定自己对它的限制的理解,请使用 strlcpy。

    我对 strncpy 和 strlcpy 的局限性的理解是,你可以用 strncpy 做一些非常糟糕的事情(缓冲区溢出),而用 strlcpy 做的最糟糕的事情就是在这个过程中丢失一个字符。

    【讨论】:

    • 感谢您的解释。如何在strlcpy 中丢失一个字符?你能解释一下吗?
    • 如果你使用 strncpy 复制一个比你的缓冲区大的字符串,它不会在缓冲区的末尾放置一个'\0'。如果您使用 strlcpy,'\0' 将在那里,但缓冲区中将少一个字符。因此,如果有人在应该使用 memcpy 时使用了 strncpy,那么使用 strlcpy 而不是 strncpy 会使他失去一个 char。
    • 谢谢。因此,当使用strlcpy 时,您唯一需要注意的是,您应该为目的地提供(src_length + 1 for null)。对吗?
    • 不完全是。非常重要的是在第三个参数中最多给出 dst 大小以避免缓冲区溢出。从那里开始,如果第三个参数至少为 src_length + 1,则副本中不会丢失任何字符。也许您希望 src 被截断,如果第三个参数小于src_length + 1,就会发生这种情况。但要小心,要将 str 截断为 5 个字符,第三个参数应该是 6,并且 dst 大小必须至少为 6 以避免缓冲区溢出。
    • @hari 不,这是错误的。 strlcpy 的重点是避免输出缓冲区溢出。这意味着size 参数是destination 缓冲区的大小,而不是源缓冲区...准确地说,它是目标缓冲区中剩余的空间量。如果你“一直提供 src_length + 1”,你的代码将充满缓冲区溢出错误。
    【解决方案4】:

    您应该始终使用标准函数,在本例中为 C11 strcpy_s() 函数。不是strncpy(),因为不保证零终止是不安全的。而不是仅限 OpenBSD 的 strlcpy(),因为它也不安全,而且 OpenBSD 总是提出自己的发明,通常不会使其成为任何标准。

    http://en.cppreference.com/w/c/string/byte/strcpy

    函数 strcpy_s 与 BSD 函数 strlcpy 类似,不同之处在于 strlcpy 截断源字符串以适应目标(这是一个安全风险)

    • strlcpy 不会像 strcpy_s 那样执行所有运行时检查
    • strlcpy 不会通过将目标设置为空字符串或在调用失败时调用处理程序来使失败变得明显。
    • 虽然 strcpy_s 由于​​潜在的安全风险而禁止截断,但可以改用经过边界检查的 strncpy_s 来截断字符串。

    如果您的 C 库没有 strcpy_s,请使用 safec 库。 https://rurban.github.io/safeclib/doc/safec-3.1/df/d8e/strcpy__s_8c.html

    【讨论】:

      【解决方案5】:

      首先,您的 dst_ptr 没有分配空间,并且您没有将其设置为指向其他空间,因此分配任何内容可能会导致分段错误。

      Strncpy 应该可以正常工作 - 只需这样做:

      strncpy(dst_arr, src_str, sizeof(dst_arr));
      

      而且你知道你不会溢出 dst_arr。如果您使用更大的 src_str,您可能必须在 dst_arr 的末尾放置自己的空终止符,但在这种情况下,您的源是

      这在任何地方都有效,而且很安全,所以我不会看其他任何东西,除非它具有求知欲。

      还请注意,最好为 10 使用非幻数,这样您就知道它的大小与 strncpy 的大小匹配:)

      【讨论】:

      • strncpy 几乎从来都不是正确的事情,因为它会零填充但不会零终止。 OP 想要strlcpy,它以零终止且不填零。此外,谈论“在这种情况下”是错误的,因为这个问题当然只是一个说明......真正的代码不会将像“hello”这样的固定字符串复制到已知更大的固定大小的缓冲区.
      【解决方案6】:

      您不应为此使用 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); 是朝着正确方向迈出的一步。
      猜你喜欢
      • 2017-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-15
      • 1970-01-01
      • 1970-01-01
      • 2014-09-01
      • 2014-09-24
      相关资源
      最近更新 更多