【问题标题】:C++ pointer to const pointer指向 const 指针的 C++ 指针
【发布时间】:2019-01-04 20:53:17
【问题描述】:

我仍然对将const 放置在具有多个间接性的指针中的位置感到困惑。有人可以澄清一下吗?

例如现在我需要一个指向 const 指针的指针,这意味着这样一个变量 int **ppTargets 我可以将 int *pTargets 变量分配给它,例如:

int foo(int **ppTargets) {
    int *pTargets = /* calculate here */;
    *ppTargets = pTargets;
    return 37; // just e.g.
}

上面的代码缺少const。所以在foo 中,我希望pTargets 指向常量内存并且在初始化后不可分配(这样就不能写,例如pTargets++),那就是int const *const pTargets = /* assigned once */。接下来我要声明ppTargetsppTargets本身可以赋值,但是*ppTargets只能读取。

换句话说,在我想要的调用者代码中:

int const* pTargets;
foo(&pTargets);

我尝试如下声明foo,但收到错误you cannot assign to a variable that is const

int foo(int *const *const ppTargets)

【问题讨论】:

  • 使用typedef 将您的简单类型构建为您真正想要使用的类型
  • 你在自相矛盾:So I want pTargets to point to constant memory and be const itself, that would be int const *const pTargets ... 然后In the other words, in the calling code I want: int const* pTargets; 那么,是哪一个? pTargets 是否应该是 const ?
  • @eerorika,换句话说,我想禁止分配给pTargets**pTargets,但不是*pTargets。我还没有看到矛盾,让我想想可能被误解的地方......
  • @SergeRogatch I want to forbid assignment to pTargetsint const* pTargets 矛盾,因为可以分配 int const*

标签: c++ c pointers constants


【解决方案1】:

我总是从最右边的变量名向左读取 C/C++ 定义。

所以:

  • const char *p;

    p 是指向char 的指针,即const

    所以p可以修改,但*p不能。

  • const char * * const p = &a;

    p 是一个const 指针,指向一个指向char 的指针,即const

    所以p不能修改(因此我初始化了它); *p可以;但**p 不能。

[编辑 - 为完整性添加数组]

  • const char * * const p[4] = { &a, &b, &c, &d };

    p 是一个由 const 指针组成的 4 元素数组...

【讨论】:

  • 这个。所以 OP 想要int foo(const int ** const ppTargets)(或int foo(int const ** const ppTargets),这是一回事)。
  • OTOH,参数中的顶级const 对于可复制类型实际上毫无意义。
  • @TonyK 不,OP 说 ppTargets 本身可以被分配”。您不能分配const int ** const。而且int const *const的地址类型不同。
  • @eerorika:是的,再次阅读问题,你是对的。但我认为,OP自相矛盾。所以我会退出这个。
【解决方案2】:

您正在寻找的是int const * const * ppTarget。不,等等,您正在寻找int const ** const ppTarget。不不,是int * const * const * ppTarget

其中一个机会是正确的(我打赌第一个)。但是,您不希望人们阅读您的代码来猜测您的意思。这太令人困惑了。 C++ 可以做到这一点。

你应该做的是使用typedefs 来确保阅读代码的人明白你想要什么。

typedef const int *CINT_PTR;
CINT_PTR pTarget = ....;
CINT_PTR *ppTarget = &pTarget;

【讨论】:

  • 第二个是正确的,正如 John Burger 解释的那样。
  • 这让我的观点更有说服力。需要 5 位数 Stack Overflow 声誉的人(其中两个接近 100,000)的三个答案才能做到这一点。不要编写只有经过一番辩论才能被一群专家理解的代码。
【解决方案3】:

由于pTargetsconst int *,它的地址是const int **,这是你想要的函数参数的类型:

int foo(const int **ppTargets)
{
    int *pTargets = malloc(sizeof(int)*4);
    pTargets[0] = 1;
    pTargets[1] = 2;
    pTargets[2] = 3;
    pTargets[3] = 4;
    *ppTargets = pTargets;
    return 37;
}

int main()
{
    int const *pTargets;
    foo(&pTargets);
    return 0;
}

编辑:

如果要设置的变量定义为int const * const pTargets;,则设置它的唯一方法是在初始化时。然后你可以这样做:

const int *foo2()
{
    int *pTargets = malloc(sizeof(int)*4);
    pTargets[0] = 1;
    pTargets[1] = 2;
    pTargets[2] = 3;
    pTargets[3] = 4;
    return pTargets;
}

int main()
{
    int const * const pTargets = foo2();
    return 0;
}

【讨论】:

  • 现在如何在foo的代码中避免ppTargets++?这意味着变量本身也是const
  • @SergeRogatch ppTargetsfoo 的局部变量,因此如果您更改它,则不会反映在调用函数中。
  • 如果 "pTargets [points] 指向常量内存 并且 [is] const 本身",则调用 foo 将不起作用Serge在问题中说。你的 pTargets 是非常量的。
  • 为什么 pTargets 在 main 中声明为 int const * 而不是 const int *
  • @zmbq 这些是等价的。
【解决方案4】:

所以我希望pTargets 指向常量内存并成为const 本身, 那将是int const *const pTargets = /* assigned once */。接下来我 想声明ppTargets 可以分配ppTargets 本身,但是 那么*ppTargets只能被读取。

不幸的是,这没有意义。您的示例代码分配给*ppTargets,确实似乎是函数foo() 的主要目标。如果*ppTargets可以分配一次,那么可以再次分配。

不清楚为什么您希望foo() 的本地pTargetsconst,而不是仅仅不修改它,但是您可以将const 值分配给相应的非@ 对象987654334@-qualifed 类型。因此,您实际寻找的可能是

int foo(int const **ppTargets) {
    int const * const pTargets = /* calculate here */;
    *ppTargets = pTargets;
    return 37; // just e.g.
}

这似乎与您的预期用途一致:

也就是说,在我想要的调用代码中:

int const* pTargets;
foo(&pTargets);

对于任何类型T,指向T 的指针的类型可以拼写为T *。特别是,这个&pTargets 的类型是int const **(看起来很熟悉?),这是函数参数的适当类型,函数应该能够通过它设置调用者的pTargets 的值。

再一次,调用foo() 让它设置调用者pTargets 的值似乎正是重点。如果应该阻止 foo() 这样做,那么理想的方法是传递 pTargets 本身(按值),而不是传递它的地址并争论 const 限定符。

【讨论】:

  • 这不会编译,因为你不能分配给*ppTargets
  • 是的,@dbush。我让自己被 OP 的请求的第一部分甩了,这是为了一些实际上不能工作的东西。我已经实质性地重写了这个答案,以解决 OP 请求的问题,并提出 确实 工作的东西。我相信你不会对你提供的函数签名感到惊讶。
【解决方案5】:

所以我希望 pTargets 指向常量内存并成为 const 本身

接下来我要声明ppTargets,ppTargets本身可以赋值,但是之后*ppTargets只能读取。

为清楚起见,让int const *Ptr 并让int const * const(即Ptr const)为CPtr

就像您在想要一个指向 const int 的 const 指针时正确地写了 int const *const pTargets(即 CPtr)一样,当您想要一个指向 const int 的非 const 指针(即 @987654328 的类型)时也是如此@即CPtr*),你需要int const *const * ppTargets。请注意,Ptr* 将隐式转换为 CPtr*

您尝试的 int *const *const ppTargets 将是一个指向非 const int 的 const 指针的 const 指针。由于类型是 const 指针,所以不能赋值,这与您的要求相矛盾。


更一般地说,一个简单的经验法则是从右到左读取 C 指针类型声明,并且 constness 适用于关键字的左侧(除非它是最左侧的标记,在这种情况下它适用于右侧)。


既然我们已经找到了满足您所述要求的类型,请让我提请您注意您对 foo 的实现,它可以实现 *ppTargets = pTargets。这与*ppTargets只能读取”的要求相矛盾。

【讨论】:

    【解决方案6】:

    经过其他人的投入,尤其是@Mahesh 的Clockwise/Spiral 规则,以及一些争论,我明白了如何轻松地读写这些东西。

    我们应该看看哪些可以修改,哪些不能修改。所以考虑没有consts 的声明:int **ppTargets。我们希望ppTargets不能自己修改,*pTargets可以修改,**pTargets不能修改。

    然后,从右到左应用这些观察结果: int const * * const ppTargets

    最右边的const 表示ppTargets++ 是不可能的。

    然后中间没有const 表示(*ppTargets)=pTargets 是可能的。

    然后,最左边的另一个const(**ppTargets)++ 是不可能的。

    【讨论】:

      【解决方案7】:

      我认为间接的基本形式化

      (read-only|read-write) <memory zone> * (read-only|read-write) <pointer>
      

      其中&lt;pointer&gt; 本身就是一个内存区域。 对于双重间接,表达式变为

      (read-only|read-write) <memory zone> * (read-only|read-write) <memory zone/pointer-level2> * (read-only|read-write) <pointer-level1>
      

      让事情更难理解的是,在* 符号左侧大小的&lt;memory zone&gt; 之前或之后放置限定符(例如read-only)的可能性。在* 符号的右侧,限定符只能放在&lt;pointer&gt; 之前。

      在 C++ 中,read-only 表示 constread-write 是隐式限定符。

      因此我们可以:

      • char* p读写指针,指向读写char内存区
      • const char* p 指向只读的读写指针 char 内存区
      • char* const p 只读指向读写char内存区的指针
      • const char* const p 只读 指向只读 char内存区的指针

      然后我们可以将const 移动到基本类型之后产生等效声明:

      • char const* p 指向只读的读写指针 char 内存区
      • char const* const p 只读 指向只读 char内存区的指针

      与目标指针相比,对于每个间接级别,允许指针转换在源指针中具有相同的限定符或限制较少的限定符。

      因此,以下情况是有效的:

      int foo(const int* const* const p);
      
      {// equal leftmost qualifier
      const int* p = nullptr;
      
      const int** p1 = &p; // 2nd and 3rd qualifiers are less restrictive
      foo(p1);
      
      const int* const* p2 = &p; // 2nd qualifier is equal, 3rd one (implicit read-write) is less restrictive
      foo(p2);
      
      const int* const* const p3 = &p; // 2nd and 3rd qualifiers are equal
      foo(p3);
      }
      
      {// less restrictive leftmost qualifier of p
      int* p = nullptr;
      
      int** p1 = &p; // 2nd and 3rd qualifiers are less restrictive
      foo(p1); 
      
      int* const* p2 = &p; // 2nd qualifier is equal, 3rd one (implicit read-write) is less restrictive
      foo(p2);
      
      int* const* const p3 = &p; // 2nd and 3rd qualifiers are equal
      foo(p3);
      }
      

      在您的情况下,作为参数传递的指针的最左边限定符 (&amp;pTargets) 不等于或小于来自 foo 函数的指针的最左边限定符。

      【讨论】:

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