简体   繁体   中英

How to use two functions that return different types as a function pointer in C?

I have a create function that I want to pass a function pointer, however I want the function pointer to be able to call either of the functions below the problem that I am having is that each one return a different type. But when I pass the function pointer I have to specify the return type of the function pointer. Is this correct? I thought using a void pointer in the would solve the return type problem, but now I can't call the function pointer. To clarify I want to pass either populateMonsters or populateClassesTaken into create and be able to call them within create .

typedef struct node{
   void * data;
   struct node* next;
} node;

typedef struct MonsterAttacks{
    unsigned int attackID;
    char monsterName[41];
    char attackLocation[41];
    unsigned int numOfVictims;
}MonsterAttacks;

typedef struct ClassesTaken{
    unsigned int classID;
    char semester[41];
    unsigned int year;
    unsigned int numberOfUnits;
    char grade[10];
}ClassesTaken;


  unsigned int idM = 1;

  MonsterAttacks * populateMonsters(){

    MonsterAttacks *m = NULL;

    m->attackID = idM;
    printf("Enter the name for the Monster \n");
    scanf("%40s",m->monsterName);
    puts("What is his/her attack location?");
    scanf("%40s",m->attackLocation);
    puts("What are the number of victims this monster has demolished?");
    scanf("%ud", &m->numOfVictims);      
    //attackID is assigned and id when node is created
    idM++;
    return m;
}

  unsigned int idC = 1;

  ClassesTaken* populateClassesTaken(){

    ClassesTaken *c = NULL;
    c->classID = idC;
    scanf("What semester was the class taken in? %40s",c->semester);
    scanf("In what year? %ud", &c->year);  
    scanf("How many units was the class? %ud", &c->numberOfUnits);
    scanf("What was your grade in the class? %9s", c->grade);     


    idC++;
    return c;
}

node* create(node* next, void *fp)
{
   node* new_node = (node*)malloc(sizeof(node));
   if(new_node == NULL)
   {
       printf("Error creating a new node.\n");
       exit(0);
   }
    void *data = (*fp)();
}



int main(void)
{

   node* tmp = NULL;
   MonsterAttacks* (*fp)() = &populateMonsters;

   create(tmp, *fp);
}

First of all, it goes without saying that you can't write code like ClassesTaken *c = NULL; c->classID = idC; ClassesTaken *c = NULL; c->classID = idC; . If you don't understand why you can't, you need to go back to learning the very basics of pointers, as explained here: Crash or "segmentation fault" when data is copied/scanned/read to an uninitialized pointer

As for the different functions - the fact that they are different function types is a clear indication that this design doesn't make sense. If you wish to use generic function pointers, you need to define a uniform function format, such as for example void* func (void) .

You cannot use void* as a generic function pointers. It is only defined for use with pointers to objects, not to functions. Code such as create(tmp, *fp); where fp is a function pointer and the function expects a void* is not valid standard C and should not compile, unless you are using non-standard compiler extensions.

Notably, you should never declare functions with an empty parenthis () in C. This is an obsolete language feature. (Unlike C++, where this is fine and encouraged.)

Replace:

node* create(node* next, void *fp)
{

with:

node* create(node* next, void* (*fp)())
{

and

create(tmp, *fp);

with

create(tmp, (void*)fp); //[updated]

Your populateMonsters() causes a Segmentation fault. I replaced its code with fputs("In populateMonsters\\n", stderr);

and it appeared to work:

$ ./a.out

In populateMonsters

There is still a problem in populateMonsters() and populateClassesTaken() . You want to fill the structures with user input but you never allocate the structures (neither on heap nor on stack). Therefore you get a Segmentation fault. Here is my code without the typedefs, include, ...

typedef void*(*myFunctionInterface)(void);

MonsterAttacks * populateMonsters (){

  MonsterAttacks *m = (MonsterAttacks*)malloc(sizeof(MonsterAttacks));
  if (m == NULL){
      printf("Error creating MonsterAttacks");
      exit (-1);
  }

  m->attackID = idM;
  printf ("Enter the name for the Monster \n");
  scanf ("%40s", m->monsterName);
  puts ("What is his/her attack location?");
  scanf ("%40s", m->attackLocation);
  puts ("What are the number of victims this monster has demolished?");
  scanf ("%ud", &m->numOfVictims);
  //attackID is assigned and id when node is created
  idM++;
  return m;
}

unsigned int idC = 1;

ClassesTaken * populateClassesTaken (){

  ClassesTaken *c = (ClassesTaken*)malloc(sizeof(ClassesTaken));
  if (c == NULL){
      printf("Error creating ClassesTaken");
      exit(-2);
  }
  c->classID = idC;
  scanf ("What semester was the class taken in? %40s", c->semester);
  scanf ("In what year? %ud", &c->year);
  scanf ("How many units was the class? %ud", &c->numberOfUnits);
  scanf ("What was your grade in the class? %9s", c->grade);


  idC++;
  return c;
}

node * create (node * next, void *(*fp) ()){
  node *new_node = (node *) malloc (sizeof (node));
  if (new_node == NULL)
    {
      printf ("Error creating a new node.\n");
      exit (-3);
    }
  void *data = fp();
  new_node->data = data;
  return new_node;
}



int main (void) {    
  node *tmp = NULL;
  MonsterAttacks *(*fp_M) () = &populateMonsters;
  ClassesTaken *(*fp_C) () = &populateClassesTaken;
  create (tmp, (myFunctionInterface)fp_M);
}

It is also not a good style to return 0 in case of an error. 0 usually means that everything is ok. Hence, I changed the exit-values to some different negative numbers to distinguish between the errors

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM