为什么需要将NUL字符附加到数组?

时间:2018-08-04 07:08:53

标签: c arrays character

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

int main()
{
    char x[] = "Happy birthday to You";
    char y[25]; 
    char z[15];

    printf("The string in array x is: %s\nThe string in array y is: %s\n", x, strcpy(y, x));

    strncpy(z, x, 14);

    z[14] = '\0'; // Why do I need to do this?
    printf("The string in array z is: %s\n", z);
    return 0; 
}
  1. 为什么我需要在数组z上附加一个NUL字符?
  2. 我也评论了这一行,输出没有变化,为什么?
  3. 如果我执行类似z[20] = '\0';的操作,它将编译并显示结果,而不会出现任何错误。我声明z数组大小为15时,这不是对内存的非法访问吗?

4 个答案:

答案 0 :(得分:2)

  

为什么需要向数组z附加NUL字符?

来自strncpy个人:

  

如果目标末尾没有隐式附加空字符   源大于num。因此,在这种情况下,目的地不应   被视为以null终止的C字符串(这样读取它会   溢出)。


  

我也评论了这一行,输出没有变化,为什么?

我的猜测是:z已被初始化为0(对于具有非静态存储持续时间的阵列,您不能依靠它)


  

当我声明我的z数组是   大小为15?

是的,越界数组访问具有未定义的行为,并且可能导致崩溃或错误的程序输出。

如果要避免对NUL终止符进行硬编码,可以切换到snprintf

snprintf(z, sizeof z, "%.*s", 14, x);

答案 1 :(得分:2)

C语言没有本地字符串类型。在C语言中,字符串实际上是一维字符数组,以空字符\0结尾。

  

为什么需要在数组NUL上附加一个z字符?

strcpy()/strncpy()这样的

C库函数可对以null结尾的字符数组进行操作。 strncpy()最多将 count 个字符从源复制到目标。如果找到终止的空字符,则将其复制到目的地并返回。如果在复制整个源数组之前已达到 count ,则结果字符数组不是以空字符结尾的,因此您需要在目标末尾显式添加以空字符结尾的字符。

  

我也评论了这一行,输出没有变化,为什么?

我只能说你很幸运。目的地的最后一个字符(即z[14])可能具有所有位0。但这可能并非每次运行程序都发生。确保在使用strncpy()时明确添加空终止符,或使用其他snprintf()这样的自动为您执行此操作的C库函数。

  

如果我做类似z[20] = '\0'的事情,它正在编译并显示结果,没有任何错误。我声明z数组大小为15时,这不是对内存的非法访问吗?

数组z的大小为15,您正在访问z[20],即访问数组z的大小超出限制。在C语言中,超出范围访问数组是未定义的行为。未定义的行为包括程序可能执行不正确(崩溃或无提示地生成错误结果),也可能偶然执行了程序员想要的操作。

答案 2 :(得分:1)

在C语言中,所有char字符串实际上都称为 空终止 字节字符串。所有的字符串函数都使用null终止符来知道字符串的结尾,因为否则它们将不知道字符串的长度(不要忘记数组会衰减到它们的第一个元素的 pointers ,指针除了类型外,没有其他有关它们指向的信息。

还要注意,在某些情况下,strncpy不会不会自动终止目的地,这意味着您必须明确地执行此操作。您在示例中使用的就是这种情况。

如果字符串未终止,则字符串函数可能会超出搜索范围,这将导致undefined behavior。不幸的是,UB的一种可能性是貌似工作。

最后,未初始化的局部非静态(也称为“自动”)变量(包括数组)将具有不确定(且看似随机)的值和内容。您可能很幸运,z[14]恰好包含一个零。

答案 3 :(得分:1)

  1.   

    为什么需要在数组z上附加一个NUL字符?

    %s中的

    printf打印所有内容,直到NUL终止符为止。如果没有在末尾添加NUL终止符,则printf将继续访问数组之外​​的无效内存位置,直到NUL终止符调用未定义行为为止。

  2.   

    我也评论了这一行,输出没有变化,为什么?

    请考虑倒霉z[14]有一个NUL终止符。无法保证,您仍然可以调用未定义行为。

  3.   

    如果我执行类似z[20] = '\0';的操作,它将编译并显示结果,而不会出现任何错误。我声明z数组大小为15时,这不是对内存的非法访问吗?

    C是一种松散类型的语言,不进行任何绑定检查。所有的权力都掌握在程序员的手中,您应该正确地编写代码。

    是的,它访问非法内存。这将调用“未定义行为”,这意味着任何事情都可能发生。考虑一下自己不幸没有崩溃或发生任何事情