开发者

C compiler complaining about void (*)

My professor defined this in the .h file

void list_map(INTLIST* list, void (*f)(void *)); /*Applies a function to each element of the list */

I wrote the function like this:

 void list_map(INTLIST* list, void (*f)(void *))
  {
   INTLIST* pTemp=NULL;

   if (list == NULL)
    {
      //list is empty
    }
   else
      {
       for(pTemp=list; pTemp->next!=NULL; pTemp=pTemp->next)
          {
             f(pTemp); //f is a function pointer we call list map from main like list_map(lst, list_sort)
          }    
      }
  }

I call it in main like this:

  list_map(aList[i], (void*)list_sort);

In windows environment, no complaints, but I have to run this in a Linux environment. I'm using a makefile to compile all of the code and I get this warning and error:

*c++ -O2 -c main.c main.c: In function ‘int main(int, char**)’: main.c:53: warning: deprecated conversion from string constant to ‘char*’ main.c:123: error: invalid conversion from ‘void ()(INTLIST)’ to ‘void ()(void)’ main.c:123: error: initializing argument 2 of ‘void list_map(INTLIST*, void ()(void))’ make: *** [main.o] Error 1*

Can somebody help with the error first and then maybe with the warning?

Edit Portion:

Someone asked for the list_sort function, here it is:

 void list_sort(INTLIST* list)
 {
  INTLIST* pTemp=NULL;
  INTLIST* pTemp2=NULL;

  pTemp=list;          //temp pointers to compare node values
  开发者_Python百科pTemp2=list;

  if (pTemp->next !=NULL)     //move to second node
   {
      pTemp2=pTemp2->next;
   }

  while(pTemp2 != NULL)
   {   
       //we implement a selection sort 
       //check if incoming node->datum with each node in the list
       //swap values if <
      if (pTemp2->datum < pTemp->datum)
         {
         //swap the values
         int temp = pTemp->datum;
         pTemp->datum = pTemp2->datum;
         pTemp2->datum = temp;
         }
         //advance the pointer
      pTemp2=pTemp2->next;
   }
 }


And if you cast your callback to the proper function type?

list_map(aList[i], (void (*)(void*))list_sort);


Well,

void list_sort(INTLIST* list)

has the wrong signature to be passed as the second argument of

void list_map(INTLIST* list, void (*f)(void *))


A simple cast of list_sort() is enough to make the warning go away, but it's not enough to make it actually work:

The C-standard doesn't guarantee that an INTLIST * and a void * have compatible representations, ie void (INTLIST *) and void (void *) are distinct, incompatible types. When list_map() invokes list_sort() through your void (*f)(void *) argument, C99 section 6.3.2.3, §8 applies:

If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined.

To make it standard compliant, you have to write a wrapper function for list_sort():

void list_sort_wrapper(void *list)
{
    list_sort(list);
}

and use that as argument to your call:

list_map(aList[i], list_sort_wrapper);

Also, if list_sort() is implemented correctly (didn't check the algorithm), it already walks the list, ie invoking it for each node doesn't make any sense at all.

edit:

Ok, list_sort() doesn't actually sort the whole list - that can be achieved via

list_map(list, list_sort_wrapper);

The naming scheme is a serious WTF - if the function doesn't sort the list, call it list_sort_step() or list_select_head() or something, but please not list_sort().


First: why are you compiling C code as C++?. Please compile it with a C compiler.

The prototype of list_sort() is:

void list_sort(INTLIST* list);

and list_map() has the prototype:

void list_map(INTLIST* list, void (*f)(void *));

This means that the second argument to list_map() is a function that takes a void * argument, and returns void (nothing).

Now, the C standard guarantees that a conversion of any object pointer to void * and back is OK, so given:

INTLIST *l;
/* make l point to a valid INTLIST */
void *pl = l;

this is OK:

list_sort(pl);

Note that, list_sort() could have been declared as:

void list_sort_generic(void *l);

In fact, since your professor is using void * in some places, he wants to extend your lists to be of a generic type at some point.

Anyway, you can pass an INTLIST * to list_sort() or list_sort_generic(), but list_sort_generic() can be passed any object pointer, whereas list_sort() can only be passed INTLIST * (or a void * which was converted from an INTLIST *).

Even though list_sort() can take a void *, the signature of list_sort() is not:

void list_sort(void *l);

So, the function types of list_sort() and list_sort_generic() are not the same, and cannot be interchanged. list_map() expects a function of the kind that list_sort_generic() is, but is getting a function of a different kind.

Since you can't change any prototypes, you need a cast. Now, void * is a generic type in C, so you would think such a cast would work. But, as I said before, only object pointers can be converted to void * and back portably—not function pointer type. So, you need to cast list_sort() to the correct type when calling list_map().

That correct type is void (*)(void *). This is a function returning void and taking a void *.

Hence, the call should be:

list_map(aList[i], (void (*)(void *))list_sort);

But, since the type of list_sort() and the type expected by list_map() for its second parameter are not the same, the cast may or may not work. Your professor has given you "not-so-nice" (i.e., wrong) prototypes. Either he should have gone all the way in declaring type-generic functions, or he should have kept everything INTLIST *. By going half-way, he has introduced a complicated cast that shouldn't have been there, and may not work. I am sure if you bring this to your professor's attention, he will admit this oversight.

Hope that helped.


In your function list_sort, the parameter is INTLIST *list.

list_map(aList[i], (void*)list_sort);

By looking at the header, the function prototype is a function pointer that has a parameter which is of type void *

void list_map(INTLIST* list, void (*f)(void *))
                                      ^^^^^^^^

The function pointer *f has to match up the signature, hence the conflict and the warning generated by your compiler. Since *f points to the list_sort, the method signature does not match up.

It would work if your function prototype has this instead

void list_map(INTLIST* list, void (*f)(INTLIST *))

Hope this helps, Best regards, Tom.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜