使用const外部值作为非const外部值是否安全?

时间:2014-06-25 15:15:33

标签: c gcc shared-libraries const extern

请参阅下一个代码和结果:

foo.c的:

const int extern_const = 1;

的main.c

#include <stdio.h>

extern int extern_const;

int main(void)
{
    printf("before: %d\n", extern_const);
    extern_const = 2;
    printf("after : %d\n", extern_const);
    return 0;
}

编译和结果:

$ gcc -shared -fpic foo.c -o libfoo.so
$ gcc main.c -L. -lfoo  -o test
$ LD_LIBRARY_PATH=`pwd` ./test
before: 1
after : 2

我声明了一个const int变量extern_const,它驻留在一个共享库libfoo.so中。 在main.c中,我将extern_const声明为extern int而不是extern const int,并将值从1更改为2. 这是否安全有效?

执行结果显示替换仍然有效。我听说覆盖const值会引发未定义的行为,实际上,当我一次编译foo.c和main.c(没有创建共享库)时,程序实际上在第二次printf之前以分段错误结束。

我想知道的是接下来的事情:

  1. 一般来说,更改外部库中任何const变量的值是否安全?
  2. 如果没有,GCC / GNU工具包是否安全?
  3. 如果1.和2.都错了,我是否得到了未定义行为的幸运案例?
  4. 如果1.或2.是正确的,那么有/无库的情况会有什么不同?

5 个答案:

答案 0 :(得分:5)

修改常量对象是未定义的行为。任何事情都可能发生。

在你的情况下,你可能已经变得不幸因为GCC还没有汇集所有常量变量和文字,它没有将它放入只读部分(定义更多它们并且可能发生),你的main()是访问该外部常量对象的第一个也是最后一个代码(虽然在假旗下作为非const)。

  

6.7.3类型限定符§6

     

如果尝试通过use修改使用const限定类型定义的对象   如果是非const限定类型的左值,则行为未定义。如果是尝试   通过使用左值来引用用volatile限定类型定义的对象   对于非volatile限定类型,行为未定义.133)

答案 1 :(得分:3)

  1. 没有
  2. 没有
  3. 基本上,它由编译器/工具链/操作系统来提供对const变量的保护。这些的一些组合尽可能地确保那些应该只读的内容是只读的,即使它意味着分配整个页面(几KB)只是为了存储一个变量。其他人做出了不同的权衡,并且不会为了保护一个变量而浪费大量空间,并且信任程序员不要做这样的疯狂事情。

答案 2 :(得分:1)

其他答案绝对正确,因为这是未定义的行为,因此你不应该这样做。您的代码破解为您“工作”的原因是动态链接工作方式的副作用,即复制重定位。基本上,所发生的是,由于主可执行文件不是与位置无关的,因此它必须具有其访问的所有数据对象的地址,该地址直接硬编码为执行访问的指令中的即时可执行文件。因此,链接器在主程序的可写数据段中分配可写空间(因为在运行时来自共享库的值),并且包含对动态链接器的指令,以便从中复制值。在启动时执行重定位时,共享库到主程序的数据。然后,修补共享库中的任何引用,以指向主程序数据中的新副本。

如果您希望看到代码失败,请尝试将主程序编译为与位置无关的可执行文件:

$ gcc -fPIE -pie main.c -L. -lfoo  -o test

看看会发生什么。请注意,PIE在许多强化系统上是默认的。同样,某些cpu架构的ABI(MIPS是一个,如果我没有弄错的话)从不需要复制重定位,因此即使没有PIE,你的程序也会崩溃。

答案 3 :(得分:0)

在foo.c中,添加:

int y;
void f(void) {
  y = extern_const;
}

编译foo.c并优化程序集。阅读f()的程序集。如果您的编译器与我的一样,您应该看到f()已经优化到相当于y = 1;

这意味着根据我的编译器(可能是你的编译器),你所做的并不合适,并且你应该得到任何奇怪的恶魔,因为在不同的文件中以不同的方式声明extern_const而可能会飞出你的鼻子

答案 4 :(得分:0)

您的示例违反了一个定义规则。这是未定义的行为,因此在编译阶段可能会发生任何,并且您的程序无法保证做任何有用的事情,另请参阅here

来自C standard

  

J.2未定义的行为

     

同一对象或函数的两个声明指定不兼容的类型   (6.2.7)。

  

6.2.7兼容类型和复合类型

     

2所有引用同一对象或函数的声明都应具有兼容的类型;   否则,行为未定义。

  

6.7.3类型限定符

     

10对于两种兼容的合格类型,两者都应具有相同的合格版本   兼容类型;说明符或限定符列表中类型限定符的顺序   不会影响指定的类型。

此外,正如其他人所说,修改const对象也是未定义的行为:

  

6.7.3类型限定符

     

6如果尝试通过use修改使用const限定类型定义的对象   如果是非const限定类型的左值,则行为未定义。如果是尝试   通过使用左值来引用用volatile限定类型定义的对象   对于非volatile限定类型,行为未定义.133)