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;
}