功能的可用性?

Kaw*_*iKx 0 c function

我只是注意到一个函数,它在C中作为参数传递,在主函数中不再可用于直接调用.以下代码的编译会产生错误

"../main.c:22:8: error: called object ‘hello’ is not a function or function pointer"
Run Code Online (Sandbox Code Playgroud)

码:

#include<stdio.h>

void hello(void)
{
    printf("hello there\n");
}

void main(hello)
{
    int y = 100;
    printf("%i",y);

    if (y==100)
        hello();
}
Run Code Online (Sandbox Code Playgroud)

是这样的,还是我做错了什么?

编辑:我将主调用更改为 - 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;
}
Run Code Online (Sandbox Code Playgroud)

这里如果你注意到调用glutDisplayFunc(显示)APPARENTLY将显示函数作为参数传递.这是怎么回事?

Jon*_*ler 7

你已经成功地模糊了一些事情.

void main(hello)声明main(错误地)返回任何内容并采用类型int(隐式int)的单个参数.你不能打电话给int; 因此,您的局部变量隐藏了同名的外部函数.C89和预标准C允许"隐含int"符号; 它在C99或C11中是正式不允许的,尽管许多编译器会继续允许它,除非你要求它们发出警告.

您应该声明main()为:

  • int main(void)
  • int main(int argc, char **argv)

void main()可悲的是,Windows确实允许.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))
Run Code Online (Sandbox Code Playgroud)

仍然不符合可接受的模式之一.

此外,启动代码不会将指向函数的指针传递给您main(); 它基本上会像你写的int main(int argc, char **argv)(或可能的int main(int argc, char **argv, char **envp))一样调用你的代码.保证不会将指针传递给您的指针main().

所以,无论发生什么,你都不会成功.通常,argc(可能是其中一部分argv)的值将被视为指向函数的指针(即使它不是).一切都会破裂.

简单定义:

int main(void)
Run Code Online (Sandbox Code Playgroud)

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
$
Run Code Online (Sandbox Code Playgroud)

传递指向函数的指针 - OpenGL代码

您可以将指向函数的指针作为参数传递给任何需要此类参数的函数.但是,main()您无法修改的功能之一(标准C库中的功能也应被视为"无法更改";同样适用于您使用的平台的操作系统接口).

在你的OpenGL示例代码,这是完全合法的传递函数指针像displayreshapeglutDisplayFunc()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;
}
Run Code Online (Sandbox Code Playgroud)

任何人都不会有任何抗议 - 编译器或人类.这是干净的(C99或C11)代码,有助于理解函数指针.如果你在void invoke(void function(void))没有显式指针的情况下编写,编译器会自动将类型转换为'指向函数的指针,该函数不带参数并且不返回值 - 也就是说void (*function)(void).因此,如果您愿意,可以编写它,但我更喜欢显式指针表示法,尤其是因为在函数内部或作为全局变量,您必须使用显式指针表示法,因此使用函数参数是一致的.在函数内部,写作:

void function(void);
Run Code Online (Sandbox Code Playgroud)

声明一个函数,而不是一个指向函数变量的指针; 必须写的:

void (*function)(void) = hello;  // Note no parentheses on hello!
Run Code Online (Sandbox Code Playgroud)

与文件范围的变量类似.因此,void (*function)(void)在函数参数列表中使用符号(在原型和定义中)与语言的其余部分一致,因此是更好的符号.

  • @LưuVĩnhPhúc:是的,确切地说,正如我在段落中提到的那样开始_此外,启动代码不会传递指向你的函数的指针...... _ (2认同)