【问题标题】:Why does this implementation of 'strncpy' work?为什么这种“strncpy”的实现有效?
【发布时间】:2019-07-13 05:24:53
【问题描述】:

我必须为赋值重写一个模仿 strncpy 行为的函数,经过多次试验和错误,一些外部帮助,这是最终代码:

 15 char    *ft_strncpy(char *dest, char *src, unsigned int n)
 16 {
 17     unsigned int i;
 18     unsigned int size;
 19
 20     i = 0;
 21     size = 0;
 22     while (src[i] && i < n)
 23     {
 24         dest[i] = src[i];
 25         i++;
 26     }
 27     while (i < n)
 28     {
 29         dest[i] = '\0';
 30         i++;
 31     }
 32     return (dest);
 33 }

它完美无缺,但我不明白这部分:

 while (i < n)
 {
     dest[i] = '\0';
     i++;
 }

此时,i 的值应该是 (n - 1) 对吧?所以'\0' goes into dest[n-1] 和循环结束,因为i becomes equal to n 然后函数结束。

我们得到了一个看起来像这样的字符串:

"string copied\0not copied part"

并打印为:string copiednot copied part

我的问题是:

  • 为什么dest[n-1]='\0'dest[n]='\0' 不是那个while 循环,而是返回string copied 而不是'字符串复制而不复制的部分',而它们基本上做同样的事情?

  • 为什么 \0 在 while 循环之后打印出来时似乎被“忽略”,而当我使用 dest[n-1] = '\0' 时它被视为句号?

这是我用来运行测试并尝试理解的主要/替代功能:

int main()
{
     char str[] = "test de chaine";
     char *str2 = "chaine de test";

     ft_strncpy(str, str2, 6);
     printf("%s", str);
     return 0;
}

char    *ft_strncpy(char *dest, char *src, unsigned int n)
 {
     unsigned int i;
     unsigned int size;

     i = 0;
     size = 0;
     while (src[i] && i < n)
     {
         dest[i] = src[i];
         i++;
     }
         dest[n-1] = '\0';
     return (dest);
 }

【问题讨论】:

  • 当他们基本上做同样的事情时 - 不正确的假设。见Null-terminated strings
  • 试试这个:char x[] = "one\0\0\0two\0three"; printf("%s %s %s\n", x, x+6, x+10); (ideone)
  • 你不必返回你写入的缓冲区。你可以有一个关于它是否成功的返回。
  • “完美运行” --> 没有。

标签: c string strcpy strlcpy


【解决方案1】:

您需要了解,虽然它们看起来很相似,但 char 数组不是字符串,而字符串是 char 数组。某些函数(即 string.h 函数)将一个接一个地读取每个字节,并在它们停止时停止遇到'\0'。所以检查这个例子,你会有不同的看法。

int main()
{
     char str3[] = "This is a test";

     printf("\n %s", str3);
     str3[5] = '\0';
     printf("\n %s \n", str3);
     int i = 0;
     for(i = 0 ; i < strlen(str3) ; i ++)
     {
         printf(" %c -",str3[i] );
     }
     printf("\n");
     for(i = 0 ; i < 12 ; i ++)
     {
         printf(" %c -",str3[i] );
     }
     return 0;
}

我们打印的不是内存中的。特别是使用 %s 将流式传输 char 数组并在命中 '\0' 时停止。 您是否清楚本示例中发生的情况?

【讨论】:

    【解决方案2】:

    i 的值应该是 (n - 1) 对吧?

    情况不一定如此,因为第一个while 循环在遇到\0 字符时退出(即使i 小于n-1)。

    while-loop 用于确保dest 数组的其余部分正确初始化为\0

    您看到的“错误”行为(在复制字符串末尾打印的字符串)是由于两个循环以相同的条件退出:当i 变为n 时,第一个循环退出由于第二个循环有检查i&lt;n,所以它没有运行。

    这对应于手册中描述的行为:

    strncpy() 函数类似,只是最多复制srcn 字节。警告:如果src 的第一个n 字节中没有空字节,则放在dest 中的字符串将不会以空值结尾。

    如果您要复制值为abc 的字符串str,它将在下面的示例中正确显示:

    #include <stdio.h>
    
    char    *ft_strncpy(char *dest, char *src, unsigned int n)
    {
        unsigned int i;
        unsigned int size;
    
        i = 0;
        size = 0;
        while (src[i] && i < n)
        {
            dest[i] = src[i];
            i++;
        }
        while (i < n)
        {
            dest[i] = '\0';
            i++;
        }
        return (dest);
    }
    
    int main()
    {
         char str[] = "test de chaine";
         char *str2 = "abc";
    
         ft_strncpy(str, str2, 6);
         printf("%s\n", str);
         return 0;
    }
    

    【讨论】:

    • 当你有一个缓冲区作为参数传递给它来写东西时,你总是需要另一个参数来适应这个缓冲区的大小。
    • @TsakiroglouFotis 这通常是正确的,但 C 库 strncpy 函数没有,因为它假设目标缓冲区足够大(许多 C 库函数确实假设这样),这这就是为什么在这样的重新实现的情况下它是可以接受的。
    • 我的意思是strncpy 并不真正关心缓冲区的大小,而只关心要复制的字符数量。确保复制的字节数正确仍然是我们的工作(这只是确保满足停止条件的另一种方式,就像我们必须确保在末尾有一个空字节一样strcpy 的缓冲区)。但是,您是正确的,使用缓冲区大小参数是 C 中的好习惯。
    • 失败的极端情况:当n==0 和代码尝试while (src[0] &amp;&amp; ... 时代码是UB 最好使用while (i &lt; n &amp;&amp; src[i])
    【解决方案3】:

    strncpy() 被指定为精确写入 n 字节,与源字符串的长度无关。

    strncpy(dst, "foo", 5); // writes 'f', 'o', 'o', 0, and 0
    strncpy(dst, "foo", 3); // writes 'f', 'o', and 'o': dst is not a string now (*)
    strncpy(dst, "foo", 1); // write 'f': dst is not a string now (*)
    // (*) unless you make sure there is a '\0' somewhere else in dst
    

    【讨论】:

    • ...如果没有第二个while 循环,该函数只会写入strlen(src) 字节。
    猜你喜欢
    • 2013-12-17
    • 2011-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-17
    • 2015-06-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多