为什么在将地址在指针中的内存中打印之前,我们必须将void指针强制转换为int或其他内容?

时间:2017-09-15 08:08:48

标签: c pointers void void-pointers dereference

我想知道为什么在打印内存中的地址内容之前,有必要将void指针强制转换为int *char *,即使我们告诉{{1}函数如何解释内存中的数据?

假设我们有以下代码:

printf()

为什么不可能这样做?

所以要明确我的问题是这是不可能的原因是什么?

修改 在所有的答案和研究之后,我仍然不确定编辑中的致命错误究竟在哪里。我感到困惑的主要原因是我的编译器(gcc)在告诉“取消引用void指针”时只给出一个警告。这是实际的错误吗?据我所知,即使有警告,该程序仍应编译。

EDIT2 我仍然对我们有错误和警告的原因感到困惑,这些错误似乎是完全独立的,但是由同一段代码生成:

int main (void)
{
 void* c = malloc(4);
 printf("%d",*c);
 return 0;
}

有些用户说只有在我们尝试使用derefenciation的结果时才会出现错误,并且警告是在我们为VOID指针实际分配内存时。

显然不是这种情况,因为如果我们删除printf行,我们确实只会收到一个警告但是完全没有一个警告。

pointer.c:7:13: warning: dereferencing 'void *' pointer                                                                                                                                                           
 printf("%d",*p);                                                                                                                                                                                                 
             ^~                                                                                                                                                                                                   
pointer.c:7:13: error: invalid use of void expression                                                                                                                                                             
 printf("%d",*p);

6 个答案:

答案 0 :(得分:8)

问题不在于printf()的解释,而在于取消引用。

对于像

这样的陈述
 printf("%d",*c);

您正尝试取消引用void *。现在,void是一个永远不完整的类型。你不能取消引用指向“不完整的东西”并获得有意义的结果。

引用C11,章节§6.2.5,P19

  

void类型包含一组空值;这是不完整的   无法完成的对象类型。

换句话说(虽然我自己有点),C是强类型语言,对于每个定义的类型,编译器知道(或必须知道)多少字节(大小)从内存中它需要读取 /操作。对于void类型,编译器是无能为力的。

出于同样的原因,void *上也不允许使用指针算法。 gcc有一个对待void*的扩展名与char *相同,但这是一个扩展名,而不是标准扩展名。

修改

我的编译器会为语句 check online 生成错误。

答案 1 :(得分:5)

*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* c = malloc(4);
*c;

不会导致错误(但只是警告):

main.c:6:2: warning: dereferencing 'void *' pointer
  *c;
  ^~

但是,如果您尝试使用*c的结果,则会生成错误。

Towards understanding void pointers了解详情。

答案 2 :(得分:3)

由于cvoid pointer,因此它可以是任何内容。要取消引用,编译器需要知道类型,包括大小。

如果没有mallocvoid *,请考虑这里发生的事情:

int main()
{
    int i  = 555555;
    char * c = (char *)&i;
    printf("%c\n", *c);
    short * s = (short *)&i;
    printf("%d\n", *s);
}

你会得到非常不同的东西。 void *也是如此 - 这应该是什么?

您告诉printf期待一个号码,但这是一个单独的操作,首先取消void *

答案 3 :(得分:0)

c指针已声明为void*。在访问/解除引用 void指针之前,您需要必须输入强制转换,以便它指向您在此指针中期望的相关类型。

答案 4 :(得分:0)

总而言之,正如之前的答案所述,void在所有C标准中都是不完整的类型。因此,每当您尝试取消引用它时,您肯定会收到错误。但是,GNU C处理的void大小为1(char)&因此它只显示警告,但GCC根据GNU扩展进行编译。

如果您希望GCC严格遵循标准,您必须添加-std=c11标记(可能包含-pedantic)。

答案 5 :(得分:0)

printf没有将指针带到要打印的内容上,主要是因为它可以接受表达式的结果:

printf("%d",4*my_function());  // value has no user-visible address

因此,编译器必须知道类型才能获取并将其提供给printf。 (你还需要格式字符串 ,因为语言提供printf的实现无法发现你给它的对象类型。)