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.

+3


source to share


3 answers


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.

+1


source


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 in typedef 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

    and typedef

    - 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 include b_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 user traverse.h

    doesn't know anything about what's in NODE

    . There are coordination problems that solve this problem less broadly.

With these headings changes, then:

  • traverse.c

    need to be enabled traverse.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 include traverse.h

    as shown and will be OK. With source code, since it main.c

    only included traverse.h

    and traverse.h

    not included b_tree_ds.h

    , the code won't compile correctly.
+1


source


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.

0


source







All Articles