为什么即使目标缓冲区为零,strncpy也能复制源字符串?

时间:2016-11-27 20:14:36

标签: c buffer strcpy strncpy

int main() {
    char src[] = "santosh";
    char dest[0];
    strncpy(dest, src, 4);
    printf("%s\n", dest); // expecting o/p "sant" but o/p was "santtosh"
    int i = 0;
    while (dest[i]) {
        printf("%c", dest[i]);   //expecting output "sant" but it was "santtosh"
        i++;
    }

    printf("\n");
    i = 0;

    while (src[i]) {
        printf("%c", src[i]); // expecting o/p "santosh" but it was "anttosh"
        i++;
    }
    return 0;
}

我遇到了这段代码的问题,我使用gcc编译器运行代码,这是我得到的输出(评论部分)。为什么行为不符合预期?为什么strncpy()能够复制源字符串,即使dest buff不够?

1 个答案:

答案 0 :(得分:1)

您的代码调用未定义的行为,尝试解释为什么它会产生某些输出或另一个是没有意义的。

以下是问题:

  • 您不包括<stdio.h>,也不包括<string.h>。调用之前未定义的函数不正常。包括正确的头文件以避免未定义的行为。

  • char dest[0];定义了一个大小为0的数组。无法访问此类对象,即使其地址有意义也无法访问。 gccclang允许这样做,但作为C标准的扩展。这个定义看起来像一个拼写错误,使用gcc -Wall -Wclang -Weverything来启用有用的警告,以防止出现这种愚蠢的错误。

  • strncpy(dest, src, 4);调用未定义的行为,因为dest的长度小于4。请注意,如果dest的长度为4,则行为仍然容易出错,因为如果源字符串的长度大于或等于size参数,strncpy将不为null终止目标数组。此处"santosh"的长度为7,因此dest将包含字符sant,但没有空终止符。您的while循环将调用未定义的行为,因为while (dest[i])将访问超出其大小的deststrncpy()容易出错,其语义被广泛误解,并且很容易导致错误。 请勿使用此功能

  • 如上所述,while (dest[i])会调用未定义的行为,因为dest无法取消引用为当前定义的,或者即使它被定义为char dest[4];

以下是使用snprintf()strncat()的改进版本:

#include <stdio.h>
#include <string.h>

int main(void) {
    char src[] = "santosh";
    char dest[5];

    *dest = '\0';
    strncat(dest, src, 4);
    printf("%s\n", dest);        // will output "sant"
    for (int i = 0; dest[i]; i++) {
        printf("%c", dest[i]);   // will output "sant" too
    }
    printf("\n");

    snprintf(dest, sizeof dest, "%s", src);
    printf("%s\n", dest);        // will output "sant"
    for (int i = 0; dest[i]; i++) {
        printf("%c", dest[i]);   // will output "sant" again
    }
    printf("\n");

    for (int i = 0; src[i]; i++) {
        printf("%c", src[i]);    // will output "santosh"
    }
    printf("\n");

    return 0;
}