1
votes

The function getManager creates a Manager struct and returns a pointer to it from the type ManagerP (This function works ok). The definitions are like this :

typedef struct Manager
{
int ID;
char name[MAX_NAME_LENGTH];
int numberOfStudentsInSchool;
double paycheck;
double attract;
} Manager;  
typedef struct Manager *ManagerP;

//My little code (that does the problem) is this (it's inside main):  

int foundId;
ManagerP manToFind = getManager(1, "manager2", 200.0 , 1.0, 1000); //this works ok.
foundId = manToFind->ID; //Error : "dereferencing pointer to incomplete type"

Can you please help me finding the problem ? I don't understand what this error mean.

Thanks.


EDIT:

Thanks but I just noticed a problem. These lines are inside "Manager.c".

typedef struct Manager
{
    int ID;
    char name[MAX_NAME_LENGTH];
    int numberOfStudentsInSchool;
    double paycheck;
    double attract;
} Manager;
typedef struct Manager *ManagerP;

In my main file I do include "Manager.h" that has some more definitions. I just checked and when I'm moving the two typedefs code (written above) to the main file, everything works properly. But I need these typedefs to be inside "Manager.c" (and then I still get a "dereferencing pointer to incomplete type" error. So what is the problem ??

Edit #2 : Ok I'm posting the three files. When I compile those I get the error : "GenSalary.c:9:21: error: dereferencing pointer to incomplete type"

These are the files :

// *Manager.h* :

#ifndef MANAGER_H
#define MANAGER_H
#define MAX_NAME_LENGTH 30

typedef struct Manager *ManagerP;

ManagerP getManager(int ID, const char name[], double paycheck, 
                    double attract, int numberOfStudentsInSchool);

#endif

// *Manager.c* :

#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "Manager.h"
#define MAX_PRINT_LENGTH 1000

typedef struct Manager
{
int ID;
char name[MAX_NAME_LENGTH];
int numberOfStudentsInSchool;
double paycheck;
double attract;
} Manager;  

ManagerP getManager(int ID, char const name[], double paycheck,
                    double attract, int numberOfStudentsInSchool)
{
    ManagerP retVal = (ManagerP) malloc(sizeof(struct Manager));
    if (retVal == NULL)
    {
        fprintf(stderr, "ERROR: Out of memory in Manager\n");   
        exit(1);
    }
    retVal->ID = ID;
    strcpy(retVal->name, name);
    retVal->paycheck = paycheck;
    retVal->attract = attract;
    retVal->numberOfStudentsInSchool = numberOfStudentsInSchool;
    return retVal;
}

// *GenSalary.c* :

#include <stdio.h>
#include <stdlib.h>
#include "Manager.h"

int main()
{
    int foundId;
    ManagerP manToFind = getManager(1, "manager2", 200.0 , 1.0, 1000); //this works ok.
    foundId = manToFind->ID; //Error : "dereferencing pointer to incomplete type"
    return 0;
}

I compile it using gcc -Wall GenSalary.c Manager.c -o GenSalary and i'm getting : GenSalary.c:9:21: error: dereferencing pointer to incomplete type

NOTE : I CAN'T CHANGE THE MANAGER FILES (THEY BELONG TO EXERCISE)I CAN CHANGE ONLY MAIN.

Thanks for helping !

5
It seems ok. Maybe you should post the real code. What compiler are you using?Giacomo Degli Esposti
Maybe struct Manager { ... } is not included?zch
@user2630165: "But I need these typedefs to be inside "Manager.c"" - why? Why not put them in "Manager.h"? That's obviously your problem.Crowman

5 Answers

2
votes

As written, getManager looks like it intends the returned pointer to be opaque. If that is the case, it would be usual to provide functions for anything the caller should be able to do. For example:

manager.h

...
typedef struct Manager *ManagerP;

ManagerP getManager(int ID, const char name[], double paycheck, 
                    double attract, int numberOfStudentsInSchool);
int getManagerID(ManagerP);

manager.c

...
int getManagerID(ManagerP m) { return m->ID; }

gensalary.c

...
int foundId;
ManagerP manToFind = getManager(1, "manager2", 200.0 , 1.0, 1000);
foundId = getManagerID(manToFind);

The alternative is to move the definition of your struct into the header, where everything can see it (at the moment it is forward-declared in the header, but only manager.c know what is inside).

1
votes

The code below works fine with gcc -Wall -pedantic -o test test.c. I am unsure, however, that hiding pointer types using typedefs has any real advantages to readability. The error must come from somewhere in the context of your code.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct Manager
{
    int ID;
    char name[42];
    int numberOfStudentsInSchool;
    double paycheck;
    double attract;
} Manager;
typedef struct Manager *ManagerP;

ManagerP getManager(int x, char *y, double z, double q, int p)
{
    ManagerP foo = malloc(sizeof(Manager));
    foo->ID = x;
    strncpy(foo->name, y, 42);
    foo->numberOfStudentsInSchool = p;
    foo->paycheck = z;
    foo->attract = q;

    return foo;
}

int main(void)
{
    int foundId;
    ManagerP manToFind = getManager(1, "manager2", 200.0 , 1.0, 1000);
    foundId = manToFind->ID;
    printf("Found ID: %d\n", foundId);

    return 0;
}
1
votes

From your own edit:

I just checked and when I'm moving the two typedefs code (written above) to the main file, everything works properly. But I need these typedefs to be inside "Manager.c"

You need to include these definitions for it to work, as you've found. Put them in "Manager.h" and include "Manager.h" in both your main file, and in "Manager.c".

EDIT: From your code, you need to include the typedef of the actual struct in the header file, as well, not just the typedef of the pointer, so move this:

typedef struct Manager
{
int ID;
char name[MAX_NAME_LENGTH];
int numberOfStudentsInSchool;
double paycheck;
double attract;
} Manager;  

out of "Manager.c", and put it immediately before the typedef of ManagerP. Otherwise, all the main file sees is the declaration of the pointer, and it has no information of what the struct actually contains, hence the "incomplete type" error you're getting.

EDIT 2: If you "CAN'T CHANGE THE MANAGER FILES" as you say, then it's a bit of a silly question, since you can't apply the best answer, but if that actually is true, then you'll just have to copy and paste the struct definition into "GenSalary.c" (or into a new, user-created header file, if you need to use it in other files, too), because that file needs it. Defining the struct separately in both "GenSalary.c" and "Manager.c" is a bad idea for lots of reasons, but it is perfectly legal C, and it'll work (that's all that's happening under the hood when you #include a header file, anyway).

1
votes

The line of doing the typedef for ManagerP will compile since it is a pointer declaration however since the struct Manager is in the file Manager.c and is not available to GenSalary.c the compiler is unable to know what the struct Manager looks like.

So the include file Manager.h needs to have the following lines of code.

typedef struct Manager
{
int ID;
char name[MAX_NAME_LENGTH];
int numberOfStudentsInSchool;
double paycheck;
double attract;
} Manager;

typedef struct Manager *ManagerP;

Then any source file that includes Manager.h will have the definition of the Manager typedef as well as the ManagerP typedef. And when the ManagerP is dereferenced, the compiler will know how to access the various parts of the Manager struct.

Edit: Other considerations

You mention that this is an exercise of some kind so I would like to note that the way this is being done, the struct in a file and the only thing exposed is a pointer to the struct, is a mechanism often used to hide struct details. The goal of this technique is to provide a pointer to an object within a library, the struct in this case, however the person using the library is not to access any of the struct members or to do anything other than pass the pointer to other functions in the library.

So it may be that the point of this exercise is to not access any of the struct members.

1
votes

When you create a pointer to a type, the compiler does not need to know what that type looks like, because all pointers are the same size (4 or 8 or however many bytes). However, if you attempt to dereference that pointer, the compiler must know what the type looks like in order to calculate memory offsets and perform other tasks. Since in your original cpp file the type Manager is not defined, only declared, the compiler cannot determine what memory offset it needs to use before it can reach the ID field. (A type like this is often called opaque.) Thus the compiler informs you that the type is incomplete.

The same issue would occur if you attempted to create a variable of type Manager directly, because the compiler does not know how much memory is required to be set aside for this variable. You could malloc a pointer to Manager, but if you tried to do sizeof(Manager), it would fail.

In order for this to work, the compiler needs to know what the type looks like at the point where you attempt to dereference the pointer. Thus, the struct definition must be placed within the main cpp file, or within any of the headers which are included by that cpp file.