我从 C编程,现代方法的第19章编写项目4。我的代码有效,但我收到此警告,试图将void *
参数返回到printf
并使用转换说明符%s
传递函数。
格式
%s
需要char *
类型的参数,但参数2的类型为void * [-Wformat=]
我可以通过将函数的返回类型强制转换为char *
来轻松删除警告,例如
printf("%s\n", (char *) function(param));
但我只想知道为什么这是必要的,因为类型void *
会自动转换为另一种指针类型。
答案 0 :(得分:5)
编译器在这种情况下抱怨是非常正确的。
根据你的逻辑本身,返回void *
的函数可能会返回一个结构指针,该结构指针被转换为void *
,但是,%s
将无法打印出来,而不是它?
所以,如果你知道你正在做什么,你可以施放结果,对于这种情况。
另外,正如其他人所指出的,也许值得一提的是,这个警告与标准规范无关,就像在标准中一样,对参数的类型没有限制。 (借用Mr. @WhozCraig 的话)这个警告基本上是由于编译器完全由它自己执行的一个额外的类型检查层,由{{1}中的-Wformat
标志启用}。
答案 1 :(得分:4)
就纯语言而言(不是标准库及其期望,实际的形式语言),你可以在论据列表中推送任何你想要的东西(包括与之相关的完全不连贯的东西)某些库例程的%s
格式说明符的要求)。当然,除非你最终推送的是什么,实际上,char
,printf
本身的nullchar终止序列的地址将在你的命令下陷入未定义的行为。
您收到的警告是基于编译器中额外的API检查层,而不是某些语言本身的违规行为。 api检查是将格式规范与经常使用的标准库api(例如printf
,scanf
等等的呈现参数的类型进行匹配。该警告检查的作者是否可以更宽容和忽略期望指针类型的规范的void*
个参数?当然,但检查功能的重点是在这种情况下会迅速减少。考虑一下:
int a = 0;
void *b = &a;
printf("%s\n", b);
如果这个api-check功能值得任何盐,那么更好咆哮这个不匹配的类型,因为就语言本身而言,什么都没有此代码有误。并且没有与我刚才要求它做的邪恶有关。就语言而言,printf
就是这样:
int printf(char *format ...);
我设置的调用肯定满足了(对我不好,谢天谢地,我的现代编译器的api-checks会让我很快知道可能存在问题)。
答案 2 :(得分:2)
指针是指向单个内存位置的变量。
指针指向的字节数取决于指针的类型。因此,如果它是int *则将其解释为4个字节,如果它是char *,则将其解释为1个字节。
void *没有类型。因此编译器无法取消引用此指针。因此,为了让编译器理解要解除引用的内存,我们需要在这里进行类型转换。
答案 3 :(得分:1)
printf
函数声明为:
int printf(char *format ...);
此处...
表示调用者提供的任何其他参数(即您要打印的字符串)。当printf
检查这些附加参数时,它会使用一些没有类型安全性的低级代码。
此代码无法确定参数的类型为void*
,并自动将其强制转换为char*
。相反,如果void*
和char*
的二进制表示相同,则可以从...
部分提取参数,而不考虑其实际类型。如果表示不同,低级代码将尝试打印不正确的值(可能会崩溃)。
void*
和char*
的代表对我所知道的所有平台都是一样的,所以它可能是安全的(如果你信任我,那就是 - 请不要这样做!)。但是,如果您使用gcc -Wall
进行编译,正如某些人所建议的那样,所有警告都会升级为错误,因此您应该按照编译器指示进行转换。