【问题标题】:C compiler complaining about void (*)C 编译器抱怨 void (*)
【发布时间】:2011-01-08 22:46:05
【问题描述】:

我的教授在 .h 文件中定义了这个

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

我这样写函数:

 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)
          }    
      }
  }

我在 main 中这样称呼它:

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

在windows环境下,没什么可抱怨的,但我必须在Linux环境下运行它。我正在使用 makefile 编译所有代码,但收到此警告和错误:

*c++ -O2 -c main.c main.c:在函数“int main(int, char**)”中: main.c:53: 警告:不推荐从字符串常量到“char*”的转换 main.c:123:错误:从“void ()(INTLIST)”到“void ()(void)”的无效转换 main.c:123: 错误: 初始化参数 2'void list_map(INTLIST*, void ()(void))' make: *** [main.o] 错误 1*

有人可以先帮助解决错误,然后再解决警告吗?

编辑部分:

有人要了 list_sort 函数,这里是:

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

  pTemp=list;          //temp pointers to compare node values
  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;
   }
 }

【问题讨论】:

  • list_sort 长什么样子?
  • 你能改变 list_sort 函数,还是教授也解决了这个问题?如果它是固定的,似乎你已经被处理了一个强硬的手。

标签: c


【解决方案1】:

list_sort() 的简单转换足以使警告消失,但不足以使其真正起作用:

C 标准不保证INTLIST * 和void * 具有兼容的表示,即void (INTLIST *) 和void (void *) 是不同的、不兼容的类型。当list_map() 通过您的void (*f)(void *) 参数调用list_sort() 时,C99 第 6.3.2.3 节第 8 节适用:

如果使用转换后的指针调用 类型不是的函数 与指向类型兼容, 行为未定义。

要使其符合标准,您必须为list_sort() 编写一个包装函数:

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

并将其用作调用的参数:

list_map(aList[i], list_sort_wrapper);

另外,如果list_sort() 实现正确(没有检查算法),它已经遍历列表,即为每个节点调用它根本没有任何意义。

编辑:

好的,list_sort() 实际上并没有对整个列表进行排序 - 这可以通过

list_map(list, list_sort_wrapper);

命名方案是一个严重的 WTF - 如果函数没有对列表进行排序,请将其命名为 list_sort_step() 或 list_select_head() 或其他名称,但请不要 list_sort()。

【讨论】:

  • list_map 是一个映射函数,它接受一个指向 list_sort() 的函数指针,因此只要您调用 list_map,您最终就会对整个列表进行排序,即使 list_sort 正在逐个元素地执行此操作。我知道这看起来很奇怪,但教授是真的不会编码的奇怪人。
【解决方案2】:

首先:为什么要将 C 代码编译为 C++?。请用C编译器编译。

list_sort()的原型是:

void list_sort(INTLIST* list);

而list_map() 有原型:

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

这意味着list_map() 的第二个参数是一个接受void * 参数并返回void(无)的函数。

现在,C 标准保证任何对象指针到void * 的转换都是可以的,所以给定:

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

没关系:

list_sort(pl);

请注意,list_sort() 可以声明为:

void list_sort_generic(void *l);

事实上,由于您的教授在某些地方使用void *,他希望在某些时候将您的列表扩展为通用类型。

无论如何,您可以将INTLIST * 传递给list_sort() 或list_sort_generic(),但list_sort_generic() 可以传递任何对象指针,而list_sort() 只能传递INTLIST *(或void *从INTLIST * 转换而来)。

虽然list_sort() 可以取void *,但list_sort() 的签名不是:

void list_sort(void *l);

所以list_sort()和list_sort_generic()的函数类型不一样,不能互换。 list_map() 需要 list_sort_generic() 的那种函数,但得到的是不同类型的函数。

由于您无法更改任何原型,因此您需要一个演员表。现在,void * 是 C 中的泛型类型,所以你会认为这样的转换会起作用。但是,正如我之前所说,只有对象指针可以转换为 void * 并可移植地返回 - 而不是函数指针类型。因此,您需要在调用 list_map() 时将 list_sort() 转换为正确的类型。

正确的类型是void (*)(void *)。这是一个返回 void 并采用 void * 的函数。

因此,调用应该是:

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

但是,由于list_sort() 的类型和list_map() 对其第二个参数所期望的类型不同,因此强制转换可能会或可能不会起作用。你的教授给了你“不太好的”(即错误)原型。要么他应该一直声明类型泛型函数,要么他应该保留所有INTLIST *。通过中途,他引入了一个不应该存在的复杂演员表,并且可能无法正常工作。我敢肯定,如果你把这件事提请你的教授注意,他会承认这个疏忽。

希望有所帮助。

【讨论】:

  • 这些是我正在寻找的写得很好的细节,我不太关心我正在寻找的答案,以了解问题以充分利用和学习语言。 Alok +1 plus 接受这个彻底的答案。你可以写一本书,我会买的。
【解决方案3】:

在你的函数list_sort中,参数是INTLIST *list。

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

通过查看头部,函数原型是一个函数指针,其参数类型为void *

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

函数指针*f 必须与签名匹配,因此编译器会产生冲突和警告。由于*f 指向list_sort,所以方法签名不匹配。

如果你的函数原型有这个,它会起作用

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

希望这会有所帮助, 最好的祝福, 汤姆。

【讨论】:

  • 不能更改教授定义的list_map原型,我们也不能更改这些原型。
【解决方案4】:

嗯,

void list_sort(INTLIST* list)

有错误的签名作为第二个参数传递

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

【讨论】:

  • 我也在学习 C 时一直在尝试关注这些东西......但我想我的问题是它看起来像 list_map 的 void (f) )(void) 是只是一个函数指针?他能把 void list_sort 变成什么? void list_sort(void* list) ?
【解决方案5】:

如果您将回调转换为正确的函数类型?

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

【讨论】:

  • “如果你得到的不是鸭子,那就打它直到你得到你想要的” - 很抱歉,这对初学者来说是一个糟糕的答案......
  • @LiraNuna:这是功课,他不能改变list_map的原型。恐怕他的老师要他打它,直到它像鸭子一样嘎嘎叫。
  • 这将使警告消失,但现在当函数在list_map()中被调用时,它将调用未定义的行为@
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-29
  • 2011-09-06
  • 1970-01-01
  • 2012-09-12
  • 1970-01-01
相关资源
最近更新 更多