【问题标题】:Why assigning a regular pointer to a pointer-to-const is allowed but not the opposite?为什么允许将常规指针分配给指向 const 的指针,但不能相反?
【发布时间】:2021-01-03 10:44:37
【问题描述】:

将常规指针 ptr2 分配给指向常量的指针 ptr1,如下面的代码所示,不会产生任何警告。

#include <stdio.h>
    
int main(void)
{
    int a = 10;
    int b = 20;
    const int *ptr1 = &a;
    int *ptr2 = &b;
    ptr1 = ptr2;
    return 0;
}

但是,将指向常量的指针 ptr1 分配给常规指针 ptr2 会在编译期间产生警告,并且根据我正在关注的书(C Primer Plus by Stephen Patra),可能导致未定义的行为。

#include <stdio.h>

int main(void)
{
    int a = 10;
    int b = 20;
    const int *ptr1 = &a;
    int *ptr2 = &b;
    ptr2 = ptr1;
    return 0;
}

据我了解,当一个指针赋值给另一个指针时,相当于让两个指针指向同一个对象。所以如果我要写一个图表,两种情况都应该是这样的。 根据我的图表,分配的顺序不会改变结果。无论我将 ptr1 分配给 ptr2 还是将 ptr2 分配给 ptr1 都应该给我相同的图表,但现在我不确定我的理解是否正确。谁能解释一下这两种情况不同的原因,请告诉我我画的图是否正确。

【问题讨论】:

  • FWIW,当我尝试编译您的第二个示例时出现错误:invalid conversion from ‘const int*’ to ‘int*’ [-fpermissive]
  • @Dai :可能您使用的是 C++ 而不是 C 编译。在 C++ 中,这是一个需要大小写的错误(最好是 const_cast)。
  • @Dai :可能您使用的是 C++ 而不是 C 编译。在 C++ 中,这是一个需要强制转换的错误(最好是 const_cast)。

标签: c pointers


【解决方案1】:

该图仅适用于第一个示例。在第二个中,两个指针都指向a,而不是'b'。但这不是问题。

ptr1 上的 const 限定符的语义是不能通过该指针更改引用的数据。这并不意味着数据本身是恒定的,它是限定的指针,并且对数据没有任何特定的约束,除非通过这样的指针访问。

所以:

ptr1 = ptr2;

不会生成警告,因为它会将限制较少的类型分配给限制较多的类型,因此“安全”是因为它不允许通过 ptr2 已经允许的更大访问权限。

另一方面:

ptr2 = ptr1;

有效但在语义上是有问题的——从某种意义上说,放松ptr1 施加的限制通常是不可取的——而且很可能是无意的。因此发出警告。

如果分配是经过深思熟虑的,则应使用显式强制转换来指示它并抑制警告。但通常它表示设计不佳或语义错误,您应该在随意丢弃它之前考虑到这一点。只有在与缺乏适当“const-safety”的第三方代码交互时才需要抛弃const

【讨论】:

    【解决方案2】:
    const int *ptr1 = &a;
    int *ptr2 = &b;
    

    这实质上是说通过ptr1 进行的任何访问都是只读的。 C 将不允许您写入指向的内存。并且通过ptr2访问可以读/写。

    所以ptr1 = ptr2 是有效的,因为ptr2 指向的内存是读/写的。 ptr1 永远不会给它写信这一事实并不违反任何假设。

    另一方面,ptr2 = ptr 是非法的,因为可以通过ptr2 写入内存,但该内存被定义为只读。所以这违反了程序员设置的限制。如果违反这些限制,没人知道会发生什么。

    您可以通过强制转换强制它:ptr2 = (int*)ptr1。但在这种情况下,您必须确保通过ptr2 完成没有写入

    如果您需要这个,这表明您的 API 设计不当,您应该更改它。一些 C 标准函数会遇到这种情况,但那是因为它们是在 const 被引入 C 之前设计的。不要效仿他们的例子。

    【讨论】:

    • 关于“C 将不允许您写入指向的内存”:C 标准不需要 C 实现来强制执行这一点,就像 C 标准不需要 C 实现来禁止访问数组一样超出范围。
    • 由于ptr1const int*,C 标准要求*ptr1 = 5 是非法的。您不能通过 const 指针写入内存位置(这就是我的意思)。您可以通过非 const 指针写入相同的位置,因此*(int*)ptr1 = 5 允许的,但如果内存恰好是有效只读的,则行为未定义。
    • Re“这就是我想说的”:教学必须通过实际写下的文字来完成,而不是通过所要表达的文字来完成。学生无法读心。
    猜你喜欢
    • 2014-08-23
    • 2014-05-30
    • 2021-03-23
    • 2010-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-25
    相关资源
    最近更新 更多