参数转换:(普通)指针到void指针,需要强制类型转换吗?

6

当给一个 void 指针赋值或从中赋值时,不需要进行强制类型转换(C99 §6.3.2.2 sub 1 / §6.5.16.1 sub 1)。那么当将一个(例如 int)指针传递给期望接收 void 指针的函数时,是否也是如此呢?

例如:

void foo(void * p){
    // Do something
}

int main(){
    int i = 12;
    foo(&i); // int * to void *: no cast needed?
}

使用GCC(4.8.1,MinGW-32)编译此代码时,即使启用了-Wall-pedantic标志,也不会出现错误或警告。

相比之下,在这个答案中建议对此调用进行强制类型转换(以消除-Wformat警告):

int main(){
    int i = 12;
    printf("%p\n", &i);
}

但在我的情况下,GCC没有抱怨。

那么,当将非void指针传递给期望void指针的函数时,需要转换吗?

1个回答

6

不同之处在于printf是一种变参函数,而变参函数对其尾随参数遵循不同的转换规则。

foo(&i); 

在这里不需要进行强制转换,因为foo是一个带有原型的函数。C语言表示,&i被转换为p的类型,就像被赋值一样,在C语言中,所有对象指针类型都隐式转换为void *
对于printf,情况不同,变参函数(如printf)在其余参数上有默认参数提升,并且指针类型的参数不会发生转换。
C语言关于带有原型的函数的解释:

(C99, 6.5.2.2p7) "如果表示所调用函数的表达式具有包括原型的类型,则参数将按照赋值方式进行隐式转换,以与相应参数的类型相同,其中将每个参数的类型视为其声明类型的未限定版本。"

C语言关于变参函数的解释:

(C99, 6.5.2.2p7) “带有省略号符号的函数原型声明符使得参数类型转换在最后一个声明的参数之后停止。默认参数推进适用于尾部参数。”

所以,当传递非空指针到期望空指针的函数中时,是否需要强制转换呢?
对于printfp转换说明符需要一个void *参数。如果参数是不同类型的,则函数调用会引起未定义的行为。因此,如果p的参数是对象指针类型,则需要使用(void *)强制转换。

(C99, 7.19.6.1p8) "p的参数必须是一个指向void的指针。"


1
你似乎在“C中的原型函数”和“..变参..”两个部分引用了同一句话,我猜你是指第7段的后半部分?“省略号表示法..” - Kninnug
1
你在调用可变参数函数时停止引用相关部分:“函数原型声明符中的省略符号会导致参数类型转换在最后一个声明的参数之后停止。默认参数提升将在尾随参数上执行”。并且提升仅适用于整数类型和浮点类型,而不适用于指针。 - Michael Burr
是的,我因为要离开而不小心复制了两次同样的段落。现在我不在,回来时会修复它。或者如果有人想修复我的答案,他是受欢迎的 :) - ouah
什么是尾随参数? - haccks
1
@haccks 尾随参数是与省略符号(printf原型中的 ...)相关的参数。 - ouah
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接