为什么在将地址在指针中的内存中打印之前,我们必须将void指针强制转换为int或其他内容?
我想知道为什么有必要在打印内存中的地址内容之前将void
指针强制转换为int *
或char *
,即使我们告诉printf()
函数如何解释内存中的数据?
假设我们有以下代码:
int main (void) { void* c = malloc(4); printf("%d",*c); return 0; }
为什么不可能这样做?
所以要明确我的问题是,这是不可能的原因是什么?
编辑:在所有答案和研究之后,我仍然不确定编辑中的致命错误究竟在哪里。 我感到困惑的主要原因是我的编译器(gcc)在告诉“取消引用void指针”时只给出一个警告。 这是实际的错误吗? 据我所知,即使有警告,该程序仍应编译。
EDIT2我仍然感到困惑的是,我们有一个ERROR和WARNING的原因似乎是完全独立的,但是由同一段代码生成:
pointer.c:7:13: warning: dereferencing 'void *' pointer printf("%d",*p); ^~ pointer.c:7:13: error: invalid use of void expression printf("%d",*p);
有些用户说只有在我们尝试使用derefenciation的结果时才会出现错误,并且警告是在我们为VOID指针实际分配内存时。
显然不是这种情况,因为如果我们删除printf行,我们确实只会得到一个警告但是完全没有一个警告。
pointer.c:6:8: warning: unused variable 'p' [-Wunused-variable] void * p=malloc(4);
问题不在于printf()
的解释,而是取消引用。
在类似声明的情况下
printf("%d",*c);
你试图取消引用void *
。 现在, void
是一个永远不完整的类型。 你不能取消引用一个指向“不完整的东西”的指针,并因此得到一些有意义的东西。
引用C11
,章节§6.2.5,P19
void类型包含一组空值; 它是一个不完整的对象类型,无法完成。
换句话说(虽然我自己有点),C是一种强类型语言,对于每种定义的类型,编译器知道( 或必须知道 )它需要读取 /操作的内存中有多少字节(大小)上。 对于void
类型,编译器是无能为力的。
出于同样的原因, void *
s上也不允许使用指针算法。 gcc有一个扩展,它处理 void*
与char *
相同,但这是一个扩展,而不是标准。
编辑:
我的编译器为语句生成错误, 在线检查 。
*c
取消引用指针,其类型为void
(不完整类型)。 你不能取消引用void
,因为编译器不知道类型(因此它的大小)。 因此,取消引用void指针会调用Undefined Behavior。
换句话说,取消引用void指针是不合理的。 想象一下,你是编译器,你将如何插入指针指向的内存(因为它可以是任何东西)? 将void*
转换为正确的类型将起到作用(向编译器提供nessecairy信息)。
为什么编译器只发出警告而不是错误?
确实如此,请注意它会生成警告和伴随错误:
main.c:8:14: warning: dereferencing 'void *' pointer printf("%d",*c); ^~ main.c:8:14: error: invalid use of void expression printf("%d",*c); ^~~~~~
(gcc)在讲述“取消引用void指针”时给出了一个警告。 这是实际的错误吗?
没有。
但是试图使用这种去除reflection的结果,你运气不好。
我的意思是:
void* c = malloc(4); *c;
不会导致错误(但只是警告):
main.c:6:2: warning: dereferencing 'void *' pointer *c; ^~
但是,如果您尝试使用*c
的结果,则会生成错误。
在了解无效指针的过程中阅读更多相关内容。
由于c
是一个void pointer
它可以是任何东西。 要取消引用,编译器需要知道类型,包括大小。
没有malloc
和void *
,请考虑这里发生的事情:
int main() { int i = 555555; char * c = (char *)&i; printf("%c\n", *c); short * s = (short *)&i; printf("%d\n", *s); }
你会得到非常不同的东西。 void *
正在进行 – 它应该是什么?
你告诉printf
期望一个数字,但这是一个单独的操作,首先解除void *
。
c
指针已声明为void*
。 在访问/解除引用 void
指针之前,您必须键入强制转换 ,以便它指向您在此指针中期望的相关类型。
总而言之,正如之前的答案所述, void
在所有C标准中都是不完整的类型,因此每当您尝试取消引用它时,您肯定会收到错误。 但是,GNU C处理大小为1(char)的void,因此它只显示警告但GCC根据GNU扩展进行编译。
如果您希望GCC严格遵循标准,您必须添加-std=c11
标志(可能使用-pedantic
)。
printf
没有指向要打印的东西,主要是因为它可以接受表达式的结果:
printf("%d",4*my_function()); // value has no user-visible address
因此,编译器必须知道类型才能获取值并将其提供给printf
。 (你也需要格式字符串,因为语言提供了printf
的实现,无法发现你给它的对象类型。)