C - char* 数组的最后一个元素
C - Last element of char* array
下面的示例代码;
char* g_commands[]={
"abcd",
NULL
};
int main()
{
g_commands[1] = "efg";
char ** tmp = &g_commands[0];
for(; *tmp != NULL; tmp++){
printf("%s", *tmp);
}
return 0;
}
因为 tmp 在一个循环中指向 g_commands 数组中的指针,在我将“efg”分配给 g_commands[1] 之后,我预计该循环自上次以来会产生分段错误g_commands 的元素不再为空。但是程序无一例外地完成并成功打印了 abcdefg。
为什么会这样?编译器是否也将 NULL 添加到 char* 数组的末尾?
I expect the loop create a segmentation fault since the last element of g_commands is not null anymore. But the program finishes without exception and prints abcdefg successfully.
Why is it so? Does the compiler add NULL to the end of char* array as well?
您调用 undefined behavior,因为您取消引用指向指向数组末尾的指针 tmp
的指针,并尝试使用 printf("%s", *tmp)
.
打印不确定的字符串
未定义的行为不需要提供错误的结果。当事情看起来是正确的时候就认为事情是正确的,这是一种误解。
你不能期待任何事情。解释未定义行为的原因和方式也没有多大意义,因为它与您编写生产代码完全无关。
我知道有些人喜欢调查这些并查看实现的行为,但如果您对编写不易受影响、可移植和可靠的代码感兴趣,通常认为这些不是需要深入关注的事情。
程序有未定义的行为。特别是它意味着程序可以产生预期或意外的结果。
I expect the loop create a segmentation fault since the last element
of g_commands is not null anymore
程序运行时没有分段错误,因为数组 g_commands
char* g_commands[]={
"abcd",
NULL
};
在全局命名空间中定义,数组之后没有其他对象的定义。这样的声明具有静态存储持续时间,编译器通常将此内存设置为零。
如果你将移动main中的定义,如
#include <stdio.h>
/*
char* g_commands[]={
"abcd",
NULL
};
*/
int main()
{
char* g_commands[]={
"abcd",
NULL
};
g_commands[1] = "efg";
char ** tmp = &g_commands[0];
for(; *tmp != NULL; tmp++){
printf("%s", *tmp);
}
return 0;
}
那么出现segmentation fault的概率就很高
让我们一步一步来。
char* g_commands[]={
"abcd",
NULL
};
int main()
{
g_commands[1] = "efg";
此时 g_commands
已更改,就好像您已按以下方式初始化它一样:
// char* g_commands[]={
// "abcd",
// "efg"
// };
请注意,从现在开始 g_commands
中不再有终止空指针。
以下
char ** tmp = &g_commands[0];
也可以写成
// char ** tmp = g_commands;
现在,当您遍历 g_commands
的元素时,您正在测试 tmp
取消对空指针的引用。不幸的是,您之前确实用 non-null 指针覆盖了 g_commands
的最后一个元素,所以这个
for(; *tmp != NULL; tmp++){
printf("%s", *tmp);
}
运行 超出了数组的边界并调用了未定义的行为。
return 0;
}
下面的示例代码;
char* g_commands[]={
"abcd",
NULL
};
int main()
{
g_commands[1] = "efg";
char ** tmp = &g_commands[0];
for(; *tmp != NULL; tmp++){
printf("%s", *tmp);
}
return 0;
}
因为 tmp 在一个循环中指向 g_commands 数组中的指针,在我将“efg”分配给 g_commands[1] 之后,我预计该循环自上次以来会产生分段错误g_commands 的元素不再为空。但是程序无一例外地完成并成功打印了 abcdefg。
为什么会这样?编译器是否也将 NULL 添加到 char* 数组的末尾?
I expect the loop create a segmentation fault since the last element of g_commands is not null anymore. But the program finishes without exception and prints abcdefg successfully.
Why is it so? Does the compiler add NULL to the end of char* array as well?
您调用 undefined behavior,因为您取消引用指向指向数组末尾的指针 tmp
的指针,并尝试使用 printf("%s", *tmp)
.
未定义的行为不需要提供错误的结果。当事情看起来是正确的时候就认为事情是正确的,这是一种误解。
你不能期待任何事情。解释未定义行为的原因和方式也没有多大意义,因为它与您编写生产代码完全无关。
我知道有些人喜欢调查这些并查看实现的行为,但如果您对编写不易受影响、可移植和可靠的代码感兴趣,通常认为这些不是需要深入关注的事情。
程序有未定义的行为。特别是它意味着程序可以产生预期或意外的结果。
I expect the loop create a segmentation fault since the last element of g_commands is not null anymore
程序运行时没有分段错误,因为数组 g_commands
char* g_commands[]={
"abcd",
NULL
};
在全局命名空间中定义,数组之后没有其他对象的定义。这样的声明具有静态存储持续时间,编译器通常将此内存设置为零。
如果你将移动main中的定义,如
#include <stdio.h>
/*
char* g_commands[]={
"abcd",
NULL
};
*/
int main()
{
char* g_commands[]={
"abcd",
NULL
};
g_commands[1] = "efg";
char ** tmp = &g_commands[0];
for(; *tmp != NULL; tmp++){
printf("%s", *tmp);
}
return 0;
}
那么出现segmentation fault的概率就很高
让我们一步一步来。
char* g_commands[]={
"abcd",
NULL
};
int main()
{
g_commands[1] = "efg";
此时 g_commands
已更改,就好像您已按以下方式初始化它一样:
// char* g_commands[]={
// "abcd",
// "efg"
// };
请注意,从现在开始 g_commands
中不再有终止空指针。
以下
char ** tmp = &g_commands[0];
也可以写成
// char ** tmp = g_commands;
现在,当您遍历 g_commands
的元素时,您正在测试 tmp
取消对空指针的引用。不幸的是,您之前确实用 non-null 指针覆盖了 g_commands
的最后一个元素,所以这个
for(; *tmp != NULL; tmp++){
printf("%s", *tmp);
}
运行 超出了数组的边界并调用了未定义的行为。
return 0;
}