Multiple header & source files and its use

Thread Starter

Kittu20

Joined Oct 12, 2022
463
I have no idea how to use multiple headers and source files in the big project. I can create one source file and header file in project. I want to understand how to create multiple headers and source files in the big project

I don't have any specific case in mind but I have written following codes and I would like to create multiple headers and source files

Program to sort array numbers in increasing order

C:
#include <stdio.h>

int main()
{
   int i = 0;
   int j = 0;
   int temp;

   int array[5] = { 50, 40, 30, 20, 10};

   for ( i = 0; i < 5; i++)
   {
        for ( j = i + 1; j < 5; j++)
        {
            temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }

   }

   for ( i = 0; i < 5; i++)
   {
       printf(" %d ", array[i]);
   }
 
    return 0;

}
Program Output
Code:
 10  20  30  40  50
Program to Remove duplicates value with 0 in array
C:
#include<stdio.h>
int main()
{
    int array[5] = { 1, 2, 1, 2, 5};

    for ( int i = 0; i < 5; i ++)
    {
        for ( int j = i+1; j < 5; j++)
        {
            if ( array[i] == array[j])
            {
                array[j] =0;
            }
        }
    }

    for ( int i = 0; i < 5; i ++)
    {
        printf("%d ", array[i]);
    }
    return 0;
}
Code:
1 2 0 0 5
Note : If you don't like my use case for multiple header and source files, please suggest something suitable. @WBahn
 
Last edited:

WBahn

Joined Mar 31, 2012
29,978
Note : If you don't like my use case for multiple header and source files, please suggest something suitable. @WBahn
You had a perfectly good case with your linked-list problem.

Create a separate source file for each object. One file for a NODE and one file for a LIST. Each file has a header that needs to get included in any file that uses those objects. That allows you to keep the file with your main program clean and also allows you to easily use your link-list functionality in other programs.

Here's the main program.

Code:
// listtest.c

#include <stdio.h>
#include <stdlib.h>

#include "LIST.h"

int main(void)
{
    LIST *mylist;
    
    mylist = LIST_new();
    
    printf("Adding nodes to list\n");
    LIST_insert(mylist, 1);
    LIST_insert(mylist, 2);
    LIST_insert(mylist, 3);
    LIST_insert(mylist, 4);
    LIST_insert(mylist, 5);
    LIST_insert(mylist, 6);
    
    LIST_print(mylist);
    printf("\n");
    
    printf("Removing nodes from list\n");
    LIST_remove(mylist, 6);  // Delete first item in list
    LIST_remove(mylist, 1);  // Delete last item in list
    LIST_remove(mylist, 4);  // Delete item in middle of list
    LIST_remove(mylist, 9);  // Delete item not in list
    
    LIST_print(mylist);
    printf("\n");
    
    LIST_del(mylist);     // Delete entire list
    
    return 0;
}
Since I want to use a linked list, I include LIST.h to give me access to everything I need to work with my linked-list objects. Note that I do NOT include NODE.h. As far as the programmer is concerned, I am working with a linked list and everything I need to have should be made available by including the header file for linked lists.

Here's the LIST.h that it includes.

Code:
// LIST.h

#ifndef LIST_DOT_H
#define LIST_DOT_H

#include "NODE.h"

typedef struct LIST LIST;

LIST *LIST_new(void);
void LIST_del(LIST *p);

void LIST_insert(LIST *p, int value);
void LIST_remove(LIST *p, int value);

NODE *LIST_head(LIST *p);
NODE *LIST_updateHead(LIST *p, NODE *head);

void LIST_print(LIST *p);

#endif
Notice that LIST.h includes NODE.h. This is because one or more of the function prototypes uses NODE objects, so the user needs access to at least the NODE typedef.

Code:
// NODE.h

#ifndef NODE_DOT_H
#define NODE_DOT_H

typedef struct NODE NODE;

NODE *NODE_new(int value);
void NODE_del(NODE *p);

NODE *NODE_next(NODE *p);
NODE *NODE_updateNext(NODE *p, NODE *nextNode);

int NODE_value(NODE *p);

void NODE_print(NODE *p);

#endif
The source code behind LIST and NODE objects the user never needs to see. In fact, I could compile them into library files that just get linked in. But here they are.

Code:
// NODE.c

#include <stdio.h>
#include <stdlib.h>

#include "NODE.h"

struct NODE
{
    NODE *next;
    int data;
};

// GET/SET Functions
// These are the ONLY functions allowed to deference a LIST pointer

NODE *NODE_getNext(NODE *p)
{
    return p? p->next : NULL;
}

NODE *NODE_setNext(NODE *p, NODE *next)
{
    if (p)
        p->next = next;
    return NODE_getNext(p);
}

int NODE_getData(NODE *p)
{
    return p? p->data : -1;
}

int NODE_setData(NODE *p, int v)
{
    if (p)
        p->data = v;
    return NODE_getData(p);
}

// API Functions
// These are the functions that the user has access to

NODE *NODE_new(int value)
{
    NODE *p = (NODE *) malloc(sizeof(NODE));
    
    if (p)
    {
        NODE_setNext(p, NULL);
        NODE_setData(p, value);
    }
    
    return p;
}

void NODE_del(NODE *p)
{
    if (p)
        free(p);
}

NODE *NODE_next(NODE *p)
{
    return NODE_getNext(p);
}

NODE *NODE_updateNext(NODE *p, NODE *nextNode)
{
    return NODE_setNext(p, nextNode);
}

int NODE_value(NODE *p)
{
    return NODE_getData(p);
}

void NODE_print(NODE *p)
{
    if (!p)
    {
        printf("[--]");
        return;
    }

    printf("[%2i]", NODE_getData(p));
}
Code:
// LIST.c

#include <stdio.h>
#include <stdlib.h>

#include "LIST.h"

struct LIST
{
    NODE *first;
};

// GET/SET Functions
// These are the ONLY functions allowed to deference a LIST pointer
NODE *LIST_getFirst(LIST *p)
{
    return p? p->first : NULL;
}

NODE *LIST_setFirst(LIST *p, NODE *first)
{
    if (p)
        p->first = first;
    return LIST_getFirst(p);
}

// API Functions
// These are the functions that the user has access to
LIST *LIST_new(void)
{
    LIST *p = (LIST *) malloc(sizeof(LIST));
    
    if (p)
    {
        LIST_setFirst(p, NULL);
    }
    
    return p;
}

void LIST_del(LIST *p)
{
    if (!p)
        return;
         
    NODE *thisNode = LIST_head(p);
    
    while (thisNode)
    {
        NODE *temp = thisNode;
        thisNode = NODE_next(thisNode);
        free(temp);
    }
    
    free(p);
}

NODE *LIST_head(LIST *p)
{
    return LIST_getFirst(p);
}

NODE *LIST_updateHead(LIST *p, NODE *head)
{
    return LIST_setFirst(p, head);
}

void LIST_insert(LIST *p, int value)
{
    if (!p)
        return;

    NODE *newNode = NODE_new(value);
    
    NODE_updateNext(newNode, LIST_head(p));
    LIST_updateHead(p, newNode);
    
    return;
}

void LIST_remove(LIST *p, int value)
{
    if (!p)
        return;
    
    NODE *priorNode = NULL;
    NODE *thisNode = LIST_getFirst(p);
    
    while ( thisNode && (NODE_value(thisNode) != value) )
    {
        priorNode = thisNode;
        thisNode = NODE_next(thisNode);
    }
    
    if (thisNode) // A node was found
    {
        if (priorNode) // It is NOT the first node
            NODE_updateNext(priorNode, NODE_next(thisNode));
        else
            LIST_updateHead(p, NODE_next(thisNode));
        
        NODE_del(thisNode);
    }
    
}

void LIST_print(LIST *p)
{
    if (!p)
        return;
        
    NODE *thisNode = NULL;
    
    thisNode = LIST_getFirst(p);

    while (thisNode)
    {
        NODE_print(thisNode);
        thisNode = NODE_next(thisNode);
    }

}
 

WBahn

Joined Mar 31, 2012
29,978
You can work with the example you are put forth.

The thing to remember is that your program has to have exactly one main() function. The idea with multiple source files is that the other files contain function definitions that are used by the code in other files.

So turn your code that sorts and array and that removes duplicates (we'll assume that replacing duplicates with zero makes sense) into functions. Put those functions in another file, perhaps arrayfunctions.c, and put prototypes for those functions in arrayfunctions.h. The include that header file in the file containing your main.

A common way of developing multi-file code is to first develop it in a single file, putting the functions below main and the prototypes above it. Then, when you are happy with the code, just move the code to the correct files and add an include directive.
 

dl324

Joined Mar 30, 2015
16,845
You can partition your code however you like. More files can make it easier for a multi-user collaboration (you'll likely need to use software for revision control). If you have code that is rarely changed, you can compile to object code and link that with the code that changes frequently. If you have many object files that you want to revise independently, you could put the object code into one or more libraries.

Once you start using more library functions, you'll need more header files:
Code:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

void main() {
 

Thread Starter

Kittu20

Joined Oct 12, 2022
463
A common way of developing multi-file code is to first develop it in a single file, putting the functions below main and the prototypes above it. Then, when you are happy with the code, just move the code to the correct files and add an include directive.
I have created everything in a single file. Now I want to create separate header and separate source file.

C:
#include <stdio.h>

//prototype
void increasing_order ( int array1[] );
void Remove_duplicate_zero( int array2[]);

int main()
{

   int array1[5] = { 50, 40, 30, 20, 10};
   int array2[5] = { 1, 2, 1, 2, 5};
     
   increasing_order(array1);
   printf("\n");
   Remove_duplicate_zero(array2);
 
    return 0;

}

void increasing_order ( int array1[] )
{
    int i = 0;
    int j = 0;
    int temp;
    for ( i = 0; i < 5; i++)
    {
        for ( j = i + 1; j < 5; j++)
        {
            temp = array1[i];
            array1[i] = array1[j];
            array1[j] = temp;
        }

   }

   for ( i = 0; i < 5; i++)
   {
       printf("%d ", array1[i]);
   }

}

void Remove_duplicate_zero( int array2[])
{
   int i = 0;
   int j = 0;

    for ( int i = 0; i < 5; i ++)
    {
        for ( int j = i+1; j < 5; j++)
        {
            if ( array2[i] == array2[j])
            {
                array2[j] =0;
            }
        }
    }

    for ( int i = 0; i < 5; i ++)
    {
        printf("%d ", array2[i]);
    }
 
}
Program Output
Code:
10 20 30 40 50
1 2 0 0 5
 

WBahn

Joined Mar 31, 2012
29,978
But what happens if you decide to change your array so that it has 10 elements?

Or what if your program has two arrays, one that has five elements and one that has ten?

You want to write your function so that it is as general purpose as possible, which means that your function needs a way to know how long the array is. Since there is no way for the function to determine that from the array itself, this means you should pass a second argument telling the function how many elements are in the array.

Second, do you want a function that removes sorts an array to always print out the array? Probably not.

Write your functions so that they do one well-defined thing. If you want to print out an array, write a function that prints out an array.
 

dl324

Joined Mar 30, 2015
16,845
I have created everything in a single file
How you partition your code is a matter of style. I have an 800 line program that is in two files. I used a header file for all of the functions and put all of the other code in main. Others might have had main simply call a lot of other functions. That makes the code look very hierarchical, but those calls add overhead that affects performance. So you need to decide if you're after something that looks good, better performance, or something in between.
 

Thread Starter

Kittu20

Joined Oct 12, 2022
463
But what happens if you decide to change your array so that it has 10 elements?

Or what if your program has two arrays, one that has five elements and one that has ten?

Write your functions so that they do one well-defined thing. If you want to print out an array, write a function that prints out an array.
User can specify how long array he want in main function. I have created a function that can print the value of array in program

maybe the value could be stored using scanf but i didn't goo too deep

C:
#include <stdio.h>


void Print_Array( int array[], int size)
{
    for ( int i = 0; i < size; i++)
    {
        printf("%d ", array[i]);
    }
}

void increasing_order ( int array1[], int size )
{
    int i = 0;  int j = 0; int temp;
    for ( i = 0; i < size; i++)
    {
        for ( j = i + 1; j < 5; j++)
        {
            temp = array1[i];
            array1[i] = array1[j];
            array1[j] = temp;
        }
   }

   Print_Array(array1, size);
}

void Remove_duplicate_zero( int array2[], int size)
{
   int i = 0;
   int j = 0;

    for ( int i = 0; i < size; i ++)
    {
        for ( int j = i+1; j < size; j++)
        {
            if ( array2[i] == array2[j])
            {
                array2[j] =0;
            }
        }
    }
   Print_Array(array2, size);
}


int main()
{

   int array1[5] = { 50, 40, 30, 20, 10};
   int array2[5] = { 1, 2, 1, 2, 5};
   
   increasing_order(array1, 5);
   printf("\n");
   Remove_duplicate_zero(array2, 5);

    return 0;

}
10 20 30 40 50
1 2 0 0 5
 
Last edited:

Thread Starter

Kittu20

Joined Oct 12, 2022
463
How you partition your code is a matter of style. I have an 800 line program that is in two files. I used a header file for all of the functions and put all of the other code in main. Others might have had main simply call a lot of other functions. That makes the code look very hierarchical, but those calls add overhead that affects performance. So you need to decide if you're after something that looks good, better performance, or something in between.
Please look at post #10 of following thread in which i have created separate c file and a header file. this is not a meaningful example
https://forum.allaboutcircuits.com/threads/c-program-compilation-process.191531/

I want to understand how to create multiple header file and source file for project.
 

dcbingaman

Joined Jun 30, 2021
1,065
Another thing to keep in mind for well behaved code; if a function or 'global' variable is not required by higher level code, it should be declared static in the .c file where it is needed thus avoiding cluttering the global namespace and it should not be exposed in the associated .h file: only and absolutely only the functions and types that are required by the higher level should be exposed. The entire idea is to reduce coupling to a minimum making it easy to reuse code modules in the future and avoid 'reinventing' the wheel.
 
Last edited:

dcbingaman

Joined Jun 30, 2021
1,065
I don't know what you mean by higher level code.

Static variable inside the function defines the lifetime of the variable and static variable outside the function defines the scope of the variable
C:
/*******************************************************************************
*******************************************************************************/
#include "Config.h"
#include "Debug.h"
#include "PCCommunicationsHandler.h"
#include "ClosedLoopISVCurrentControl.h"
#include "SleepLib.h"
#include "AnalogInputs.h"
#include "IdleControlStateMachine.h"
#include "ParametricData.h"
#include "RPMMonitor.h"
#include "DigitalInputs.h"
#include "RefClockGenerator.h"
#include "Status1To3AndScope1To2Multiplexer.h"
#include "ErrorInterface.h"
#include "OverCurrentCPLDControl.h"

/*******************************************************************************
*******************************************************************************/
int main()
{
    OverCurrentCPLDControl_Init();  // transparent mode out the door
    ErrorInterface_Init();
    RefClockGenerator_Init();
    DigitalInputs_Init();
    ParametricData_Init();
    PCCommunicationsHandler_Init();
    ClosedLoopISVCurrentControl_Init();
    SleepLib_Init();
    AnalogInputs_Init();
    IdleControlStateMachine_Init();
    RPMMonitor_Init();
    Status1To3AndScope1To2Multiplexer_Init();   
    Debug_Init();
    while(1)
    {
        PCCommunicationsHandler_Update();
    }
    return 0;
}

// EOF *************************************************************************
Very simple: Higher level code 'uses' lower level interfaces (code). Above the higher level code is just main(). It 'uses' the header files from 'lower level' code. Lower level code knows nothing about main() but main has to know at least about the interfaces to lower level code so it can take advantage of them.
 

Thread Starter

Kittu20

Joined Oct 12, 2022
463
This is how i seperated the source file

main.c
C:
#include <stdio.h>
#include"temp.h"

int main()
{

   int array1[5] = { 50, 40, 30, 20, 10};
   int array2[5] = { 1, 2, 1, 2, 5};
 
   increasing_order(array1, 5);
   printf("\n");
   Remove_duplicate_zero(array2, 5);

    return 0;

}
temp.h
Code:
void Print_Array( int array[], int size);
void increasing_order ( int array1[], int size );
void Remove_duplicate_zero( int array2[], int size);
temp.c
C:
#include<stdio.h>

void Print_Array( int array[], int size)
{
    for ( int i = 0; i < size; i++)
    {
        printf("%d ", array[i]);
    }
}

void increasing_order ( int array1[], int size )
{
    int i = 0;  int j = 0; int temp;
    for ( i = 0; i < size; i++)
    {
        for ( j = i + 1; j < 5; j++)
        {
            temp = array1[i];
            array1[i] = array1[j];
            array1[j] = temp;
        }
   }

   Print_Array(array1, size);
}

void Remove_duplicate_zero( int array2[], int size)
{
   int i = 0;
   int j = 0;

    for ( int i = 0; i < size; i ++)
    {
        for ( int j = i+1; j < size; j++)
        {
            if ( array2[i] == array2[j])
            {
                array2[j] =0;
            }
        }
    }
   Print_Array(array2, size);
}
Output
Code:
10 20 30 40 50
1 2 0 0 5
 

WBahn

Joined Mar 31, 2012
29,978
User can specify how long array he want in main function. I have created a function that can print the value of array in program

maybe the value could be stored using scanf but i didn't goo too deep

C:
#include <stdio.h>


void Print_Array( int array[], int size)
{
    for ( int i = 0; i < size; i++)
    {
        printf("%d ", array[i]);
    }
}

void increasing_order ( int array1[], int size )
{
    int i = 0;  int j = 0; int temp;
    for ( i = 0; i < size; i++)
    {
        for ( j = i + 1; j < 5; j++)
        {
            temp = array1[i];
            array1[i] = array1[j];
            array1[j] = temp;
        }
   }

   Print_Array(array1, size);
}

void Remove_duplicate_zero( int array2[], int size)
{
   int i = 0;
   int j = 0;

    for ( int i = 0; i < size; i ++)
    {
        for ( int j = i+1; j < size; j++)
        {
            if ( array2[i] == array2[j])
            {
                array2[j] =0;
            }
        }
    }
   Print_Array(array2, size);
}


int main()
{

   int array1[5] = { 50, 40, 30, 20, 10};
   int array2[5] = { 1, 2, 1, 2, 5};
  
   increasing_order(array1, 5);
   printf("\n");
   Remove_duplicate_zero(array2, 5);

    return 0;

}
10 20 30 40 50
1 2 0 0 5
So change your code in main() (and ONLY in main()) to make array1 contain ten elements, counting back from 100 to 10, and see what happens.
 

dcbingaman

Joined Jun 30, 2021
1,065
If you want to create your own library of functions similar to this...

1674513175495.png
You may want to consider prefixing your library with something that makes it unique to avoid name clashes with other libraries and to make it clear when using your own library. For example, my initials are ddb so if it was my functions I would probably have them like the following:

void ddblib_PrintArray(...);
void ddblib_increasing_order(...)

etc.

And then place them in something like...

ddblib_array_functions.h

or similar

Also, if you are going to make a library of functions for reuse be sure to have a good set of unit tests that test all conditions for your library. WBhan brought this up in his previous post. You need a good set of tests to verify they work. The last thing you want is to be running into problems with a library that has issues.
 

WBahn

Joined Mar 31, 2012
29,978
C:
int array1[10] = { 50, 40, 30, 20, 10};
I didn't understand the meaning of above line so can you tell me what do you mean
Change it to this:

C:
int array1[10] = { 100, 90, 80, 70, 60, 50, 40, 30, 20, 10};
Run it and you will see that your function does NOT sort the new array properly.

You need to get in the habit of stress testing your code.
 

Thread Starter

Kittu20

Joined Oct 12, 2022
463
Change it to this:

C:
int array1[10] = { 100, 90, 80, 70, 60, 50, 40, 30, 20, 10};
Run it and you will see that your function does NOT sort the new array properly.

You need to get in the habit of stress testing your code.
i found my mistake, Now it sort array for 10 elements

C:
C:
#include<stdio.h>

void Print_Array( int array[], int size)
{
    for ( int i = 0; i < size; i++)
    {
        printf("%d ", array[i]);
    }
}

void increasing_order ( int array1[], int size )
{
    int i = 0;  int j = 0; int temp;
    for ( i = 0; i < size; i++)
    {
        for ( j = i + 1; j < size; j++)
        {
            temp = array1[i];
            array1[i] = array1[j];
            array1[j] = temp;
        }
   }

   Print_Array(array1, size);
}
 

WBahn

Joined Mar 31, 2012
29,978
When you call the sqrt() function from the math library, do you want it to always print out the square root of the argument you sent it?

If not, then why do you think it's a good idea to have a function whose purpose is to sort the elements in an array print them out? Don't you think it's possible, even likely, that you will have arrays that all you want to do is sort them so that you can do something else with them. Are you really sure that you will ALWAYS want to print them out? Even if they have millions of elements in them?
 

Thread Starter

Kittu20

Joined Oct 12, 2022
463
When you call the sqrt() function from the math library, do you want it to always print out the square root of the argument you sent it?

If not, then why do you think it's a good idea to have a function whose purpose is to sort the elements in an array print them out? Don't you think it's possible, even likely, that you will have arrays that all you want to do is sort them so that you can do something else with them. Are you really sure that you will ALWAYS want to print them out? Even if they have millions of elements in them?
The main objective of this thread was to learn what is the advantage of multiple header files and source files. I have shown some programs which I have solved just for practice. Of course they can be improved but that would separate topic
 

WBahn

Joined Mar 31, 2012
29,978
The main objective of this thread was to learn what is the advantage of multiple header files and source files. I have shown some programs which I have solved just for practice. Of course they can be improved but that would separate topic
While I understand your point, they aren't as separate as you might think. The primary advantage of multi-file programs is problem decomposition and modularity. What you put into a file and what you put into a function are two aspects of the same thing. As you strengthen your ability to do one, you improve your ability to do the other. The inverse is also true -- if you let one stay weak, it weakens your ability to do the other. So if we don't point out bad practices, it will reinforce the notion in your mind that they are good practices.
 
Top