全局内存分段错误

Agr*_*cus 0 c

第一个文件:

//a.c

const int i = 9;   //Just contains the variable definition
Run Code Online (Sandbox Code Playgroud)

第二个文件:

//b.c

#include<stdio.h>

extern int i;

int main()
{
    int *ptr = &i;

    printf("Before: %d\n",i);

    *ptr = 99;

    printf("After:  %d\n",i);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我原本期望在bcconst出现限定符错误/警告extern int i;和只读内存修改错误,但编译却进展顺利。*ptr = 99;

第一个打印语句打印良好,但第二个打印语句出现分段错误

为什么它没有在编译阶段崩溃并出现预期的错误,而是给出分段错误?

注意 - GCC 8.3.0

Dan*_*ein 6

const int根据标准,尝试通过指针写入是未定义的行为。

实践中可能发生的情况是,您的变量被放置在可执行文件的.rodata区域中 - 该区域在内存中标记为只读 - 当您尝试通过指针写入它时,它会触发页面错误,导致程序崩溃。


const如果您需要写入变量,则删除应该可以解决此问题。

  • 不 - 编译器无法知道您的“extern int i”是放置在“.rodata”中的常量。 (3认同)

tst*_*isl 6

翻译单元单独编译。因此编译器不知道它iconst其他文件中。它信任以下声明:

\n
extern int i;\n
Run Code Online (Sandbox Code Playgroud)\n

那里没有const。这就是为什么您看不到任何警告。

\n

为了确保尽早检测到类似的错误,建议将全局变量的声明放入头文件中,并让两者a.cb.c包含此头文件。

\n

在这种情况下,编译器将在编译时生成诊断信息a.c,因为声明和定义不兼容。

\n
... a.h\n#pragma once\nextern int i;\n\n... a.c\n#include "a.h"\nconst int i = 9;\n\n... b.c\n#include<stdio.h>\n#include "a.h"\n\nint main()\n{\n    int *ptr = &i;\n\n    printf("Before: %d\\n",i);\n\n    *ptr = 99;\n\n    printf("After:  %d\\n",i);\n\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

编译命令:

\n
gcc a.c b.c\n
Run Code Online (Sandbox Code Playgroud)\n

错误:

\n
a.c:3:11: error: conflicting type qualifiers for \xe2\x80\x98i\xe2\x80\x99\n    3 | const int i = 9;\n      |           ^\nIn file included from a.c:1:\na.h:2:12: note: previous declaration of \xe2\x80\x98i\xe2\x80\x99 was here\n    2 | extern int i;\n      |            ^\n\n
Run Code Online (Sandbox Code Playgroud)\n
\n

编辑

\n

编译时,a.c编译器假设该对象是常量,并将其放入只读内存中。在 Linux 机器上,它将被放置.rodata在没有标志的情况下映射到进程地址空间的部分PROT_WRITE

\n

当内存被修改时b.c会引发分段错误异常(对内存的无效访问)并且程序崩溃。

\n

请注意,修改常量对象会触发未定义的行为。该程序可能会运行数年,然后突然开始崩溃。

\n