【问题标题】:Problem on strncpy with source string longer than destination array源字符串长于目标数组的 strncpy 问题
【发布时间】:2020-04-16 12:52:46
【问题描述】:

我试图思考如何制作函数strncpy,我遇到了这个问题。

    char src[] = "123456789A";
    char dest[10];
    int n = 10;
    printf("strncpy:%s\n", strncpy(dest, src, n));

输出

strncpy:123456789A123456789A

发生了什么?

【问题讨论】:

  • dest 没有空间用于终止空字符,因此最终不会有空间,因此 printf 将继续从内存中打印,直到找不到空字符为止。
  • 遇到未定义的行为
  • 仔细阅读strncpy 的文档,特别是当字符串大于指定的最大大小时会发生什么。基本上strncpy 函数是没用的。使用strncpy 有意义的用例并不多。

标签: c buffer c-strings strncpy


【解决方案1】:

快速回答:strncpy不是你的朋友!

strncpy不是strcpy更安全版本,它将从src复制最多n个字符,如果src更短, 将用空字节填充目标,总共 n 个字符。

如果源字符串包含n 或更多字符,则目标数组将不会以null 终止,并且传递给printf("%s", 将具有未定义行为:@987654331 @ 将在 dest 结束后继续从内存中读取和打印字节,直到它找到一个空字节或直到这种未定义的行为导致其他不可预知的副作用......

strncpy 的语义违反直觉且容易出错,请避免使用此函数。长答案见这篇文章:https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/

【讨论】:

    【解决方案2】:

    正如其他人所说,如果目标大小与字符串长度相同,strncpy 将不包含终止 null。为了给您一个实用的答案,我通常只需使用 sizeof 从目标大小中减去一个即可获得目标大小,包括终止符的空间:

    char src[] = "123456789A";
    char dest[10];
    printf("strncpy:%s\n", strncpy(dest, src, sizeof(dest) - 1));
    

    这给出了“strncpy:123456789”的输出,这是一个短于您想要的字符,但至少是定义的行为,并让您知道目标缓冲区不够大,无法包含空终止符。因此,为您提供所需结果的最终代码将是:

    char src[] = "123456789A";
    char dest[11];
    printf("strncpy:%s\n", strncpy(dest, src, sizeof(dest) - 1));
    

    【讨论】:

    • printf("strncpy:%s\n", strncpy(dest, src, sizeof(dest) - 1)); 在这两种情况下都有未定义的行为,因为dest[sizeof(dest)-1] 保持未初始化状态。
    【解决方案3】:

    数组dest 不包含字符串,因为没有足够的空间容纳复制的源字符串的终止零'\0'

    所以要输出数组使用以下语句

    printf("strncpy: %*.*s\n", n, n, strncpy(dest, src, n));
    

    否则你必须像下面这样写

    strncpy( dest, src, n )[sizeof( dest ) - 1] = '\0';
    
    printf("strncpy: %s\n", dest );
    

    在这种情况下,目标数组不会包含将被零字符覆盖的源字符串的最后一个字符。

    如果您想复制的字符少于目标数组的大小,那么复制后的操作取决于您的意图。如果您只想覆盖已存储在目标数组中的部分字符串,则无需执行任何其他操作。否则将位置n处的字符设置为零字符。

    这是一个演示程序。

    #include <stdio.h>
    #include <string.h>
    
    int main(void) 
    {
        char src[] = "123456789A";
        char dest[10] = "543216789";
    
        size_t n = 5;
    
        strncpy( dest, src, n );
    
        printf("strncpy: %s\n", dest );
    
        strncpy( dest, "Hello", n )[n] = '\0';
    
        printf("strncpy: %s\n", dest );
    
        return 0;
    }
    

    它的输出是

    strncpy: 123456789
    strncpy: Hello
    

    【讨论】:

    • @chqrlieforyellowblockquotes 没关系。
    • @chqrlieforyellowblockquotes 没有未定义的行为。在 strncpy 的调用中使用了目标数组的大小。
    猜你喜欢
    • 2012-04-07
    • 1970-01-01
    • 1970-01-01
    • 2021-02-19
    • 2020-10-01
    • 1970-01-01
    • 2016-04-19
    • 2020-07-01
    • 1970-01-01
    相关资源
    最近更新 更多