【问题标题】:Why is it Illegal to create a Pointer to a Pointer that points to a const in C?为什么在 C 中创建指向指向 const 的指针的指针是非法的?
【发布时间】:2019-01-15 06:43:01
【问题描述】:

这是常见的“分配时丢弃的 const 限定符”错误的来源。但是,我不明白为什么这样做是非法的?

考虑这段代码,

int sum_array(const int *a, int n){
   int sum, *i;
   sum = 0;
   for(i = a; i<a+n; i++)
       sum += *i;
   return sum;
}

显然我可以使用i = 0 和比较a + i &lt; a+n 进行相同的操作;但是,为什么简单地将变量的地址复制到另一个变量中是非法的,这对我来说没有意义?

通常,const 变量表示变量的值不能更改。例如。 const int x = 7,这里我们声明x的值不能从7改变。

但是,使用 const 指针,创建更改变量的“潜力”也是非法的。 IE。 i = a 不会改变 a 指向的值,真正“非法”的应该是 *i = 0,但是,i = a 仍然是非法的,因为它让我们有可能改变 a。

我知道你可以回答我“因为语言是这样创建的”,我只是想知道我在这里是否遗漏了什么。

【问题讨论】:

  • 您不应该使用指向非常量数据的指针来修改常量(不可修改)的数据。使用 const int *i; 可以防止您写入(意外)sum += (*i = 3); 或同样不可信的东西,它会修改 i 指向的值。
  • 在这种特殊情况下,本地指针只会增加混乱。正确编写的代码是int sum_array(const int *a, int n){ int sum = 0; for(size_t i = 0; i&lt;n; i++) sum += a[i]; return sum; }
  • “为什么在 C 中创建一个指向常量指针的指针是非法的?”,不要吹毛求疵,但这句话是不是有点误导? OP 的示例中没有“常量指针”。要拥有const pointer,必须编写类似int sum_array(int * const a, int n) 的内容。现在指针a 是常量。
  • 你是对的,类型保护指针之前的 const 和类型保护指向的对象之后的 const,我的错误。我更新了标题,希望它足够准确。

标签: c pointers memory


【解决方案1】:

const 的主要目的是争取编译器的帮助,以确保您不会意外修改给定对象。它为此使用类型系统。如果您愿意,您实际上可以通过编写显式转换来规避它;但通常你不想要那个,因为如果你想要那个,那通常意味着你并不真的想要 const 开头。

【讨论】:

  • 天堂禁止将const int* 指针分配给中间的void* 指针,然后再分配给int *。有很多方法可以阻止编译器尽其所能提供的帮助。
【解决方案2】:

const 有两个用途:

  1. 在函数调用的契约中(这里是int sum_array(const int *a, int n))表示函数不会使用参数a来修改a指向的内容(a不是常量,它是指向被认为是常量的事物的指针)。然后调用者知道数据不会通过调用被修改,编译器必须尽可能地强制执行:

  2. 在函数定义中,实现者可以防止意外修改a 指向的内容。 IE。如果他试图写类似*a = someValue; 的东西,那么编译器会因为合同被违反而抱怨。但随后它也验证了该合同无论如何都得到了尊重。因此,任何来自a 的派生变量可能允许访问相同的数据,都必须被视为const,否则将违反合同。然后写int *p = a 显然违反了这一点,因为p 不是指向常量数据的指针,这会让你写*p = someValue 来修改数据。

注意 const 并不表示数据是常量,只是表示禁止通过指针修改数据。那么:

const int *a...
int *p = a;

禁止但

int *a...
const int *p = a;

是正确的,因为在这种情况下,您有一个指针,可让您通过它修改数据,并且您构造了一个派生的指针,以限制通过派生指针访问相同数据。数据可通过a 修改,但不能通过p 修改。限制从来没有危险,开放可以。

当然,您可以使用强制转换来强制执行危险的派生(这可以让您删除const),但我不建议这样做。 忠告,如果你想这样做,请克制自己,三思而后行,如果之后你真的想这样做,那就让事情搁置一整夜,第二天再考虑。 .. 配方是:

const *a...;
int *p = (int *)a; // "seriously refrain" advice

【讨论】:

  • 这是 C++ 示例,问题与 C 无关。
【解决方案3】:

问题提出,给定const int *aint *i = a; 可以被允许,而随后的*i = 0; 将被禁止(“非法”)。

这是不可行的,因为它需要编译器跟踪i 中有关数据来源的信息。在出现*i = 0; 时,编译器必须知道i 包含一个从a 初始化的值。

我们可以用非常简单的代码做到这一点。但考虑到代码可能非常复杂,有循环、分支和函数调用。在*i = 0; 出现的那一点,编译器通常无法知道i 是否仍然持有来自a 的地址,还是已经更改为其他地址。 (我认为这等同于停机问题,因此通常在逻辑上是不可能的)。

C 使用类型来管理它。当a 是指向const 的指针时,它只能分配给指向const 的指针(除非被显式强制转换覆盖)。所以i必须是指向const的指针,这样编译器才能知道它指向的东西不应该被修改。

【讨论】:

    猜你喜欢
    • 2011-01-14
    • 1970-01-01
    • 2015-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-15
    相关资源
    最近更新 更多