我刚注意到一个函数,它在C中作为参数传递,在主函数中不再可用于直接调用。下面代码的编译产生错误
"../main.c:22:8: error: called object ‘hello’ is not a function or function pointer"
代码:
#include<stdio.h>
void hello(void)
{
printf("hello there\n");
}
void main(hello)
{
int y = 100;
printf("%i",y);
if (y==100)
hello();
}
是这样的,还是我做错了什么?
编辑:我将主调用更改为 - void main(void hello (void))
现在编译没有错误,但在IF语句执行printf语句后仍然调用hello函数。
编辑NO 2:在阅读了很多答案后,我了解到我们可以将指向函数的指针作为另一个函数的参数传递。将整个函数作为参数传递是没有意义的。实际上,整个混乱来自以下opengl C代码,它显然在Eclipse IDE中成功运行:
#include <GL/glut.h>
#include<GL/gl.h>// Header File For The GLUT Library
void init(void)
{
glClearColor (0.0,0.0,0.4,0.0);
glShadeModel(GL_FLAT);
}
void reshape (int w, int h)
{
glViewport(0,0, (GLsizei) w, (GLsizei)h); // indicates the shape of the available screen area into which the scene is mapped
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-10,10,-10,10,-10,10);
}
void display (void)
{
int i,j;
while(1){
for (i=-10; i <=10; i++){
glClearColor (0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0, 0.0, 1.0);
glBegin(GL_TRIANGLES);
glVertex3f(i,2,5);
glVertex3f(6,-i,-5);
glVertex3f(1,9,-1);
glEnd();
glFlush();}
}
}
int main (int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
glutInitWindowPosition(100,100);
glutInitWindowSize(500,500);
glutCreateWindow (argv[0]);
init ();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMainLoop ();
return 0;
}
如果您注意到调用glutDisplayFunc(显示)APPARENTLY将显示函数作为参数传递。这是怎么回事?
答案 0 :(得分:7)
你已经成功地模糊了事情。
void main(hello)
声明main
(错误地)为什么都不返回并且采用类型为int
的单个参数(隐式int
)。你不能打电话给int
;因此,您的局部变量隐藏了同名的外部函数。 C89和预标准C允许'隐式int
'表示法;它在C99或C11中是正式不允许的,尽管许多编译器会继续允许它,除非你要求它们发出警告。
您应该将main()
声明为:
int main(void)
int main(int argc, char **argv)
Windows遗憾地允许void main()
。 C ++没有。
如果您使用GCC,请使用-Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Wold-style-declaration
等选项进行编译(最好使用-Werror
)。并非所有版本的GCC都支持所有这些选项;使用您的版本支持的那些。
顺便说一下,通过将hello
声明为main()
的参数,您隐藏或隐藏了hello()
函数的外部定义。这是标准的C行为;在本地范围定义的名称会隐藏具有在外部作用域级别定义的相同名称的不同对象。您可以使用-Wshadow
选项向GCC发现当您遇到此问题时。
正如我已经问到的那样,为什么你认为启动环境会将指向函数的指针传递给你的main()
?
功能定义:
void main(void hello(void))
仍然与其中一种可接受的模式不匹配。
此外,启动代码不会将指向函数的指针传递给main()
;它基本上会调用您的代码,就像您编写int main(int argc, char **argv)
(或可能是int main(int argc, char **argv, char **envp)
)一样。保证不会将指针传递给您的main()
。
所以,无论发生什么,你都不会成功。通常,argc
(可能是argv
的一部分)的值将被视为指向函数的指针(即使它不是)。一切都会破裂。
只需定义:
int main(void)
并直接在hello()
的正文中致电main()
。这就是所有有意义的事情。
为了记录,这就是GCC对问题中的代码所说的内容(修改后):
$ gcc -O3 -g -I/Users/jleffler/inc -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror -Wshadow -c hello.c
hello.c:3:6: error: no previous prototype for ‘hello’ [-Werror=missing-prototypes]
void hello(void)
^
hello.c:8:6: error: return type of ‘main’ is not ‘int’ [-Werror=main]
void main(void hello (void))
^
hello.c:8:6: error: first argument of ‘main’ should be ‘int’ [-Werror=main]
hello.c:8:6: error: ‘main’ takes only zero or two arguments [-Werror=main]
hello.c: In function ‘main’:
hello.c:8:16: error: declaration of ‘hello’ shadows a global declaration [-Werror=shadow]
void main(void hello (void))
^
hello.c:3:6: error: shadowed declaration is here [-Werror=shadow]
void hello(void)
^
cc1: all warnings being treated as errors
$
您可以将指向函数的指针作为参数传递给任何需要此类参数的函数。但是,main()
是您无法修改的功能之一(标准C库中的函数也应被视为“无法更改”;同样适用于您使用的平台的操作系统接口)。
在您的OpenGL示例代码中,将display
或reshape
等函数指针传递给glutDisplayFunc()
和glutReshapeFunc()
函数是完全合法的,因为它们旨在接受指向函数的指针。如果你把你的首次提议写成:
#include <stdio.h>
static void hello(void)
{
printf("hello there\n");
}
static void invoke(void (*function)(void))
{
printf("-->> %s:\n", __func__);
function(); // Or (*function)();
printf("<<-- %s\n", __func__);
}
int main(void)
{
int y = 100;
printf("%i\n", y);
if (y == 100)
invoke(hello);
return 0;
}
任何人都不会有任何抗议 - 编译器或人类。这是干净的(C99或C11)代码,有助于理解函数指针。如果你在没有显式指针的情况下编写void invoke(void function(void))
,编译器会自动将类型转换为'指向函数的指针,该函数不带参数并且不返回任何值 - 也就是void (*function)(void)
。因此,如果您愿意,可以编写它,但我更喜欢显式指针表示法,尤其是因为在函数内部或作为全局变量,您必须使用显式指针表示法,因此使用函数参数是一致的。在函数内部,写作:
void function(void);
声明一个函数,而不是一个指向函数变量的指针;必须写的:
void (*function)(void) = hello; // Note no parentheses on hello!
与文件范围的变量类似。因此,在函数参数列表中使用void (*function)(void)
表示法(在原型和定义中)与语言的其余部分一致,因此是更好的符号。
答案 1 :(得分:4)
参数hello
会影响全局hello
,因此hello
中main
的所有外观都是指参数,而不是函数。因为参数的类型为int
(因为您没有指定类型,而int
是默认值),int
不是函数(或函数指针) ),那是一种类型错误。
答案 2 :(得分:1)
main(hello)
错了
使用
main()