cast 中的中间指针必须是 "const qualified" - 为什么?
Intermediate pointers in cast must be "const qualified" - why?
在下面的代码中...
#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);
}
...编译器正确地检测到对 work
中 p
指向的数据的任何潜在访问都是危险的访问。按原样,代码指示编译器可以安全地发出优化掉对 *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。请注意,按预期在 uint32_t *p
前面添加 volatile
可解决此问题。我的问题是为什么 GCC 推荐 const
而不是 volatile
.
好吧,I raised a ticket in GCC's Bugzilla 关于这个……约瑟夫·迈尔斯 (Joseph Myers) 的回答简洁明了:
No, GCC is not confused. It's saying that it's type-safe to convert
uint32_t **
to volatile uint32_t *const *
, but not to convert it to
volatile uint32_t *
.
...他还添加了对 this part of the C FAQ 的引用。
不得不承认,我对此的第一反应是 "say what?"。我快速测试了该建议,更改代码以使其使用建议的声明(并强制转换)...
#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 语法中应该做的那样):
a pointer to a constant pointer (that will never change) that points to volatile data
这确实很好地映射了这里发生的事情:我得到一个指向易失性数据的指针,这是一个驱动程序提供的缓冲区 - 即我确实不允许更改,因为它来自驱动程序本身分配的预分配缓冲区列表。修改 get_buffer_from_HW_driver
返回的指针是没有意义的;不是我可以修改的,我只能照原样使用
我承认我真的很惊讶 C 的类型系统(增强了 -Wcast-qual 的非常强大的静态分析检查)实际上可以帮助保证这些语义。
非常感谢约瑟夫 - 我会将这个问题搁置几周,以防其他人想要详细说明。
P.S。补充一点:从现在开始,当有人声称 C 是一种简单的语言时,我想我会在这里指出他们。
在下面的代码中...
#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);
}
...编译器正确地检测到对 work
中 p
指向的数据的任何潜在访问都是危险的访问。按原样,代码指示编译器可以安全地发出优化掉对 *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。请注意,按预期在 uint32_t *p
前面添加 volatile
可解决此问题。我的问题是为什么 GCC 推荐 const
而不是 volatile
.
好吧,I raised a ticket in GCC's Bugzilla 关于这个……约瑟夫·迈尔斯 (Joseph Myers) 的回答简洁明了:
No, GCC is not confused. It's saying that it's type-safe to convert
uint32_t **
tovolatile uint32_t *const *
, but not to convert it tovolatile uint32_t *
.
...他还添加了对 this part of the C FAQ 的引用。
不得不承认,我对此的第一反应是 "say what?"。我快速测试了该建议,更改代码以使其使用建议的声明(并强制转换)...
#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 语法中应该做的那样):
a pointer to a constant pointer (that will never change) that points to volatile data
这确实很好地映射了这里发生的事情:我得到一个指向易失性数据的指针,这是一个驱动程序提供的缓冲区 - 即我确实不允许更改,因为它来自驱动程序本身分配的预分配缓冲区列表。修改 get_buffer_from_HW_driver
返回的指针是没有意义的;不是我可以修改的,我只能照原样使用
我承认我真的很惊讶 C 的类型系统(增强了 -Wcast-qual 的非常强大的静态分析检查)实际上可以帮助保证这些语义。
非常感谢约瑟夫 - 我会将这个问题搁置几周,以防其他人想要详细说明。
P.S。补充一点:从现在开始,当有人声称 C 是一种简单的语言时,我想我会在这里指出他们。