【问题标题】:Is it possible change value of the const global variable?是否可以更改 const 全局变量的值?
【发布时间】:2013-12-28 11:19:14
【问题描述】:

是否可以更改 const global 或 extern 全局变量的值?通过指针?

你可以改变 const 局部变量的值。

【问题讨论】:

  • 常量,尝试修改它(通过指针或其他方式)将导致未定义的行为
  • 你认为关键字const是什么意思?
  • Mr.Ed Heal,您可以更改 const 局部变量的值。你不知道吗?
  • @selva - 真的。用codepad.org 证明这一点
  • @ED Heal:use in code::blocks 并查看结果 #include int main(void) { int const index = 10; int *ptr = &index; printf("%d\n",index); *ptr = 20; printf("%d\n",index);返回 1; }

标签: c pointers


【解决方案1】:

虽然在某些 C 实现中可能可能改变const 对象的值,但这种外观可能是一种错觉,并且很难以可控的方式进行,因为:

  • C 标准未定义尝试修改使用 const 定义的对象时的行为。
  • 修改可能会起作用,因为它会导致对象的某些用途发生变化,但不会改变其他用途。例如,编译器可能会将const 对象的值构建到其他表达式中,因此const int a = 10; int x = 2*a; 中的x 的初始化将x 初始化为20,而根本不引用a。如果您设法更改它,a一些 使用引用分配给它的内存并使用更改的值,而同一程序中 a 的其他使用使用以内置方式的原始值。显然,这会导致程序非常混乱。
  • 修改可能在一个 C 实现中“有效”,但在另一个 C 实现中失败,因为这两个实现以不同的方式管理 const 对象。
  • 当您以明显不相关的方式更改源代码(例如插入需要编译器将一些临时值保存到堆栈的额外计算)时,修改可能看起来在您的程序中有效,但会失败。当您更改编译器选项时,您的程序也可能会失败,尤其是当您启用优化时。

在许多 C 实现中,常量全局数据存储在标记为只读的内存区域中。尝试修改它会导致通常终止您的程序的异常。可能会要求操作系统更改此保护,然后修改内存。但是,这样做可能会产生来自编译器假设常量数据不会改变的副作用。

当您能够修改自动对象(例如定义为函数体的对象)但不能修改静态对象(例如定义在任何函数之外的对象)时,可能是因为静态对象保留了常量全局数据,它被标记为只读,但自动对象保留在堆栈中。堆栈包含多种不同的东西,在当今的处理器上,不可能将堆栈的一小部分标记为只读,而让其他部分可写。因此,您可能拥有修改const 自动对象的物理访问权限。但是,您仍然没有 C 标准的许可来执行此操作;行为未定义,并且可能存在会破坏您的程序的隐藏副作用。


另外,const 并不总是意味着对象是恒定的。当一个对象用const定义时,C 标准没有定义尝试修改它的行为。但是,可以添加 const 到指针并稍后将其删除。例如,这是合法代码:

int a = 3;
const int *b = &a;
int *c = (int *) b;
*c = 4;

之所以需要这种行为,是因为 C 语言最初设计时没有 const,因此其语义并未设计为适应它,并且在某些情况下,如果始终强制执行 const,事情将无法正常工作。

【讨论】:

  • 很好的解释。所以从你的角度来看,我不能尝试改变 const 值,无论它在哪里声明?
  • @selva:您可以尝试,从某种意义上说,您可以编写代码来尝试它。如果您想符合 C 标准,您可能不会这样做。如果您尝试,您的代码可能无法正常工作,即使它最初看起来可以工作。
【解决方案2】:

我猜这将取决于。例如,对于这个程序

#include <stdio.h>

const int val = 12345;

int main(void)
{
    printf("%d\n", val);

    int *tmp = (int *)&val;
    *tmp = 678;

    printf("%d\n", val);
    return 0;
}

程序(在 Linux 上使用 gcc 编译)产生分段错误。但是,如果将“const”行移到主函数内部并改为创建一个局部变量,它似乎确实可以工作(没有启用任何优化)。

在使用objdump 检查可执行文件时,变量val 位于名为.rodata 的部分中,当程序试图以某种方式更改内存时会导致分段错误。

【讨论】:

    【解决方案3】:

    C11:6.7.3 类型限定符:

    如果尝试通过使用来修改使用 const 限定类型定义的对象 具有非 const 限定类型的左值,行为未定义。

    换句话说,您可以说const-qualified 对象是不可修改

    【讨论】:

      【解决方案4】:

      您仍然可以尝试使用 mprotect 函数将数据部分更改为 RW 而不是 RO。

      http://linux.die.net/man/2/mprotect

      Self modifying code always segmentation faults on Linux

      由于它是您的程序自己的段,因此您不需要任何特权。此代码可能有效:

      #include <stdio.h>
      #include <sys/mman.h>
      
      const int c = 1;
      
      int main(int argc, char *argv[])
      {
          if(mprotect(&c, sizeof(int), PROT_READ | PROT_WRITE) == 0) c = 5;
          printf("%d\n", c);
          return 0;
      }
      

      【讨论】:

        【解决方案5】:

        常量变量不能改变,这就是为什么它们是常量。 可以更改其他变量,如果您可以访问变量本身,则不需要指针

        【讨论】:

          【解决方案6】:

          常量的值不能更改,因此称为常量

          【讨论】:

            【解决方案7】:

            这个问题没有意义:变量没有值,只有对象表达式有。

            您不能更改常量对象的值。

            但是,您可以使用某种合适的“常量引用”类型的变量来评估不同的事物:

            int a = 10;
            
            const int * const b = &a;
            
            int main()
            {
                print(b);
                *a = 20;
                print(b);
            }
            

            【讨论】:

            • @hacks:谢谢,已修复!
            【解决方案8】:

            是的,您可以:

            #include <stdio.h>
            
            const volatile int global = 10;
            
            int main(void)
            {
                int *ptr = (int*) &global;
            
                printf("Initial value of global : %d \n", global);
                *ptr = 100;
                printf("Modified value of global: %d \n", global);
                return 0;
            }
            

            由于volatile (take a look) 可能会起作用

            但是按照其他人的建议,不要这样做

            【讨论】:

            • 那太好了。我不会用它
            • 使用 GCC 4.2.1(Apple build 5666)在 Mac OS X 10.6.8 上使用默认选项编译并执行结果会产生总线错误。不应将“这恰好在我尝试在一个系统上使用一个 C 实现的一种情况下工作”与“这在 C 中受支持”混淆。事实上,这几乎有效; volatile 确实告诉 C 实现 global 的值可能会改变。但是,它并没有说您可以更改纯 C 中的值。它说该值可能会以某种 C 实现未知的方式更改。所以你可以编写其他代码来修改它,也许通过……
            • ... 修改global 所在页面上的内存保护,然后在C 中修改它。如果正确完成,它可能是基于C 标准组合的有效、完全定义的代码以及主机系统的内存保护功能规范(例如,用于更改内存保护的mprotect 调用的文档)。但是,就目前而言,这段代码并没有正确地进行修改。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-08-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多