【问题标题】:Intermediate pointers in cast must be "const qualified" - why?强制转换中的中间指针必须是“const 限定的”——为什么?
【发布时间】:2018-02-01 10:41:36
【问题描述】:

在下面的代码中...

#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 在这里有什么意义。

附:请注意,按预期在uint32_t *p 前面添加volatile 可以解决此问题。我的问题是为什么 GCC 推荐 const 而不是 volatile

【问题讨论】:

  • 我怀疑您在编译器消息中发现了一个错误。编译器很可能根据相同的规则处理所有类型限定符,但与常用的“const-correctness”相比,“volatile-correctness”是一种罕见的野兽。所以也许在编码时,他们将const这个词“硬编码”到消息中,却没有意识到volatilerestrict_Atomic也是如此。
  • 请注意,clang 确实发出了相同的警告(措辞略有不同,但指的是const)。有没有办法找到此类警告消息的来源?
  • @Lundin 确实,看起来很有可能。我打开了a ticket in GCC's Bugzilla - 让我们看看开发人员怎么说。
  • @Lundin GCC dev 回复 - 在下面总结了我对他们回复的理解。
  • @Lundin 常量正确性要求 Foo *** 安全地转换为 Foo const * const * const *。也许令人惊讶的是,volatile-correctness 意味着Foo *** 安全地转换为Foo volatile * volatile * volatile * --- 它转换为Foo volatile * const * const *。这就是消息的意思。

标签: c gcc constants volatile


【解决方案1】:

好吧,I raised a ticket in GCC's Bugzilla 关于这个......约瑟夫迈尔斯的回答很简洁:

不,GCC 没有混淆。这是说转换是类型安全的 uint32_t **volatile uint32_t *const *,但不要将其转换为 volatile uint32_t *.

...他还添加了对this part of the 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

$ 

...事实上,没有任何警告了。

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

一个指向 constant 指针(永远不会改变)的指针,它指向 volatile 数据

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

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

非常感谢 Joseph - 我会将这个问题留几个星期,以防其他人想详细说明。

附言补充一句:从现在开始,当有人声称 C 是一门简单的语言时,我想我会在这里指出他们。

【讨论】:

  • 这里有两个问题。一是pointer-to-pointerqualified-pointer-to-pointer不兼容。好吧,这是一个常见问题解答,没有人为此争论。另一个问题是 gcc 突然引入了constconst 本身并没有什么神奇之处。这里适用的是 C11 6.3.2.372 “对于任何限定符 q,指向非 q 限定类型的指针可以转换为指向该类型的 q 限定版本的指针;存储的值在原始指针和转换后的指针中应该比较相等。”那么const 是从哪里来的呢?
  • @Lundin C11 6.3.2 谈论隐式转换。这与手头的问题无关。我们这里有演员表。 C11 允许任何指向 6.5.4/3 中的指针的指针。然而,演员表并不安全,gcc 会警告你。
猜你喜欢
  • 2011-12-16
  • 2013-10-20
  • 2015-09-10
  • 2011-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-17
  • 1970-01-01
相关资源
最近更新 更多