#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;
}
z
上附加一个NUL字符?z[20] = '\0';
的操作,它将编译并显示结果,而不会出现任何错误。我声明z
数组大小为15时,这不是对内存的非法访问吗?答案 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)
为什么需要在数组
z
上附加一个NUL字符?
%s
中的 printf
打印所有内容,直到NUL终止符为止。如果没有在末尾添加NUL终止符,则printf
将继续访问数组之外的无效内存位置,直到NUL终止符调用未定义行为为止。
我也评论了这一行,输出没有变化,为什么?
请考虑倒霉,z[14]
有一个NUL终止符。无法保证,您仍然可以调用未定义行为。
如果我执行类似
z[20] = '\0';
的操作,它将编译并显示结果,而不会出现任何错误。我声明z
数组大小为15时,这不是对内存的非法访问吗?
C是一种松散类型的语言,不进行任何绑定检查。所有的权力都掌握在程序员的手中,您应该正确地编写代码。
是的,它访问非法内存。这将调用“未定义行为”,这意味着任何事情都可能发生。考虑一下自己不幸没有崩溃或发生任何事情