【问题标题】:double pointer to constant pointer指向常量指针的双指针
【发布时间】:2020-03-06 09:10:17
【问题描述】:

嘿,有人能告诉我为什么在这个例子中会出现值 11 11,如果我理解正确的话 主块中的 p 是指向 Integer 的常量指针,这意味着我无法更改其地址的值 我怎么能在函数 foo 中通过

做到这一点
 #include <stdio.h>

int j = 11;
void foo(int **);

int main()
{
    int i = 10;
    int *const p = &i;
    foo(&p);
    printf("%d\n", *p);
}
void foo(int **p)
{
    *p = &j;
    printf("%d\n", **p);
}

【问题讨论】:

  • 请不要更新您的问题来解决已发布答案中的问题。这可能会使答案变得无用,有时如果您解决了所有问题,甚至您的问题也可能变得无用。请记住,这个论坛不仅是为了解决您此时此地的问题,而且也是为了让其他人在未来找到类似(或完全相同)问题的解决方案。
  • 这段代码不是有效的 C,所以如果一些编译器让它通过,不管任何人的猜测,它会做什么。您根本无法将int*const* 隐式转换为int**
  • @Someprogrammerdude 原始代码包含几个与所问问题无关的错误。请参阅我对更改的解释。编辑在被接受之前确实需要同行评审。我的编辑被接受了,这样答案就可以集中在被丢弃的限定词的概念上,而不是他特定的 sn-p 的特质。
  • @Lundin 尝试让 clang 或 gcc 准确地编译上面的代码 sn-p。它工作正常,但它们都给出丢弃的限定符警告。生成的可执行文件为两者打印"11\n11\n"
  • @WilliamRosenbloom C 语言中没有“编译器错误”之类的东西,这就是原因。编译器只需要在 C 语言违规时给出诊断信息。如果编译器选择通过警告、错误或发送到程序员家门口的手写便条来做到这一点,则与 C 标准无关。它仍然是违反语言标准的约束。如果您希望 gcc 或 clang 为您提供 C 语言违规的编译器错误,您必须使用 -std=c11 -pedantic-errors 进行编译。

标签: c pointers constants


【解决方案1】:

你有未定义的行为

首先您的函数foo 尝试修改main 变量p,它是常量。如果您在调用之前提供了正确的函数声明/原型foo,编译器将能够捕捉到这一点并警告您传递不兼容的类型。

另外foo你有任务

*p = &j;

这样你就可以让*p指向local变量j,这个变量的生命周期在foo函数结束时结束,当你取消引用它时指针无效main 函数。

【讨论】:

  • 问题已更新,将j 的声明移至全局范围。
【解决方案2】:

大多数编译器,包括 clang 和 gcc,都会对你的 sn-p 做出反应,并发出类似这样的警告。

const int * 作为int * 参数传递会丢弃限定符

这意味着编译器发现你违反了指针的恒定性。尽管抱怨,编译器会尝试补偿。编译器将忽略p 声明中的const 修饰符。 p 在运行时的有效 类型将是可变的int *

此行为不属于任何 C 标准。严格来说,const 就像你对编译器做出的承诺,你不会修改任何东西。这个承诺允许对机器代码进行某些优化,允许编译器假设某些值永远不会改变。 当你通过隐式类型 casting 转换巧妙地违反const 承诺时,更天真的(但在技术上仍然兼容)编译器可能会生成遇到分段错误或其他问题的机器代码,因为你背叛了恒定性假设。

现代编译器总是检查const 的东西是否真的是恒定的。如果不是,那么编译器会“丢弃限定符”,这意味着他们推断您并不是真的想说const。如果您从不更改变量的值,无论您是否将其声明为常量,大多数编译器还会推断您确实是指const

进一步阅读

【讨论】:

  • “更天真(但在技术上仍然合规)”不是真的。不为此提供诊断消息的编译器是不兼容的。这是违反简单分配规则的约束。自 1989 年以来一直如此。
  • 也没有所谓的“隐式类型转换”。存在隐式或显式转换。强制转换是一种显式转换。
  • @Lundin 我不知道铸造只是明确的。那很有意思。我已经更正了这个词。我现在正在查找简单的分配规则来验证您的第一条评论。
  • 6.5.16.1 "......并且左边指向的类型具有右边指向的类型的所有限定符;"。函数的参数传递是按照简单赋值的规则进行的。
【解决方案3】:

当将p 传递给foo 时,指针的const 限定符将被丢弃,因为函数参数的类型为int **

启用适当的警告后,您的编译器应该会抛出警告:

传递“foo”的参数 1 会从指针目标类型 [-Wdiscarded-qualifiers] 中丢弃“const”限定符

所以您应该将foo 更改为: void foo (int * const * p) 那么您的编译器将因错误而中止

分配只读位置

分配超出范围的局部变量的地址的问题已经被其他人指出了

【讨论】:

    【解决方案4】:

    程序有未定义的行为。

    对于初学者来说,函数 f 应该在 main 调用之前声明。

    在这种情况下,编译器会发出一个错误,指出参数的限定符 const 已被丢弃。

    通过指针更改常量对象(在本例中为常量指针)会导致未定义的行为。

    未定义行为的第二个原因是变量j 是函数f 的局部变量。所以退出函数后,变量不再存在,指针p的值无效。

    如果您将编写函数在 main 调用之前正确声明它,例如

    #include <stdio.h>
    
    void f( int *const *p )
    {
        int j = 11;
    
        *p = &j;
    
        printf( "**p = %d\n", **p );
    }
    
    int main(void) 
    {
        int i = 10;
    
        int * const p = &i;
    
        f( &p );
    
        printf( "*p = %d\n", *p );
    
        return 0;
    }
    

    您将收到类似于以下的错误消息

    prog.c: In function ‘f’:
    prog.c:7:5: error: assignment of read-only location ‘*p’
      *p = &j;
         ^
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-10-23
      • 1970-01-01
      • 2013-01-04
      • 2011-08-04
      • 2014-02-23
      • 2020-03-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多