【问题标题】:Const variable changed with pointer in C常量变量用 C 中的指针改变
【发布时间】:2012-02-21 03:46:13
【问题描述】:

变量i 被声明为 const 但我仍然可以使用指向它的内存位置的指针来更改值。怎么可能?

int main()
{

    const int i = 11;
    int *ip = &i;
    *ip=100;
    printf("%d\n",*ip);
    printf("%d\n",i);
}

当我编译时,我得到这个警告:

test.c: In function ‘main’:
test.c:11: warning: initialization discards qualifiers from pointer target type

输出是这个

100
100

【问题讨论】:

  • 您正在丢弃 const 限定符,如果您希望警告成为错误,请查阅您的编译器文档。

标签: c++ c pointers constants


【解决方案1】:

const 是一个编译时 功能。
它不会阻止您在脚下射击自己;这就是警告的目的。

【讨论】:

  • const 不一定只是编译时功能。在我的环境中,如果我在上面的示例中设置变量i static,那么程序在运行时会崩溃。
  • const 和 static 是有区别的。 const 允许编译器生成警告。 static 允许编译器将值替换为文字数字或将其放在只读内存或其他任何它喜欢的地方
  • @Martin Beckett: static 单独不允许编译器将其放置在只读内存中 - 原则上只有 const 允许这样做,但实际上const 必须是与static 结合使用以获得这种效果,因为对于自动变量这样做根本不切实际。 const 单独的变量可以在使用时被文字替换,甚至是自动的。
【解决方案2】:

const 不是对编译器的请求,以使其无法更改该变量。相反,它是对编译器的承诺,但你不会。如果你违背了你的承诺,你的程序可以做任何事情,包括崩溃。

例如,如果我使用具有-O2 优化级别的 gcc 编译您的示例代码,则输出为:

100
11

允许编译器将const 限定变量放在只读内存中,但它没有必须(除了其他任何东西,某些环境不要实施任何这样的事情)。特别是,将自动(“本地”)变量放在只读内存中几乎总是不切实际的。

如果将i的声明改为:

static const int i = 11;

那么你很可能会发现程序现在在运行时崩溃了。

【讨论】:

  • 实际上并不是static 导致程序崩溃。这是不正确的演员阵容。这就是错误,错误的确切后果可能取决于许多微小的因素,例如static。而在另一个编译器上,只有当有 no static; 时你才会看到崩溃。错误的结果很难预测。
  • 实际上不是演员阵容导致程序崩溃——演员阵容是允许的,如果有点可疑的话。它正在修改使用 const 限定符声明的对象,这就是错误。
  • static is 与崩溃有关。在现代系统中,static const 变量(或文件范围内的const 变量)可以分配在只读段上,尝试写入这样的地址会导致保护错误。
【解决方案3】:

虽然这可能是 C 中的警告,但这是 C++ 中的编译器错误。

如果您设法在 C/C++ 中强制转换此 const int,那么它可能会导致 未定义的行为。原因是,您将数字文字(即 const int i =11;)从 const 转换为可变值。

您的代码显示的内容与以下顺序有所不同:

int x = 11;    // x is modifiable
const int i = x;
int *ip = (int*)(&i);  // ok
*ip=100;

【讨论】:

  • 是警告还是错误取决于编译器的灵活性,而不是语言。在 C 和 C++ 中都是错误的。
  • @ChrisLutz,对于 C,它可能是可靠的(这就是我使用“可能是”的原因),但是,对于 C++,它肯定是编译器错误,因为破坏了 const 的正确性。跨度>
【解决方案4】:

无法修改const 限定的对象。如果做出这样的尝试,程序会表现出未定义的行为 (C99 6.7.3.5)。 (您的程序不正确。)

更多关于const声明为const的对象可以修改,但不能使用const-qualified左值。这在涉及指针时最为明显。例如,考虑以下声明:

int i = 10;
int *p1 = &i;
const int *p2 = &i;

可以通过i 本身和p1 修改i,但不能通过p2。这意味着*p2 可以计算出不同的值,即使p2 指向const 对象,因为other 语句可能会更改p2 指向的对象(别名指针,如示例所示)。

但是,如果 i 本身是 const-qualified,那么尝试通过 p1 对其进行修改将产生未定义的行为(就像您的代码一样)。

【讨论】:

    【解决方案5】:

    导致程序行为未定义的不是修改i 的尝试,而是ip 的初始化。

    const int i = 11;
    int *ip = &i;
    

    &i 的类型为 const int*ip 的类型为 int*。尝试使用const int* 值初始化int*违反约束。符合要求的实现需要发出诊断;一旦完成,它可能会或可能不会拒绝翻译单元。如果它接受它,C 标准就不会说明生成的程序的行为。

    接受此类内容的编译器通常会生成等效于从const int* 转换为int* 的代码,从而使声明实际上等效于:

    int *ip = (int*)&i;
    

    但该语言不需要这种行为。

    不要忽视警告。

    (请注意,使用强制转换,代码不会违反约束;然后是以下行为

    *ip = 100;
    

    未定义,因为它试图修改const-qualified 对象。)

    gcc 尤其有很多情况,其中约束违规的诊断被处理(默认情况下)作为警告而不是致命错误。我个人不喜欢 gcc;它允许通过太多糟糕的代码。

    (您的程序的行为也未定义,因为您在没有可见声明的情况下调用printf;添加#include <stdio.h>。并且int main() 应该是int main(void)。)

    【讨论】:

      猜你喜欢
      • 2011-08-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多