如果我使用 GCC 13.2.0 使用
-std=c17 -Wall -Wextra -Wpedantic
编译以下代码,尽管没有在与 void*
格式说明符对应的参数中使用 "%p"
,但我不会收到任何警告。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main()
{
const char cstr[] = "ABC";
size_t size = sizeof cstr;
const uint8_t ustr[] = "ABC";
const int8_t sstr[] = "ABC";
const char* pcstr = cstr;
const uint8_t* pustr = ustr;
const int8_t* psstr = sstr;
printf("cstr ptr: %p\n", cstr);
printf("size ptr: %p\n", (void*)&size); // we need cast to prevent Wformat
printf("&cstr ptr: %p\n", (void*)&cstr); // we also need this cast
printf("pcstr: %p\n", pcstr);
printf("ustr ptr: %p\n", ustr);
printf("pustr: %p\n", pustr);
printf("sstr ptr: %p\n", sstr);
printf("psstr: %p\n", psstr);
return 0;
}
阅读完问答后*什么是警告以及如何解决警告:格式'%p'期望参数类型为'void *',但是参数2在打印时具有类型'int'[-Wformat=],我应该在这里期待未定义的行为吗?我尝试了几次搜索,但您会明白对于这样一个特定的组合来说,这些搜索有多困难。
是否可能是void*
和
char-ish*
共享某些属性I 缺失,或者这只是 GCC 中 缺失警告 的情况?希望这里有人能够阐明这个问题。
void *
需要与指向字符类型的指针具有相同的表示形式,即
char
、
signed char
和
unsigned char
,或任何type 是其中之一的 typedef。这是由
C 标准第 6.2.5p28 节规定的:
指向 void 的指针应具有相同的表示和对齐方式 要求作为指向字符类型的指针。这部分是由于 C 语言的历史早于48)
相同的表示和对齐要求旨在 暗示作为函数参数的可互换性,返回值 职能和工会成员。
void *
存在并且
char *
被用作通用指针类型。但是,第 7.21.6.1p9 节有关
fprintf
功能的规定:
如果转换规范无效,则行为为 不明确的。如果任何参数的类型不正确 相应的转换规范,行为未定义。因此,虽然这实际上似乎是标准中未定义的行为,但 GCC(根据其发出的警告的性质)似乎允许在需要
char *
作为扩展的地方使用
void *
由于要求两种类型具有相同的表示。所以我得出的结论是,这样的构造在 GCC 中是安全的,尽管在其他编译器上不一定。例如,可以想象,某些编译器可能会使用一种调用约定,将
void *
传递给函数,而不是传递
char *
,但考虑到表示要求,这似乎又是一种延伸。事实上,第 7.16.1.1p2 节有关
va_arg
宏的说明如下:
...如果没有实际的下一个参数,或者类型不兼容 与实际下一个参数的类型(根据 默认参数促销),行为未定义,除了 对于以下情况:因此,假设
一种类型是有符号整数类型,另一种类型是相应的无符号整数类型,值可以表示为 两种类型;
- 一种类型是指向 void 的指针,另一种是指向字符类型的指针。
fprintf
/
printf
使用
va_arg
读取可变参数,行为由上述定义,但当然这是对
printf
函数族的行为做出假设。
在我看来,这是标准中的一个缺陷,应该解决该缺陷,以便明确定义此类转换。