liststructs.h:
struct _data_object {
int temp;
int interval_length;
};
typedef struct _data_object temp_data_object;
struct _list_node {
data_object *temp_data;
struct _list_node *prev;
struct _list_node *next;
};
typedef struct _list_node list_node;
struct _list {
int time;
list_node *head;
list_node *tail;
};
typedef struct _list list;
list.h:
list_node *alloc_node(int temp, int interval_length);
list_node *alloc_dummy_node(void);
list *alloc_temp_list(void);
void delete_first(list *list);
void insert_node(list *list, list_node *new_node);
void insert(list *list, int temperature, int interval);
I then use this in another file called calculations.c and in main.c, but then I declare extern list *xs; in calculations.h (it is defined in calculations.c) it complains:
Error[Pe020]: identifier "list" is undefined
I have included liststructs.h and list.h in that order in calculations.c and main.c and want to use xs in calculations and main.
Also:
What is better? To have structs and listoperations declared in the same header or separate them?
Protect your include files with #include safeguards, include liststructs.h in list.h, and both files in calculations.h. Safeguards in header files are typically written as:
#ifndef _XXXX_H_ // XXXX = LIST, LISTSTRUCT etc
#define _XXXX_H_
// definitions for file XXXX.h
#endif /* _XXXX_H_ */
From what you've told us, you have extern list *xs; declared in calculations.h, but didn't mention having included liststructs.h before that line which defines the identifier list.
liststructs.h needs to be included anywhere before you use the identifier list, just as list.h must be included before you attempt to call any of the functions it declares.
As long as you have include/header guards don't worry about including header files multiple times in a translation unit.
Related
I'm working on some data structures in C. I have created a queue data structure that can take any type of data. This is currently being done by a macro which is default initialized to int type.
#ifndef DATATYPE
#define DATATYPE int
#endif
The queue header is being included in another data structure - the binary search tree, and I am using the queue for a breadth-first-search implementation. In the Makefile, I have modified the DATATYPE macro from an int to a binary_tree_node_t * type.
binary_tree: DEFS=-DDATATYPE="struct BINARY_TREE_NODE *"
My question is, is there a better way to do this using typedefs? Can I define a type DATATYPE as an int in the queue implementation, but have it get modified in a different header file?
Or is it possible to implement a queue that can take any datatype?
Here are the source files (redacted for sake of brevity) for reference:
queue.h
#ifndef _QUEUE_H
#define _QUEUE_H
#ifndef DATATYPE
#define DATATYPE int
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
typedef struct LL_NODE {
DATATYPE data;
struct LL_NODE* next;
} node_t;
typedef struct QUEUE {
node_t* head;
node_t* tail;
int size;
} queue_t;
queue_t* init_queue();
void destroy(queue_t* queue);
bool is_empty(queue_t* queue);
int size(queue_t* queue);
void enqueue(queue_t* queue, DATATYPE data);
DATATYPE dequeue(queue_t* queue);
DATATYPE peek(queue_t* queue);
#endif
binary_search_tree.c
#include "binary_search_tree.h"
void bfs_trav(binary_tree_node_t* root) {
queue_t* queue = init_queue();
binary_tree_node_t* temp = root;
enqueue(queue, root);
while (!is_empty(queue)) {
temp = dequeue(queue);
printf("%d ", temp->data);
if (temp->left) {
enqueue(queue, temp->left);
}
if (temp->right) {
enqueue(queue, temp->right);
}
}
destroy(queue);
return;
}
binary_search_tree.h
#ifndef _BINARY_SEARCH_TREE_H
#define _BINARY_SEARCH_TREE_H
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "../queue/queue.h"
typedef struct BINARY_TREE_NODE {
int data;
struct BINARY_TREE_NODE *left;
struct BINARY_TREE_NODE *right;
} binary_tree_node_t;
void bfs_trav(binary_tree_node_t* root);
#endif
One popular way to do what you're trying to do is to nest your data structures and thereby make "polymorphic" types. In your case, that would mean removing the payload from the LL_NODE struct entirely and declare various "derived" structs as needed. For example:
typedef struct LL_NODE {
struct LL_NODE* next;
} node_t;
typedef struct INT_NODE {
node_t node;
int payload;
} int_node_t;
typedef struct TREE_NODE {
node_t node;
struct BINARY_TREE_NODE *payload;
} tree_node_t;
Any time you want to work with the linked list simply pass a pointer to the node struct member for any derived type.
Of course for this to be useful, you'll need a way to "downcast" from a linked list node to a derived type. This is usually done with a macro that will look roughly as follows:
#define LIST_NODE_DOWNCAST(ptr, derivedType, nodeName) \
(derivedType *)((ptrdiff_t)ptr - (ptrdiff_t)&((derivedType *)0)->nodeName)
So for example, given a node_t pointer called list_ptr, that you know refers to an int_node_t object, you can recover the int_node_t object as follows:
int_node_t *derived = LIST_NODE_DOWNCAST(list_ptr, int_node_t, node);
Note that this is the primary method used in the Linux kernel for managing queues and lists. I answered a related question a while ago where you can see a specific example in usage in the kernel.
I would suggest (quite strongly) that the makefile is the wrong place to make that change. You should be setting the data type in the BST code before including queue.h:
…
#define DATATYPE struct BINARY_TREE_NODE *
#include "queue/queue.h"
…
Your binary tree code knows what it wants to use; it should not rely on the vagaries of the makefile to ensure it gets what it needs.
Whether you should add #undef DATATYPE before defining it is trickier; I'd not do it unless it was proven necessary — and I'd also debate why it's necessary and how you are going to have two sets of functions with the same names but working with different types, given that this is C and C++. On the whole, not using #undef is safer; you will be told if there is a problem which using #undef will conceal — and the concealed problems will be hell to debug!
In the below code,
/**********linkedListImpl.c ***********/
#include"list/list.h"
#if defined(LINKED_LIST)
/***************** Representation - start ******************/
/* struct members are not visible to other .c files */
static struct DListNode{
void *item;
struct DListNode *next;
struct DListNode *prev;
};
/* Should be used in this .c file, only, so static */
static typedef struct DListNode DListNode;
static DListNode* createNode(void *);
static struct List{
DListNode *head;
int size; /*size attribute is not part of list definition, but quick way
to help user code */
}List;
.....
#endif
/************ list.h ************/
#ifndef LIST_H /* Header guard */
#define LIST_H
#include"type.h"
/***************** Usage-start ************/
#if defined(ARRAY) || (LINKED_LIST)
typedef struct List List;
#else
#error "Wrong list implementation macro name !!!"
#endif
...
#endif /* LIST_H */
List type is declared in list.h, using staticspecifier,
typedef struct List List;
and defined in linkedListImpl.c, using staticspecifier,
static struct List{
DListNode *head;
int size;
}List;
Purpose: To make List symbol available to user(main.c) only through a pointer(List*). User(main.c) should not be able to access List members in main.c.
In linkedListImpl.c Symbol DListNode is defined using static specifier,
static struct DListNode{
void *item;
struct DListNode *next;
struct DListNode *prev;
};
static typedef struct DListNode DListNode;
Purpose: For symbol DListNode, user(main.c) should neither be able to access DListNode members in main.c nor access DListNode object through DListNode* pointer.
Is this the right approach to hide the symbols List & DListNode?
Note: Here is the complete code
Yes, this is it. It is called opaque pointer, and is used to hide the implementation details in the interface.
You may be interested by What is an opaque pointer in C?
static struct DListNode { ... }; is useless (it declares no variables of struct DListNode), but do declare the struct DListNode so is exactly the same as simply struct DListNode { ... };. It is like static int; declaring a list of 0 integer variables (notice that static int x,y; is a common way of declaring a list of two variables x & y).
A very common way is to define all your struct (and their fields) in public header files. Indeed, you are showing implementation details (but the compiler needs them to emit code).
A less common way is to declare a struct in a header file, and only use pointers to it (an opaque pointer, like Burns' answer). Then you might have an implementation defining the members of that struct. See also this.
A common example is the FILE from <stdio.h>. It is very likely to be defined as some struct but the field names are conventionally hidden and unused.
C programming is a lot about having good conventions and following them.
What you have will work.
The typedef you have in the header file also acts as a forward declaration of the structure. This is sufficient to pass around a pointer to the struct type but prevents the pointer from being dereferenced.
This allows you to keep the implementation details of the struct private to the .c file were it is defined.
When you need to create opaque references in plain C the best way is to create 2 separate sets of headers, one with full declarations that will be used in sources that implement the 'methods', and another header with the opaque reference for user use.
This is the common strategy used in many OS's. Take a look to MS headers, they use HANDLEs to refer to objects. The HANDLES are created and passed to system functions, plain C simulation of C++ methods, that resides in modules using the full definition header.
Consider these 2 headers:
/* Internal header "InnerDList.h" */
struct DListNode{
void *item;
struct DListNode *next;
struct DListNode *prev;
};
2nd
/* User header "DList.h" */
typedef struct DListNode DListNode;
DListNode *CreateDList(void * data, ...);
HRESULT AddDListNode(DListNode *Dlist, void *data);
HRESULT GetDListNode(DListNode *Dlist, void *SearchData);
...
Using the first allow the compilation of the functions that works on the DList structures, the second allows the use of DList's through an opaque object.
A variation to allow the use of full or partial definition could be the following: define a preprocessor symbol in the internal header and use this to conditionally define opaque objects.
/* Internal header "InnerDList.h" */
#define INNERDLIST 1 //We will use this symbol to modify user header
typedef struct DListNode{
void *item;
struct DListNode *next;
struct DListNode *prev;
} DListNode;
#include "DList.h" //Now include user header
In the User header:
/* User header "DList.h" */
#ifndef INNERDLIST
typedef struct DListNode DListNode; //define only if not called by internal header
#endif
DListNode *CreateDList(void * data, ...);
HRESULT AddDListNode(DListNode *Dlist, void *data);
HRESULT GetDListNode(DListNode *Dlist, void *SearchData);
...
I have the following struct in node.c:
typedef struct node {
char something[1024];
char onething[1024];
struct node *next;
} LList;
How would I define it in node.h?
Simply move that code to the header file, and include node.h in node.c.
I'm a noob at C and need some help getting my code to...welll...do anything. I have the following put into a .h file.
typedef struct
{
int active;
int dead;
ucontext_t t;
struct node *link;
}gtthread_t;
struct node{
struct gtthread_t thread;
};
typedef struct
{
int rubic;
}gtthread_mutex_t;
This is in a .h file... a .h file where I have had to #include ucontext.h ... which I know I am also not supposed to do... but it's the only way I can access ucontext_t, I find.
So, my error here is "field thread has incomplete type".
Why? What does that even mean? And how can I possible NOT import ucontext.h if I want to declare structs with that kind of data in the .h file?
Has nothing to do with your other include. This has to do with the fact that your first struct is anonymous and has a type name gtthread_t. C unlike C++ distinguishes between gtthread_t and struct gtthread_t.
Either name the struct:
struct gtthread_t
{
int active;
int dead;
ucontext_t t;
struct node *link;
};
Or change the type name to the typedef:
struct node{
gtthread_t thread;
};
The ucontext.h header only pre-declares the ucontext_t type. You're only supposed to use pointers to it, since its size is not known. Change your code to:
ucontext_t *t;
and you will be fine. Notice that all the functions in the ucontext.h header use such pointers to refer to contexts. This is common in C API design, since it allows the representation to be hidden inside the API's implmentation, which is nice.
One problem is that struct node has not been declared yet, so the gtthread_t type cannot be completed.
typedef struct
{
int active;
int dead;
ucontext_t t;
struct node *link;
}gtthread_t;
struct node{
struct gtthread_t thread;
};
You'll need a forward declaration of struct node:
struct node;
typedef struct
{
int active;
int dead;
ucontext_t t;
struct node *link;
}gtthread_t;
struct node{
struct gtthread_t thread;
};
Try that and see if it makes a difference.
I have a catalog.h file with this
typedef struct node* list_node;
struct node
{
operationdesc op_ptr;
list_node next;
};
and a parser.h with this
#include "catalog.h"
int parse_query(char *input, list_node operation_list);
Both headers have #ifndef, #define, #endif.
The compiler gives me this error: expected declaration specifiers or ‘...’ before ‘list_node’ on the parse_query line.
What's the matter?
I tried to put the typedef in parser.h, and it's fine. Why do I get this error when the typedef is in catalog.h?
The error is this (from your comment):
I had an #include "parser.h" in the catalog.h. I removed it, and now it compiles normally...
Assuming that #include "parser.h" was before the typedef in catalog.h, and you have a source file that includes catalog.h before parser.h, then at the time the compiler includes parser.h, the typedef isn't available yet. It's probably best to rearrange the contents of the header files so that you don't have a circular dependency.
If this isn't an option, you can ensure that any source files that include these two files include parser.h first (or only).
Try this for catalog.h:
typedef struct node_struct {
operationdesc op_ptr;
struct node_struct* next;
} node;
typedef node* list_node;