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

tts*_*ras 9 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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

$ gcc -c -Wall -Wextra -Wcast-qual constqual.c
Run Code Online (Sandbox Code Playgroud)

...不会抱怨丢失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);
           ^
Run Code Online (Sandbox Code Playgroud)

我看不出const这里有多大意义.

PS请注意,正如预期的那样,volatile在前面添加可以uint32_t *p解决问题.我的问题是为什么GCC建议const而不是volatile.

tts*_*ras 3

好吧,我在 GCC 的 Bugzilla 中就此提出了一个问题……约瑟夫·迈尔斯 (Joseph Myers) 给出了简洁的答案:

不,GCC 并不困惑。uint32_t **它表示转换为是类型安全的 volatile uint32_t *const *,但转换为 则不是 volatile uint32_t *

...他还添加了对C FAQ 的这一部分的引用。

我必须承认,我对此的第一反应是“说什么?” 。我快速测试了该建议,更改了代码以使其使用建议的声明(和转换)......

#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

$ 
Run Code Online (Sandbox Code Playgroud)

...事实上,不再有警告了。

所以我继续阅读了相关的常见问题解答 - 我想我对正在发生的事情有了更多的了解。通过添加const修饰符,我们传递的参数是(从右到左读取,就像我们在这种 C 语法中应该做的那样)

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

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

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

非常感谢约瑟夫 - 我将把这个问题留出几周时间,以防其他人想要详细说明。

PS 添加一个注释:从现在开始,当有人声称 C 是一种简单语言时,我想我会在这里指出他们。