How to use multiple source and header files
I recently learned how we can use multiple source files with header files to make our code portable and hierarchical. For this, I tried to create my tree program using this principle. Here are my files
b_tree_ds.h - This will contain a tree node tree data structure declaration that can be called for different functions implementing different tree functionality (which can be in different source files)
typedef struct node {
struct node* left;
struct node* right;
int key; // contains value
}NODE;
When I try to add extern like in typedef extern struct node
it gives error for multiple storage classes, but if I miss it I get error for multiple definitions.
Here are my other source files
traverse.h - contains the traverse function declaration
void traverse_print (NODE* p);
Here also I am getting error for unknown NODE id
traverse.c - contains the definition for this function
#include <stdio.h>
#include "b_tree_ds.h"
#include "traverse.h"
void traverse_print(NODE* p)
{
if(p->left != NULL)
{
traverse_print(p->left);
}
if (p->right != NULL)
{
traverse_print(p->right);
}
printf ("\n%d",p->key);
}
Finally main.c
#include <stdio.h>
#include "traverse.h"
void main()
{
// input
NODE p;
printf("\nInput the tree");
input_tree (&p);
printf("\n\nThe tree is traversing ...\n")
traverse_print(&p);
}
void input_tree (NODE *p)
{
int in;
int c;
NODE *temp;
printf("\n Enter the key value for p: ");
scanf("%d", &in);
p->key =in;
printf ("\n\nIn relation to node with value %d",in);
printf ("Does it have left child (Y/N): ")
if ((c = getchar()) == Y);
{
//assign new memory to it.
temp = (NODE *)malloc(sizeof(NODE));
input_tree(temp);
}
printf ("\n\nIn relation to node with value %d",p->key);
printf ("\nDoes it have right child (Y/N): ")
if ((c = getchar()) == Y);
{
//assign new memory to it.
temp = (NODE *)malloc(sizeof(NODE));
input_tree(temp);
}
}
This is my first attempt at this kind of practice, please suggest structuring my program to be good or should I try something else.
source to share
You may be in trouble because you don't have a compelling reason to split things up yet. A good reason will help you determine which parts belong together and which are separate. So start with a simpler approach.
Divide the program into three files main.c, which contains main (), node.h, a header that provides declarations common to the entire program and therefore understood by the compiler and node.c, functions that control the structure of the NODE.
Put typedef ... NODE;
all the function declarations that manipulate the NODE in one header file node.h too. This way, you can combine existing header files into one and call it node.h.
As Joop Eggen recommends, place #ifndef _NODE_H_ ... #endif
node.h around the content to protect it from accidentally including # twice.
Check out this file with a minimal main.c file containing:
#include "node.h"
int main() { return 0; }
and compile it. This should not lead to compilation errors. If it contains errors, the error is in the header file.
Place the functions that control the NODE in a file called node.c, which will initially be:
#include "node.h"
compile and link this with main.c (gcc main.c node.c) and there should be no errors.
Create a program - these are the steps by adding code to main.c file, node.c file and add function declarations to node.c file in node.h. Add small amounts of code and compile often (with warnings included, like gcc -Wall main.c node.c) and check that it does what you expect.
The program will eventually be completed.
source to share
I recommend looking at What are external variables in C? ...
You can enable system headers such as <stdio.h>
without worrying about whether other headers are needed to consume your services. In the same way, you should create your own headers. You should also prevent errors if your file is included multiple times (accidentally or intentionally).
You have:
-
b_tree_ds.h
typedef struct node { struct node* left; struct node* right; int key; // contains value } NODE;
To the point, it's okay; you just need to wrap it in header defenders so that re-enabling it doesn't hurt.
#ifndef B_TREE_DS_H_INCLUDED #define B_TREE_DS_H_INCLUDED typedef struct node { struct node* left; struct node* right; int key; // contains value } NODE; #endif /* B_TREE_DS_H_INCLUDED */
You note:
When I try to add
extern
like intypedef extern struct node
it gives error for multiple storage classes, but if I miss it I get error for multiple definitions.Syntactically,
extern
,static
,auto
,register
andtypedef
- all storage classes, and you can have only one storage class specified in the declaration. This is why you are getting a multiple memory class error. The "multiple definition" error will remain a problem until C2011 is widespread and header defenders become a problem. I think the title defenders will remain valuable even after C2011 is widely available. -
traverse.h
void traverse_print (NODE* p);
As it stands, you can't just write
#include "traverse.h"
to take advantage of its capabilities. This should be avoided whenever possible. (See: Self-sufficent Header Files in C and C ++ , What is good reference documentation for using h files in C, and Should #include be used in headers .) So this should includeb_tree_ds.h
:#ifndef TRAVERSE_H_INCLUDED #define TRAVERSE_H_INCLUDED #include "b_tree_ds.h" extern void traverse_print(NODE *p); #endif /* TRAVERSE_H_INCLUDED */
You can omit a heading that includes security elements in that heading (assuming it
b_tree_ds.h
's self-defense), but it's easier to be self-consistent in all headings.Another possible method can be mentioned:
#ifndef TRAVERSE_H_INCLUDED #define TRAVERSE_H_INCLUDED typedef struct node NODE; extern void traverse_print(NODE *p); #endif /* TRAVERSE_H_INCLUDED */
This makes the
NODE
type opaque; the header usertraverse.h
doesn't know anything about what's inNODE
. There are coordination problems that solve this problem less broadly.
With these headings changes, then:
-
traverse.c
need to be enabledtraverse.h
(and possibly include it before any other header to allow an automatic self-condensation test), but - If it
traverse.c
contains both headers, there is no problem, regardless of the order in which they are included (and it does not matter if the repetition is direct or indirect). - Yours
main.c
can only includetraverse.h
as shown and will be OK. With source code, since itmain.c
only includedtraverse.h
andtraverse.h
not includedb_tree_ds.h
, the code won't compile correctly.
source to share
Forget extern. In traverse.h, you must include b_tree_ds.h. Some compilers have a pragma to turn on once, but don't corrupt the contents of b_tree_ds.h with:
#ifndef B_TREE_DS_H
#define B_TREE_DS_H
...
#endif // B_TREE_DS_H
Look at the compiler information in this case as well as the precompiled headers.
The above is a platform independent way of excluding content a second time.
source to share