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.
精彩评论