Passing struct pointer through a function then accessing struct data - c

I'm working on a function that works as part of a larger program. My C pointer skills are a bit rusty and so I need some help here.
Notice: I've omitted a lot of the program because it is irrelevant and works correctly, all you need to know is that I import a header file that declares the struct ImportedStruct, also, I cannot change the function header, so I need to pass the ImportedStruct as a pointer and the function as a pointer.
I get the following error message in VS code: pointer to incomplete class type is not allowed.
Any ideas?
#include "name.h"
long int *functionName(struct ImportedStruct *structName, int *irrelevant){
int width = structName->width;
int height = structName->height;
// Remaining function ...
}
Header file:
struct ImportedStruct
{
int width, height;
};

I just compiled the code you provide
struct ImportedStruct
{
int width, height;
};
long int *functionName(struct ImportedStruct *structName, int *irrelevant){
int width = structName->width;
int height = structName->height;
// Remaining function ...
return (long int *)(width + height);
}
and it compiled successfully.
Make sure struct definition is included.
Also, you can put
struct ImportedStruct
{
int width, height;
};
right before your function definition. If compiler does not give you multiple definition error, then struct definition is not included by headers.

Related

Pass a string in a struct to a function and return it

I want to return the name of the smallest city population-wise, if it is the second city. (Please don't mind the if statement, I know it's bland), the missing return is what bothers me.
I assume I should declare a pointer inside the function rSmallestCity, like *rtrn but I guess the source variable is destroyed before it is used?
typedef struct Coordinate{
int x,y;
}Coordinate;
typedef struct city{
char name[20];
int population;
Coordinate coordinates;
}city;
char *rSmallestCity(city **cl, int n)
{
char *rtrn = NULL;
if(cl[n-2]->population>cl[n-1]->population)
{
rtrn = &cl[n-1]->name;
}
return rtrn;
}
int main()
{
city c1 ={.name="Mumbai", .population=310, .coordinates.x=3, .coordinates.y=4};
city c2 ={.name="Delhi", .population=300, .coordinates.x=3, .coordinates.y=2};
city *clist[2];
clist[0]=&c1;
clist[1]=&c2;
printf("\n%s is smallest\n",rSmallestCity(clist,2));
}
warning: assignment to 'char ' from incompatible pointer type 'char ()[20]' [-Wincompatible-pointer-types]|
I assume I should declare a pointer inside the function rSmallestCity, like *rtrn but I guess the source variable is destroyed before it is used?
A good question. And your assumption is correct. Creating a variable inside a function it's existence ends upon leaving the function. But in this case, because the struct member name is already a char * you do not need to create another variable. Just return c1.name. (see code example below.)
A few other suggestions:
In the struct declaration:
typedef struct Coordinate{
int x,y;
}Coordinate;
You've used the same symbol (Coordinate) for the struct name, and for it's typedef. This is not a good practice. If you need both a struct name and a typedef, pick different symbols. BTW, in this this example, only one or the other is needed. Say you pick the typedef, then the struct is completely defined by:
typedef struct {
int x,y;
}Coordinate;
That suggestion applies to both struct declarations in your example code.
The signatures for the main function do not include int main(){...} rather
int main(void){..., return 0;} and int main(int argc, char *argv[]){..., return 0;}
The following code example illustrates some of the other suggestions for improvements in comments under your post,
typedef struct {
int x,y;
}Coordinate;
typedef struct {
char name[20];
int population;
Coordinate coordinates;
}city;
//return char * rather than char to allow for full null terminated char array (string)
char * rSmallestCity(city c1[],int cityCount)//generisize function prototype to
{ //to easily accommodate bigger arrays if needed
long long size, sizeKeep = 8e9; //index and population. initialize larger than possible population
int indexKeep = 0;
//note you do not need to define a char *, the struct already contains one
for(int i=0; i<cityCount; i++)//use a loop rather than a single comparison, keep the smalles
{
size = c1[i].population;
sizeKeep = (size < sizeKeep) ? indexKeep = i, size : sizeKeep;
}
printf("\n%s\n",c1[indexKeep].name);
return c1[indexKeep].name;
};
int main(void)//use minimum signature for main, and call return before leaving.
{
//combining your original declarations and assignments for struct
//into a single declaration/definition.
city c1[] = {{.name="Mumbai", .population=310, .coordinates.x=3, .coordinates.y=4},
{.name="Delhi", .population=300, .coordinates.x=3, .coordinates.y=2}};
int cityCount = sizeof(c1)/sizeof(c1[0]);
printf("\n%s is smallest",rSmallestCity(c1, cityCount));
return 0;
};
The solution that I originally left in comment under OP (remove & in the line &cl[n-1]->name;) needs some explanations to avoid problems later.
(It is an educational answer not a full answer on pointers, array decay, ... And many examples can be found on stackoverflow. I tried to simplify)
Try this simple code.
int main()
{
char myString1[25]="Toulouse" ; // French City
printf("%p\n",myString1);
printf("%p\n",&myString1);
}
The output is the same, but an array name and the address of an array name are not the same. The array name is evaluated to the address of its first element. So it works in your case but a warning is issued during compilation and it is very important. Firstly, do not remove compilation warnings.
Now, try this code :
int main()
{
char myString1[25]="Toulouse" ; // French City
printf("%p\n",myString1+1);
printf("%p\n",&myString1+1);
}
The outputs are different. Because myString1 is evaluated to char* and &myString1 to char [25]. So +1, in the first, case adds one (sizeof char) to the pointer and in the other case, it adds 25.
Delete the "&" in the line:
rtrn = &cl[n-1]->name;
To extremely simplify, you assigned an "address of char[]" to a char*, but array syntax makes it work regardless.

Segmentation fault in cast struct in c

In an attempt to encapsulate struct members (in a similar way as discussed in this question), I created the code below.
In the code below, I have a c-struct, which contains methods to access members of the struct which are hidden (by being cast into a struct otherwise the same but without the hidden properties)
#include <stdio.h>
typedef struct class {
int publicValue;
int (*getPV)();
void (*setPV)(int newPV);
} class;
typedef struct classSource {
int publicValue;
int apv;
int (*getPV)();
void (*setPV)(int newPV);
int PV;
} classSource;
class class_init() {
classSource cs;
cs.publicValue = 15;
cs.PV = 8;
int class_getPV() {
return cs.PV;
};
void class_setPV(int x) {
cs.PV = x;
};
cs.getPV = class_getPV;
cs.setPV = class_setPV;
class *c = (class*)(&cs);
return *c;
}
int main(int argc, const char * argv[]) {
class c = class_init();
c.setPV(3452);
printf("%d", c.publicValue);
printf("%d", c.getPV());
return 0;
}
When I run this, I get a segmentation fault error. However, I noticed that if I comment out certain lines of code, it (seems) to work okay:
#include <stdio.h>
typedef struct class {
int publicValue;
int (*getPV)();
void (*setPV)(int newPV);
} class;
typedef struct classSource {
int publicValue;
int apv;
int (*getPV)();
void (*setPV)(int newPV);
int PV;
} classSource;
class class_init() {
classSource cs;
cs.publicValue = 15;
cs.PV = 8;
int class_getPV() {
return cs.PV;
};
void class_setPV(int x) {
cs.PV = x;
};
cs.getPV = class_getPV;
cs.setPV = class_setPV;
class *c = (class*)(&cs);
return *c;
}
int main(int argc, const char * argv[]) {
class c = class_init();
c.setPV(3452);
//printf("%d", c.publicValue);
printf("%d", c.getPV());
return 0;
}
I presume that it might have something to do with using the initializer to add the getter and setter methods to the struct, as those might overwrite memory.
Is what I am doing undefined behavior? Is there a way to fix this?
EDIT: With the help of the answer below, I have re-written the code. In case anyone wants to see the implementation, below is the revised code
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int pub;
} class;
typedef struct {
class public;
int PV;
} classSource;
int class_getPV(class *c) {
return ((classSource*)c)->PV;
}
void class_setPV(class *c, int newPV) {
((classSource*)c)->PV = newPV;
}
class *class_init() {
classSource *cs = malloc(sizeof(*cs));
if((void*)cs == (void*)NULL) {
printf("Error: malloc failed to allocate memory");
exit(1);
}
cs->public.pub = 10;
cs->PV = 8;
return &(cs->public);
}
int main() {
class *c = class_init();
class_setPV(c,4524);
printf("%d\n",class_getPV(c));
printf("%d\n",c->pub);
free(c);
return 0;
}
There are at least three separate problems in your code.
You don't actually have a "struct otherwise the same but without the hidden properties". Your class and classSource structs have their getPV and setPV members in different places. Internally member access boils down to byte offsets from the beginning of the struct. To have a fighting chance of working, your code would need to have a common initial prefix of members between the two struct types (i.e. get rid of int apv; or move it to the end).
You're returning a struct by value, which automatically makes a copy. You've reimplemented the object slicing problem: Because the return value has type class, only the members of class will be copied. The extra members of classSource have been "sliced off".
You're using nested functions. This is not a standard feature of C; GCC implements it as an extension and says:
If you try to call the nested function through its address after the containing function exits, all hell breaks loose.
This is exactly what's happening in your code: You're calling c.setPV(3452); and c.getPV after class_init has returned.
If you want to fix these problems, you'd have to:
Fix your struct definitions. At minimum all members of class need to appear at the beginning of classSource in the same order. Even if you do that, I'm not sure you wouldn't still run into undefined behavior (e.g. you might be violating an aliasing rule).
I'm somewhat sure that embedding one struct in the other would be OK, however:
typedef struct classSource {
class public;
int PV;
} classSource;
Now you can return &cs->public from your initializer, and your methods can cast the class * pointer back to classSource *. (I think this is OK because all struct pointers have the same size/representation, and X.public as the first member is guaranteed to have the same memory address as X.)
Change your code to use pointers instead. Returning a pointer to a struct avoids the slicing problem, but now you have to take care of memory management (malloc the struct and take care to free it later).
Don't use nested functions. Instead pass a pointer to the object to each method:
class *c = class_init();
c->setPV(c, 3452);
int x = c->getPV(c);
This is somewhat tedious, but this is what e.g. C++ does under the hood, essentially. Except C++ doesn't put function pointers in the objects themselves; there's no reason to when you can either use normal functions:
setPV(c, 3452);
int x = getPV(c);
... or use a separate (global, constant, singleton) struct that just stores pointers to methods (and no data). Each object then only contains a pointer to this struct of methods (this is known as a vtable):
struct classInterface {
void (*setPV)(class *, int);
int (*getPV)(const class *);
};
static const classInterface classSourceVtable = {
class_setPV, // these are normal functions, defined elsewhere
class_getPV
};
Method calls would look like this:
c->vtable->setPV(c, 1234);
int x = c->vtable->getPV(c);
But this is mainly useful if you have several different struct types that share a common public interface (class) and you want to write code that works uniformly on all of them.

Returning pointer to pointer of structs

I am working on a database which has multiple structs. I have defined a function which loads data from a csv file and stores each line as a struct. I store them using a double pointer, so one pointer points to multiple pointers for each struct variable. The function does return the double pointer correctly, however I get a warning: return from incompatible pointer type.
My code is as follows:
struct part** loadParts(char* fileName, int m)
{
typedef struct part
{
int id;
int cost;
} Part;
FILE* fptr = fopen(fileName, "r");
//creat pointer to array of pointers to part structs
Part** parts;
parts = malloc((nParts) * sizeof(Part *));
//length of one line
char line[1000];
//while new items can be added
int i;
i=0;
while (fgets(line, sizeof(line), fptr)!= NULL)
{
parts[i] = malloc(sizeof(Part));
//get id
int id = atoi(strtok(line, ";"));
parts[i]->id = id;
// get cost
int id = atoi(strtok(line, ";"));
parts[i]->cost = cost;
i++;
}
fclose(fptr);
return parts;
}
Does anybody know why this warning occurs? Many thanks in advance!
You have in outline:
struct part** loadParts(char* fileName, int m)
{
typedef struct part
{
int id;
int cost;
} Part;
…
Part** parts;
…
parts = …
…
return parts;
}
There is no way that this will compile without warnings. The struct part used in the function signature is, by definition, wholly unrelated to the struct part defined inside the function. You are, therefore, returning a pointer to one type in a function that is expecting to return a pointer to a different type, even though those types may both be spelled struct part. There isn't even a way to cast your code out of trouble.
As indicated in the comments, the structure definition must come outside the function, before the function definition (and probably before any declaration — and typically in a header file that's used wherever the structure is used).
One way to fix the code, therefore, is:
typedef struct part
{
int id;
int cost;
} Part;
struct part **loadParts(char *fileName, int m)
{
…
Part **parts;
…
parts = …
…
return parts;
}
You could have the function return a Part ** in this scenario.
However, the function should now be defined as static unless you have a header to contain the structure definition. If you don't have a header, you can't (reliably) access the structure type in other source files. (It can be done by writing the code out twice, but writing code twice should be anathema — it becomes a maintenance liability before you've finished typing, or copy'n'pasting, the second copy.)
It is possible that you are dealing with an opaque type; the code outside this file doesn't need to know about the structure details. That's legitimate; it can even be (very) beneficial. You just need a different way of writing things, though:
Header:
typedef struct part Part;
extern struct part **loadParts(char *fileName, int m);
If you decide not to expose the name Part, you could use this header instead:
struct part;
extern struct part **loadParts(char *fileName, int m);
The first line says "there is a type struct part but the details will be supplied later, if you need them". The second declares the function returning a pointer to pointer to struct part value. The extern is optional; I use it — many people don't. In this code, the first line is optional. However, if you had extern int num_parts(struct part **list); as a function, you would need that to appear after the loadParts() declaration, or you would need the struct part; line to ensure that the type in the prototype is not new.
Source:
struct part
{
int id;
int cost;
};
struct part **loadParts(char *fileName, int m)
{
…
Part **parts;
…
parts = …
…
return parts;
}
You need to worry about header guards in the header to ensure idempotency. (In this example, there's no problem of self-containedness, but you should also ensure that your headers are self-contained — there are multiple questions on SO which will explain those terms if you search).

dereferencing pointer to incomplete type in C

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 !
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).
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;
}
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).
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.
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.

Array of struct pointers, invalid initializer error, in C

This code:
extern void *malloc(unsigned int);
struct Box {
int x, y ,w, h;
};
struct Wall {
char color[15];
struct Box *boxes[20];
};
int main(int argc, const char *argv[])
{
struct Wall *mywall = malloc(sizeof(struct Wall));
struct Box *myboxes[] = mywall->boxes;
return 0;
}
gives me invalid initializer error at line 14. What I am trying to do, is to get a copy of array of struct pointers, which are in a different struct.
Ouch; there are a number of problems here.
extern void *malloc(unsigned int);
Don't do that; use #include <stdlib.h> because that will be correct and what you wrote is typically incorrect (the argument to malloc() is a size_t, which is not necessarily an unsigned int; it might be unsigned long, or some other type).
struct Box {
int x, y ,w, h;
};
Apart from erratic space, struct Box is OK.
struct Wall {
char color[15];
struct Box *boxes[20];
};
And struct Wall is OK too.
int main(int argc, const char *argv[])
You aren't using argc or argv, so you'd be better using the alternative declaration of:
int main(void)
Original code again:
{
struct Wall *mywall = malloc(sizeof(struct Wall));
This allocates but does not initialize a single struct Wall. Of itself, it is OK, though you should check that the allocation succeeded before you use it. You also need to worry about allocating the struct Box items that the elements of the array will point to.
struct Box *myboxes[] = mywall->boxes;
You've got a minor catastrophe on hand here. You can't copy arrays like that. You haven't checked that you've got an array. Ignoring the error checking, you are stuck with one of:
struct Box *myboxes[] = { &mywall->boxes[0], &mywall->boxes[1], ... };
or:
struct Box **myboxes = &mywall->boxes;
I'm not convinced that you'd want the second version, for all it's shorter.
return 0;
I like to see return 0; at the end of main(), even though C99 allows you to omit it.
}
How about:
struct Box **myboxes = mywall->boxes;
?
Then you can do stuff like:
for ( int i = 0 ; i < 15 ; i++ )
mywall->boxes[i] = malloc(sizeof(Box));
Box* x = myboxes[1];
As the code is now, mywall->boxes isn't initialized.
NOTE: just re-read the question - this won't return a copy of the array, but point to the same location. There's no short solution for a copy without using memcpy or just copying the structs.

Resources