0

I just noticed that a function, which is passed as an argument in C, is no more available in the main function for direct calling. the compilation of the code below produces the error

"../main.c:22:8: error: called object ‘hello’ is not a function or function pointer"

Code:

#include<stdio.h>

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

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

    if (y==100)
        hello();
}

is it the case or I did something wrong?

EDIT: I changed the main call to - void main(void hello (void)) Now there is no error in compilation but still the call to hello function after IF statement doesn't execute printf statement.

EDIT NO 2 : After reading many answer, I learnt that we can pass a pointer to a function as an argument to another function. passing whole function as argument is meaningless. Actually, this whole confusion arose out of the following opengl C code, which apparently ran successful in 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;
}

Here if you notice the call glutDisplayFunc(display) APPARENTLY passes the display function as an argument.How this is happening?

KawaiKx
  • 9,558
  • 19
  • 72
  • 111

3 Answers3

7

You've managed to blur things horribly.

void main(hello) declares main (incorrectly) as returning nothing and taking a single argument of type int (implicit int). You can't call an int; hence your local variable hides the external function of the same name. The C89 and pre-standard C allowed the 'implicit int' notation; it is officially not allowed in C99 or C11, though many compilers will continue to allow it unless you ask them for warnings.

You should declare main() as either:

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

Windows does allow void main(), sadly. C++ does not.

If you use GCC, compile with options like -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Wold-style-declaration (preferably with -Werror). Not all versions of GCC support all those options; use those that your version does support.

Incidentally, by declaring hello as an argument to main(), you are hiding or shadowing the external definition of the hello() function. This is standard C behaviour; a name defined at local scope hides different objects with the same name defined at outer scope levels. You can use the -Wshadow option to GCC to spot when you run into this as a problem.


Post-Edit

As I already asked, why on earth do you think the startup environment is going to pass a pointer-to-function to your main()?

The function definition:

void main(void hello(void))

still does not match one of the acceptable patterns.

Further, the startup code won't pass a pointer to function to your main(); it will basically call your code as if you'd written int main(int argc, char **argv) (or possibly int main(int argc, char **argv, char **envp)). You are guaranteed not to get a pointer-to-function passed to your main().

So, whatever else happens, you will not be successful. Typically, the value of argc (and perhaps part of argv) will be treated as a pointer to function (even though it isn't). All hell will break loose.

Simply define:

int main(void)

and call hello() directly in the body of main(). That's about all that makes sense.


For the record, this is what GCC says about the code in the question (as amended):

$ 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
$

Passing pointers to functions — the OpenGL code

You can pass a pointer-to-function as an argument to any function that expects such an argument. However, main() is one of the functions you can't tinker with (the functions in the Standard C library should be treated as 'cannot be changed' either; ditto the operating system interfaces for the platform(s) you use).

In your OpenGL example code, it is perfectly legitimate to pass a function pointer like display or reshape to the glutDisplayFunc() and glutReshapeFunc() functions because they are designed to accept pointers to functions. Had you written your initial offering as:

#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;
}

there would have been no outcry from anyone — compiler or human. This is clean (C99 or C11) code that helps in understanding pointers to functions. If you write void invoke(void function(void)) without the explicit pointer, the compiler automatically converts the type to 'pointer to function taking no arguments and returning no value — aka void (*function)(void). So, you can write that if you prefer, though I prefer the explicit pointer notation, not least because inside a function, or as a global variable, you must use the explicit pointer notation, so using that with function arguments is consistent. Inside a function, writing:

void function(void);

declares a function, not a pointer to function variable; that must be written:

void (*function)(void) = hello;  // Note no parentheses on hello!

Similarly at with variables at file scope. So, using the void (*function)(void) notation in function argument lists (in prototypes and definitions) is consistent with the rest of the language and is therefore the better notation to use.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    whatever you write your main's declaration, the operating system's startup code still pass to main the parameters as `int main(int argc, char **argv)`. How can it know what function your program have and want to call? You'll end up using the parameter as a wrong type and can never run the program – phuclv Apr 11 '14 at 02:18
  • 2
    @LưuVĩnhPhúc: yes, precisely, as I mentioned in my paragraph starting _Further, the startup code won't pass a pointer to your function…_ – Jonathan Leffler Apr 11 '14 at 02:20
4

The parameter hello shadows the global hello, so all appearances of hello within main refer to the parameter, not the function. And since the parameter is of type int (because you didn't specify a type and int is the default) and ints aren't functions (or function pointers), that's a type error.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • alright. I changed the main call to - void main(void hello (void)) still the call to hello function after IF statement doesn't execute printf statement. – KawaiKx Apr 11 '14 at 01:59
  • 1
    Why on earth do you think the startup environment is going to pass a pointer-to-function to your `main()`, @Saurabh? It won't; it will basically call your code as if you'd written `int main(int argc, char **argv)` (or possibly `int main(int argc, char **argv, char **envp)`). You are guaranteed not to get a pointer-to-function passed to your `main()`. – Jonathan Leffler Apr 11 '14 at 02:06
1
main(hello) 

is wrong

use

main()
chouaib
  • 2,763
  • 5
  • 20
  • 35
  • 1
    Unfortunately, pre-standard C and C89 both allowed you to declare parameters with no type (and assumed that the type was `int`). However, `main()` was always supposed to be defined as either `int main(void)` or `int main(int argc, char **argv)` or other variations as allowed by the implementation. It is (very, very) poor style to write such code in 2014; it was in 1999, too. But it was allowed until the C99 standard was released. Since then, strictly, it is not allowed by the current C standard. – Jonathan Leffler Apr 11 '14 at 02:01
  • @JonathanLeffler I agree with you, I just showed what was wrong with his code since this was the question – chouaib Apr 11 '14 at 02:09