演员中的中间指针必须是" const限定" - 为什么?

时间:2018-02-01 10:41:36

标签: c gcc const volatile

在以下代码中......

#include <stdlib.h>
#include <stdint.h>

extern void get_buffer_from_HW_driver(volatile uint32_t **p);

void getBuffer(volatile uint32_t **pp)
{
    // Write an address into pp, that is obtained from a driver
    // The underlying HW will be DMA-ing into this address,
    // so the data pointed-to by the pointer returned by this
    // call are volatile.
    get_buffer_from_HW_driver(pp);
}

void work()
{
    uint32_t *p = NULL;
    getBuffer((volatile uint32_t **)&p);
}

...编译器正确地检测到pwork指向的数据的任何潜在访问都是危险的访问。按原样,代码指示编译器安全地发出代码,以优化对*p的重复读访问 - 这确实是错误的。

但奇怪的是,通过编译此代码发出警告......

$ gcc -c -Wall -Wextra -Wcast-qual constqual.c

...并没有抱怨volatile的丢失 - 而是建议使用const

constqual.c: In function ‘work’:
constqual.c:20:15: warning: to be safe all intermediate pointers in cast from 
                   ‘uint32_t ** {aka unsigned int **}’ to ‘volatile uint32_t ** 
                   {aka volatile unsigned int **}’ must be ‘const’ qualified
                   [-Wcast-qual]
 getBuffer((volatile uint32_t **)&p);
           ^

我看不出const在这里有什么意义。

P.S。请注意,按预期在volatile前面添加uint32_t *p可解决此问题。我的问题是为什么GCC建议使用const而不是volatile

1 个答案:

答案 0 :(得分:3)

嗯,I raised a ticket in GCC's Bugzilla关于这一点......约瑟夫迈尔斯回答了一个简洁的回答:

  

不,海湾合作委员会并不困惑。它说它转换是类型安全的   uint32_t **volatile uint32_t *const *,但不将其转换为   volatile uint32_t *

......他还添加了对this part of the C FAQ的引用。

我必须承认,我对此的第一反应是&#34;说什么?&#34; 。我很快测试了这个建议,改变了代码,使其使用了建议的声明(和强制转换)......

#include <stdlib.h>
#include <stdint.h>

extern void get_buffer_from_HW_driver(volatile uint32_t * const *p);
void getBuffer(volatile uint32_t * const *pp)
{
    // Write an address into pp, that is obtained from a driver
    // The underlying HW will be DMA-ing into this address,
    // so the data pointed-to by the pointer returned by this
    // call are volatile.
    get_buffer_from_HW_driver(pp);
}

void work()
{
    uint32_t *p = NULL;
    getBuffer((volatile uint32_t * const *)&p);
}

$ gcc -c -Wall -Wextra -Wcast-qual constqual.c

$ 

......事实上,再也没有警告了。

所以我继续阅读相关的常见问题解答 - 我想我更了解正在发生的事情。通过添加const修饰符,我们传递的参数是(从右到左阅读,因为我们应该在这种C语法中执行此操作)

  

指向指向易失性数据的常量指针(永不改变)的指针

这确实很好地映射到了这里发生的事情:我得到一个指向易失性数据的指针,这是一个驱动程序提供的缓冲区 - 即我确实不允许更改 ,因为它来自驱动程序自己分配的预先分配的缓冲区列表。修改get_buffer_from_HW_driver返回的指针是没有意义的;它不是我的修改,我只能按原样使用它。

我承认我真的很惊讶C&C的类型系统(通过-Wcast-qual的非常强大的静态分析检查进行了扩充)实际上可以帮助保证这些语义。

非常感谢约瑟夫 - 我将问题保持开放几周,以防其他人想要详细说明。

P.S。添加一个心理记录:从现在开始,当有人声称C是一种简单的语言时,我想我会将它们指向这里。