tags:

views:

543

answers:

5
+6  Q: 

A few C questions

A friend of mine was trying to test me on C (my strongest language is C++) and he asked me three questions which I could not answer:

Try to explain the following declarations:

1) int (*x)(int, char *, void *);
2) int (*x[10])(int, char *, void *);
3) int (**x[10])(int, char *, void *);

Can anyone explain these function declarations and explain what concepts are being used?

+7  A: 
  1. A function pointer
  2. An array of function pointers
  3. An array of pointers to function pointers
tensaix2j
Actually, the second one is an array of functions, not function pointers, and is a syntax error according to GCC: `<stdin>:1: error: declaration of ‘x’ as array of functions`
Chris Lutz
+21  A: 

Well, the first one is a pointer to a function. In other words, it declares a variable "x" which points to a function of the following type:

int function(int, char*, void*);

And could be used as follows:

int myfunc(int a, char* b, void* c) {
    return a;
}

void acallingfunction() {
    int (*x)(int, char*, void*);
    x = myfunc;
    x(1, "hello", 0);
}

The second appears to be invalid syntax, but I may be wrong. If it had an asterisk before the x (such as int (*x[10])(int, char*, void*) ), it would be an array of function pointers, and would be used like a normal array:

x[3](1, "Hi there", 0);

The third is an array of pointers to function pointers, which doesn't seem practical, but is perfectly valid. An example usage might be:

void anothercaller() {
    int (*x)(int, char*, void*);
    int (**y)(int, char*, void*);
    x = myfunc;
    y = &x;
    (*y)(1, "hello", 0);
}

Note that of these, the first two are relatively common. Pointers to functions are used to accomplish callbacks and various Object-Oriented programming concepts in C. An array of pointers to functions might be used for an event table, to find the appropriate callback.

Note that all of those are, in fact, valid C++ as well. ;)

Edit: I committed the atrocities of void main() apparently.

Edit 2: As Chris Lutz points out below, they really should be wrapped in typedefs. Typedefs make code containing pointers to functions MUCH clearer.

Walt W
Chris Lutz
i was going to say the same thing as Chris. Other than that, excellent answer!
nmuntz
I think I might need that lecture. Why's it so offensive? It's invalid C to not have my code in a function!
Walt W
main returns int, not void
1800 INFORMATION
yeah. . . I know that. But it always seems to compile fine for me as void. Must be the evil compiler I'm using.
Walt W
`main` must, _must_, **must** return `int`. It _cannot_ in standard, ANSI C have a return type other than `int`, it never could, and any compiler that accepts `void main()` is wrong. See http://en.wikipedia.org/wiki/Main_function_%28programming%29#C_and_C.2B.2B and http://c-faq.com/ansi/maindecl.html
Chris Lutz
"Must be the evil compiler I'm using." Many compilers will accept it, as it became a common practice somewhere along the line because people got tired of `return`ing at the end or used OSes where the return value didn't matter. My version of GCC complains about `void main()` even when all warnings are off.
Chris Lutz
Looks like the regex that SO uses to match `code` in comments is off today...
Chris Lutz
@Chris: it may be wrong, but it's not uncommon in embedded software. Some embedded compilers don't even require there to be a main function at all!
Steve Melnikoff
@Chris Lutz, the versions using `int` are the least required versions. It's *not* wrong to have a version of main returning `void` if the compiler supports and documents this. The program is still conforming.
Johannes Schaub - litb
Excellent, concise answer.
Andrew Coleson
@litb - Okay, that sounds familiar, which means it's probably correct. I remember the standard allowing more arguments than just `int, char **` if the compiler wanted, but I had thought that a return type of `int` was more or less required. And it turns out it is, if you want cross-compiler portability.
Chris Lutz
@Christ, yeah - i just want to say that compilers that accept it are not "wrong". However surely it's not good to have it return `void` if you could aswell make it return `int`, i agree.
Johannes Schaub - litb
@Chris: "Many compilers will accept it, as it became a common practice somewhere along the line because people got tired of return'ing at the end"; that's no valid reason: the standard allows to omit the `return` in `main()` - reaching the end of the function will implicitly return `0`. Changing the return type of `main()` to `void` means you actually have to type one character more!
Christoph
+6  A: 

They are function pointers, as said above, but written rather obnoxiously (in my opinion). The way I would write them is:

typedef int (*funcptr)(int, char *, void *);

funcptr x;
funcptr x[10];
funcptr *x;

See Walt W's excellent answer for more about function pointers.

Chris Lutz
+28  A: 

You need the cdecl program, which will give you a definite, correct answer to such questions. Learning to interpret such statements manually is doable and beneficial, but even so cdecl is extremely useful for checking if you have an correct answer.

prompt>cdecl
Type `help' or `?' for help
cdecl> explain int (*x)(int, char *, void *);
declare x as pointer to function (int, pointer to char, pointer to void) returning int
cdecl> explain int (*x[10])(int, char *, void *);
declare x as array 10 of pointer to function (int, pointer to char, pointer to void) returning int
cdecl> explain int (**x[10])(int, char *, void *);
declare x as array 10 of pointer to pointer to function (int, pointer to char, pointer to void) returning int
cdecl>
hlovdal
this is incredible! i had no idea about this utility. thank you!
nmuntz
+1, beat me to it.
Kristo
+1, beat me as well ;)
D.Shawley
+3  A: 

Since the syntax of C is like the one of C++ in this matter, geordi could be interesting to you. It is another good tool for teaching and learning those declarations (and other things related to C++ and sometimes, C too).

geordi: << ETYPE_DESC(x); int (*x)(int, char *, void *);
lvalue pointer to a function taking an integer, a pointer to a character, a pointer to anything, and returning an integer

geordi: << ETYPE_DESC(x); int (*x[10])(int, char *, void *);
lvalue array of 10 pointers to functions taking an integer, a pointer to a character, a pointer to anything, and returning integers

geordi: << ETYPE_DESC(x); int (**x[10])(int, char *, void *);
lvalue array of 10 pointers to pointers to functions taking an integer, a pointer to a character, a pointer to anything, and returning integers

As its page explains, it can do much more, including building a type for you

geordi: make type array of 10 pointers to functions taking an integer and returning void
void(*[10])(int)

If you know in principle how to declare things, but are unsure about just one thing, you may use parentheses:

geordi: make type ( void(int, bool) )*
void(*)(int, bool)

If you want to see how this looks like with an identifier in it, you may change the type of names, too

geordi: -c int x;
Success
geordi: make x a ( void(int, bool) )* and show
-c void (* x)(int, bool) ;

If you build up a declaration, but you are unsure about the precedence of the operators, then geordi's precedence functions can help you out

geordi: precedence *x(a, b)
*(x(a, b))
Johannes Schaub - litb