Setting up structs in C - giving strange errors - c

I have set-up structs like so in a C program:
typedef struct header block_header;
struct header {
size_t size;
block_header *next_pointer;
block_header *prev_pointer;
};
However, when I run any expression like the following:
int myinit()
{
block_header *p = init_heap_segment(BLOCK_HEAD_SIZE);
// etc etc
}
It gives me several errors for each function that it is declared in:
allocator.c: In function ‘myinit’:
allocator.c:37:38: error: ‘header’ undeclared (first use in this function)
allocator.c:37:38: note: each undeclared identifier is reported only once for each function it appears in
allocator.c: In function ‘function’:
allocator.c:67:2: error: unknown type name ‘header’
What is the problem with the way that it is set-up? How do I make these errors go away?
EDIT: Definition of:
#define BLOCK_HEAD_SIZE (ALIGN(sizeof(header)))

This is your problem
#define BLOCK_HEAD_SIZE (ALIGN(sizeof(header)))
There's no such type as header in your program, which is what the compiler is telling you. You have defined type struct header and you have defined a typedef name block_header for it. So choose whichever you prefer: either sizeof(struct header) or sizeof(block_header). But not sizeof(header).
In C++ language defining a struct header type would also introduce typename header into the program. But not in C. In C the type defined by struct header is called struct header - two words. It cannot be shortened to a mere header.

In your program, there is no such type called header. But you are using
#define BLOCK_HEAD_SIZE (ALIGN(sizeof(header))) // problem
It should be-
#define BLOCK_HEAD_SIZE (ALIGN(sizeof(struct header)))
or
#define BLOCK_HEAD_SIZE (ALIGN(sizeof(block_header)))
Whenever you are calculating size like this, make sure that you are using correct parameters!

Why not just do:
typedef struct block_header {
size_t size;
block_header *next_pointer;
block_header *prev_pointer;
}header;
and with that you can do:
header *p = init_heap_segment(BLOCK_HEAD_SIZE);
with a new declaration for init_heap_segment() where it returns 'header *' instead of 'struct block_header *'. Just much cleaner.

Related

Why is GCC make throwing errors and uneccesary warnings only when using the struct name instead of typedef?

I have a program consisting of two source files (farm.c, init.c) and two corresponding header files (farm.h, init.h) Both source files contain header guards and each other, because they both require functions/variables from each other.
init.h:
#ifndef INIT_H
#define INIT_H
#include<stdio.h>
#include<stdlib.h>
#include"farm.h"
#define PIG_SOUND "oink"
#define CALF_SOUND "baa"
enum types {PIG, CALF};
typedef struct resources {
size_t pork;
size_t veal;
size_t lamb;
size_t milk;
size_t eggs;
} resources;
typedef struct animal {
size_t legs;
char* sound;
int efficiency;
void (*exclaim)(struct animal*);
void (*work)(struct animal*, struct resources*);
} animal;
/* I have tried various ways of declaring structs in addition to
the typedef such as this */
//animal stock;
//animal farm;
void make_pig(struct animal* a, int perf);
void make_calf(struct animal* a, int perf);
#endif
farm.h:
#ifndef FARM_H
#define FARM_H
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
#include"init.h"
/* GCC does not recognise the typedef or struct identifier
until these forward declarations have been made in
addition to the included init.h header file */
//typedef struct animal animal;
//typedef struct resources resources;
void exclaim(animal* b);
void work(struct animal* b, struct resources* s);
#endif
init.c:
#include"init.h"
void make_pig(animal* a, int perf) {
a->legs = 4;
a->sound = PIG_SOUND;
a->efficiency = perf;
a->exclaim = exclaim;
a->work = work;
}
void make_calf(animal* a, int perf) {
a->legs = 4;
a->sound = CALF_SOUND;
a->efficiency = perf;
a->exclaim = exclaim;
a->work = work;
}
farm.c:
#include"farm.h"
int main() {
return 0;
}
void exclaim(animal* a) {
for (int i = 0; i < 3; i++) {
printf("%s ", a->sound);
}
printf("\n");
}
void work(animal* a, struct resources* r) {
if (!strcmp(a->sound, PIG_SOUND)) {
r->pork += a->efficiency;
}
if (!strcmp(a->sound, CALF_SOUND)) {
r->veal += a->efficiency;
}
}
Using both types of names (i.e.,struct ani and animal) normally works perfectly fine on my Linux system with the C99 standard. However when I use struct ani instead of animal here, I get the below warnings for every instance of the type usage for struct ani and struct resources.
lib/farm.h:10:21: warning: ‘struct ani’ declared inside parameter list will not be visible outside of this definition or declaration
10 | void exclaim(struct ani* a);
| ^~~
lib/farm.h:11:33: warning: ‘struct resources’ declared inside parameter list will not be visible outside of this definition or declaration
11 | void work(struct ani* a, struct resources* r);
And 10 warnings in total for every usage of function pointers of the form:
src/init.c:17:16: warning: assignment to ‘void (*)(struct ani *)’ from incompatible pointer type ‘void (*)(struct ani *)’ [-Wincompatible-pointer-types]
17 | a->exclaim = exclaim;
| ^
src/init.c:18:13: warning: assignment to ‘void (*)(struct ani *, struct resources *)’ from incompatible pointer type ‘void (*)(struct ani *, struct resources *)’ [-Wincompatible-pointer-types]
18 | a->work = work;
Can someone please explain why such behaviour occurs and how I can avoid problems? It typically takes me an unfeasible amount of time to solve these errors and I still don't truly understand my mistake in the first place.
You've hit one of the odd corner cases of C scoping rules.
Informally, a tagged struct (or union, but I'm not going to repeat that over and over) springs into existence when it is named if no declaration for it is visible. "Springs into existence" means that it is considered declared in the current scope. Also, if a tagged struct was previously named in a scope and you then declare a struct with the same tag, the two structs are considered the same. Until a declaration for the struct is completed, the struct is considered an incomplete type, but a pointer to an incomplete type is a complete type, so you can declare a pointer to a tagged struct before you actually complete the definition of the struct.
Most of the time, that just works with minimal thought. But function prototypes are a bit special, because a function prototype is a scope, all by itself. (The scope lasts only until the end of the function declaration.)
When you put that together, you end up with the issue you're facing. You cannot use a pointer to a tagged struct in a function prototype unless the tagged struct was known before the function prototype appears. If it had been mentioned before, even in an outer scope, the tag is visible and therefore will be considered to be the same struct (even if it is still incomplete). But if the tag was not previously visible, a new struct type will be created within the prototype scope, which will not be visible after that scope ends (which is almost immediately).
Concretely, if you write the following:
extern struct animal * barnyard;
void exclaim(struct animal*);
then the two uses of struct animal refer to the same struct type, which will presumably be completed later (or in another translation unit).
But without the extern struct animal * barnyard; declaration, the struct animal named in the exclaim prototype is not previously visible, so thus is declared only in the prototype scope, so it is not the same type as some subsequent use of struct animal. Had you put the declarations in the opposite order, you would have seen a compiler warning (assuming you'd asked for compile warnings):
void exclaim(struct animal*);
extern struct animal * barnyard;
(On godbolt, bless it's heart)
A typedef declaration performs the same way as the extern declaration above; the fact that it is a type alias is not relevant. What's important is that the use of struct animal in the declaration causes the type to spring into existence, and you can subsequently use it freely in a prototype. That's the same reason that the function pointers inside your struct definitions are OK; the start of the struct definition was sufficient to cause the tag to be declared, so the prototype sees it.
In fact, any syntactic construction which contains a tagged struct (struct whatever) will serve the same purpose, because what matters is the effect of mentioning a tagged struct with no visible declaration. Above, I used extern global declarations as examples because they are lines which might appear in a header, but there are many other possibilities, including even the declaration of a function which returns a pointer to a struct (because the return type of a function declaration is not in the prototype scope).
See below for some additional comments about the edited question.
My personal preference is to always use typedefs as forward declarations of tags, and never use struct foo anywhere in my code other than the typedef and the subsequent definition:
typedef struct Animal Animal;
void exclaim(Animal*);
// ...
// Later or in a different header
struct Animal {
Animal* next;
void (*exclaim)(Animal *);
// etc.
};
Note that I always use the same identifier for the tag and the typedef. Why not? There's no confusion and tags have not been in the same namespace as other identifiers since C was premordial.
For me, a big advantage of this style is that it lets me separate implementation details; the public header only contains the typedef declarations (and prototypes which use that type) and only the implementation needs to contain the actual definitions (after first having included the public header).
Note: since this answer was written, the question was edited to add a more detailed code sample. For now, I'll just leave these additional notes here:
On the whole, you get better answers when you provide better information. Since I couldn't see your actual code, I did the best I could, which was to try to explain what is going on, leaving you to apply that to your actual code.
In the code you have now added to the question, there is a circular header dependency. These should be avoided; it's almost impossible to get them right. The circular dependency means that you don't control the order of inclusion, so a declaration in one header might not come before the use in another header. You no longer have a forward declaration, because, depending on the inclusion order, it might be a backward declaration.
To resolve the circular dependency, abstract out the shared components and put them in a new header file. Here, forward declarations of structs (using, for example, typedefs) are highly useful because they don't depend on anything used in the definition of the struct. A shared header might include only typedefs, or it might also include prototypes which don't require additional dependencies.
Also, avoid putting long lists of library includes in your header files; include only those headers actually necessary to define types actually used in the header.

typedef struct leads to "pointer to incomplete type not allowed" error

I am using a library which contains the following declaration in its header (http_client.h):
typedef struct _httpc_state httpc_state_t;
The library defines the struct in the implementation (http_client.c)
typedef struct _httpc_state
{
struct altcp_pcb* pcb;
ip_addr_t remote_addr;
u16_t remote_port;
int timeout_ticks;
struct pbuf *request;
struct pbuf *rx_hdrs;
u16_t rx_http_version;
u16_t rx_status;
altcp_recv_fn recv_fn;
const httpc_connection_t *conn_settings;
void* callback_arg;
u32_t rx_content_len;
u32_t hdr_content_len;
httpc_parse_state_t parse_state;
#if HTTPC_DEBUG_REQUEST
char* server_name;
char* uri;
#endif
} httpc_state_t;
In that same C file, it implements the following function, which uses the struct:
/** http client tcp poll callback */
static err_t
httpc_tcp_poll(void *arg, struct altcp_pcb *pcb)
{
/* implement timeout */
httpc_state_t* req = (httpc_state_t*)arg; // Here the void pointer is casted to httpc_state_t
LWIP_UNUSED_ARG(pcb);
if (req != NULL) {
if (req->timeout_ticks) { // Here the concrete type is used. Works. No problems.
req->timeout_ticks--;
}
if (!req->timeout_ticks) {
return httpc_close(req, HTTPC_RESULT_ERR_TIMEOUT, 0, ERR_OK);
}
}
return ERR_OK;
}
I have a C++ file which uses this library, and of course includes the required header (http_client.h).
extern "C"
{
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "lwip/tcpip.h"
#include "lwip/apps/http_client.h" // Here I include their http_client.h file
#include "projdefs.h"
}
In my next function, I need to do exactly what their implementation does. I need to do something with httpc_state_t. I implemented their callback function as follows:
err_t rec_fn(void *arg, struct altcp_pcb *conn, struct pbuf *p, err_t err)
{
if (p)
{
httpc_state_t* req = (httpc_state_t*)arg; // Compiler sees no problems in casting to my desired type....
req->timeout_ticks = 30; // COMPILE ERROR, pointer to incomplete class type _httpc_state is not allowed
}
}
Why am I getting that compile error?! Header file is included. Header files declares the typedef. Even after reading this and this, I still don't see what I am doing wrong....
In the translation unit where the function rec_fn is defined the compiler sees only the following declaration
typedef struct _httpc_state httpc_state_t;
It knows nothing about whether the data member timeout_ticks used in this statement
req->timeout_ticks = 30;
is indeed declared within the structure struct _httpc_state and what is its type. That is the name timeout_ticks is undeclared in this translation unit. So the compiler issues an error.
If you are going to use data members of the structure in a translation unit then the compiler needs to know their declarations. That is you need also to include the structure definition.
Either move the structure definition in the header if you are allowed to do that or duplicate its definition in the module where your function is defined.
Pay attention to that if the structure definition was not placed in the header then the reason of that can be that the author of the code does not want to make it available outside his module or library.
Incomplete type means its declared but not defined. You need to define that struct in the header file and include it into your C file.
The error message is poorly worded.
Pointers to incomplete types are fine!
Dereferencing to a member of an incomplete type is the problem.
At the point where the error occurs the compiler hasn't 'seen' the full definition of the type in that translation unit.
It recognises the type, but doesn't know if the type even has a member timeout_ticks let alone how to generate code for access it.
[Such as where the member is in relation to the start of the object.]
Pointers to incomplete types are a useful way to reduce dependencies and code coupling. If code only needs to pass pointers to a type around the type can be declared (incomplete) and help with type checking but not be exposed to the full definition.

Redeclaring opaque struct in C file

I'm working on a header file that declares some opaque structs which is supposed to be defined in the corresponding C file. Here it is:
decl.h
#ifndef DECL_H
#define DECL_H
typedef struct test_t test;
#endif //
Some library that is supposed to be used in the implementation defines another opaque struct in its header lib.h:
//...
typedef struct _library_struct_t library_struct;
//...
Now in my decl.c file I want to make struct test_t to be the same (or compatible) with the library_struct. I tried this:
decl.c
//...
typedef library_struct test; //error: conflicting types for ‘test’
//...
But it does not compile. So the only way to go I can see now is
struct test_t{
library_struct *lib_struct_ptr;
};
Is there shorter or more convenient way? Both test and library_struct are opaque. Why can't I make the test to be the same as library_struct? Can macros be helpful here?
your code is equivalent to
typedef struct test_t test; /* from decl.h */
typedef library_struct test; /* in decl.c */
So you redefine test and of course the compiler doesn't accept that
I don't know what you expect to do through macros but redefinition is not allowed.
In the worst case you can hide the type of a pointer with a void * then casting to the type you (hope) have, but this is obviously dangerous because the compiler will follow you at your own risk.
The compiler does not check the types against you but to help you to see your errors at compile time ...

cannot declare struct member within a structure in c

I want to declare a structure value in a different structure definition in header file. So I have done the following in header file. But compiler gives me the error message field myStructMember has incomplete type
in A.h header file:
struct My_A{
int value;
};
In B.h header file:
struct My_A; // forward declaration
struct My_B
{
struct My_A myStructMember; // error is here!
int differentValue;
};
Is it possible to declare a struct member within a structure declarations?
EDIT: My_A and My_B are declarated in different header file.
EDIT 2:
When I add include "A.h" in B.h, then it works. Does it make any problem?
In B.h header file:
include "A.h" // added this line insead of forward declaration
struct My_B
{
struct My_A myStructMember; // not any error anymore
int differentValue;
};
Not without # including A.h in B.h (or before B.h every time it is referenced - when the compiler parses My_B otherwise it lacks the information required to determine the size of My_A. You could use a pointer instead, but that gains all the overhead of handling them.
If you are just trying to achieve abstraction / details hiding, then you could change your forward declaration of My_A so that it is now a struct of the correct size (using a char array, for instance). Then you must make sure that nothing includes both A.h and B.h as that will cause a compiler error. Oh, and really make sure you get the size right or weird stuff will happen. There are ways to ensure this is correct using macro assertions.
e.g. make B.h
struct My_A{
char hidden_data[4];
};
struct My_B
{
struct My_A myStructMember;
int differentValue;
};
The error is what it says - you can't use struct until you has it's complete type. You simply don't know its size at that point. Forward declaration won't help here.
Alternatively you can use pointer to struct. Pointer size is known.

errors while trying to pass a structure to a function

In the following program I try to pass a structure to a function. But I get errors,and I do not understand why. What mistake have I made in this program ?
I am using gcc for compiling this c program.
#include <stdio.h>
struct tester {
int x;
int *ptr;
};
void function(tester t);
int main() {
tester t;
t.x = 10;
t.ptr = & t.x;
function(t);
}
void function(tester t) {
printf("%d\n%p\n",t.x,t.ptr);
}
Errors :
gcc tester.c -o tester
tester.c:8:15: error: unknown type name ‘tester’
tester.c: In function ‘main’:
tester.c:12:2: error: unknown type name ‘tester’
tester.c:13:3: error: request for member ‘x’ in something not a structure or union
tester.c:14:3: error: request for member ‘ptr’ in something not a structure or union
tester.c:14:13: error: request for member ‘x’ in something not a structure or union
tester.c: At top level:
tester.c:18:15: error: unknown type name ‘tester’
NOTE : If I replace printf with cout and stdio with iostream and name the extension to .cpp (!), I get no errors. Why is that ? No wonder I compile it using g++
If you don't typedef the struct you must specify struct in front of the struct name while declaring it like so:
struct tester t;
Either you do that or you do the following:
typedef struct {
int x;
int *ptr;
}tester;
Update
Below is a quote from Adam Rosenfield from the following post Difference between 'struct' and 'typedef struct' in C++?:
In C++, all struct/union/enum/class declarations act like they are implicitly typedef'ed, as long as the name is not hidden by another declaration with the same name.
your struct isn't named. either use struct tester t; or usa a typedef
The thing is you're trying to compile with gcc, which is a "c language" compiler and you're following C++ style of code.
It is possible to create a struct variable by just structname variablename;
but in c++, you explicitly have to tell the compiler it is a struct like
struct structname variablename;
Just do that and you'll be fine, otherwise you can use a typedef which is basically you tell the compiler form now on you are going to call struct tester to only tester, which will suit you more since you only have to only a minor change.

Resources