【问题标题】:When using strcpy() does the destination string need to be one element bigger than the source string?使用 strcpy() 时,目标字符串是否需要比源字符串大一个元素?
【发布时间】:2017-06-30 16:40:20
【问题描述】:

考虑一下这段代码 sn-p(为了清楚起见,简化了语法)。

void simple (char *bar) {
    char MyArray[12];
    strcpy(MyArray, bar);
}

我的导师说 MyArray 最多可以从 bar 复制 12 个元素,但是从我的读过,MyArray 只能存储 11 个字符,因为它最后需要空字符的空间。因此,如果 bar 的接收值为 12 或更大,则会发生缓冲区溢出。我的导师说只有当收到 bar 的值时才会发生这种情况 为 13 或更大。谁是对的?如果你能引用一个可靠的消息来源,我将不胜感激,这样我就可以说服他。

【问题讨论】:

  • man strncpy 完全回答了这个问题。
  • man strcpystrcpy() 函数将 src 指向的字符串,包括终止的空字节 ('\0') 复制到 dest 指向的缓冲区中。
  • @Oka,您的报价不完整,因此是错误的。
  • 标题为strncpy,但代码使用strcpy。你问的是哪个?
  • 对不起大家,我指的是strcpy(),strncpy()是错别字

标签: c strcpy


【解决方案1】:

定义char MyArray[12]创建了一个包含12个char的数组,可以用来存储一个字符串。由于 C 中的字符串以空字符结尾,因此其中一个字符需要能够在字符串末尾存储空字节。

所以char [12] 类型的变量可以容纳最多11 个字符的字符串。在您的示例中尝试使用 strcpy 复制长度为 12 或更长的字符串将溢出数组的边界。

如果您要使用strncpy,如下所示:

strncpy(MyArray, bar, 12);

这不会溢出缓冲区,因为它最多会复制 12 个字符。但是,如果复制了 12 个字符,则意味着该字符串不是以 null 结尾的,因此在技术上不是字符串。然后尝试在 MyArray 上使用任何其他字符串函数,该函数期望以 null 结尾的字符串将读取数组的末尾。

【讨论】:

  • 问题是关于strncpy。它最多复制n 字节,然后不添加终止空字符。 12 个字符的数组可以容纳 12 个字符。空字节是调用 strncpy 的人的问题。
  • @PaulOgilvie 标题写着strncpy,但给出的示例使用strcpy。我已经更新以解决这两个问题。
  • @PaulOgilvie strncpy 确实复制终止的空字符,如果可以的话,也就是说,如果有空间。 (但strncpy 可能会或可能不会复制终止空字符这一事实使其基本上无用。)
  • @SteveSummit 你可以使用它,但你需要足够的自律,在使用后手动添加空终止符。
  • @dbush 谢谢,这对我来说非常清楚。尽管我引用了丹尼斯·里奇(Dennis Ritchie)自己写的 C 书,但我的导师坚持认为他是正确的。这是一个中级信息安全课,所以他可能并不知道。
【解决方案2】:

strncpy 的正确用法是:

void simple(char *bar) {
    char MyArray[12];
    strncpy(MyArray, bar, sizeof(MyArray)-1);
    MyArray[sizeof(MyArray)-1]= '\0';
}

这只是放入一个终止空字符,无论strncpy 是否能够做到这一点。

【讨论】:

    【解决方案3】:

    很难说,因为你的问题措辞有点混乱,但我认为你是对的,你的导师错了。

    给定代码

    void simple(char *bar) {
        char MyArray[12];
        strcpy(MyArray, bar);
    }
    

    如果传入的bar 指向一个11 个或更少字符的字符串,则将一个有效的字符串复制到MyArray,而不会出现缓冲区溢出。但如果字符串长度为 12 个(或更多)字符,你是对的,会有缓冲区溢出,因为strcpy 也会复制第 13 个终止空字符。

    您之前询问过strncpy。给定代码

    void simple2(char *bar) {
        char MyArray[12];
        strncpy(MyArray, bar, 12);
    }
    

    如果传入的bar 指向一个11 个或更少字符的字符串,则一个有效的字符串将被复制到MyArray。但如果字符串是 12 个字符长,我们就会遇到不同的问题。 strncpy 将复制 12 个字符并停止,这意味着它不会复制终止的空字符。不会出现缓冲区溢出,但MyArray 最终仍不会包含有效字符串。

    此外,您还要求提供可靠的消息来源。我写了C FAQ list——你认为这可信吗? :-)

    【讨论】:

      【解决方案4】:

      我的导师说 MyArray 最多可以从 bar 复制 12 个元素

      更准确地说,数组MyArray 最多可以容纳数组bar 的12 个元素。否则将尝试访问数组之外​​的内存。

      所以事实上你的导师是对的。

      数组MyArray被声明只有12个元素

      char MyArray[12];
      

      但根据我的阅读,MyArray 只能存储 11 个字符,因为 最后需要空字符的空间

      终止的零也是一个字符。函数strcpy 复制源字符串中的所有字符,包括源字符串中出现的终止零。

      所以如果接收到的 bar 值为 12 或更大,则缓冲区溢出 会发生

      在这种情况下,神奇的数字 12 是什么意思?是数组 bar 中的字符数还是存储在数组 bar 中的字符串的长度(用作参数的字符串转换为指向其第一个元素的指针)?

      如果数字 12 表示存储在数组 bar 中的字符串的大小,则函数 strcpy 将尝试复制数组的所有字符,包括终止零,在这种情况下,数组 MyArray 必须被声明为有 13 个元素。

      char MyArray[13];
      

      但是,如果数字 12 表示数组 bar(用作函数的参数)中的元素数,并且它包含一个字符串,那么字符串的长度显然小于 12。所以数组 MyArray可以接受源数组的所有字符,包括终止零。

      所以造成混淆的原因是您没有得出一个共同的结论,即数字 12 是源字符串的长度还是源数组的大小。

      在第一种情况下,确实会有未定义的行为。

      在第二种情况下,如果源数组包含一个字符串,那么代码将是格式正确的。

      【讨论】:

        【解决方案5】:

        char数组和字符串相似,但又不一样。

        在 C 中,

        string 是一个连续的字符序列,以第一个空字符终止并包括第一个空字符。 C11dr §7.1.1 1


        void simple (char *bar) {
            char MyArray[12];
            strcpy(MyArray, bar);
        }
        

        我的导师说MyArray 最多可以从 bar 中复制 12 个元素,

        这是正确的:MyArray[] 最多可以接收 12 个字符。

        strcpy() 将内存从bar 开始复制到数组MyArray[] 并继续直到它复制一个空字符。如果尝试复制超过 12 个字符(12 的计数包括空字符),则结果是 未定义的行为 (UB)。

        MyArray只能存储11个字符

        不完全是。 MyArray[] 可以存储 12 个字符。要将数据视为 stringnull 字符 必须是这 12 个字符之一。当解释为 string 时,字符串包括所有最多为空字符的字符。它还包括 null 字符MyArray[] 的每个元素都可以是一个'x',但由于缺少空字符,因此该内存不会是 字符串

        所以如果bar的接收值是12或更大,就会发生缓冲区溢出。

        不完全是。如果strcpy() 尝试在MyArray[] 之外写入,则结果未定义。可能会发生缓冲区溢出。程序可能会停止等。结果未定义。这是未定义的行为

        我的导师说只有当bar 的接收值为 13 或更大时才会发生这种情况。

        bar 是一个指针 - 它可能没有“值 13”。 bar 可能指向的内存是一个字符串string 包括它的终止空字符,因此 string 可能由 12 个非空字符和一个最终空字符组成,总共 13 个字符。 MyArray[] 不足以存储该字符串的副本。

        谁是对的?

        我怀疑断开连接的含义不准确,即“bar 是 13”`。我认为教练报告的内容不正确。

        【讨论】:

          猜你喜欢
          • 2011-06-30
          • 2022-09-23
          • 2011-03-03
          • 1970-01-01
          • 2022-01-04
          • 2017-03-01
          • 2013-03-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多