C Header, Multifile, Cmdargs

Header Files in C

A header file is a file with a .h extension that contains function declarations (prototypes), macro definitions, constants, and type definitions. It allows sharing these declarations across multiple source files without rewriting them.

Think of a header file like a menu card — it lists what functions are available without showing how each one is written inside.

Standard Header Files

Header FileKey Functions / Contents
<stdio.h>printf, scanf, fopen, fclose, fgets, fprintf
<stdlib.h>malloc, calloc, free, exit, atoi, rand, abs
<string.h>strlen, strcpy, strcmp, strcat, strstr
<math.h>sqrt, pow, floor, ceil, fabs, sin, cos, log
<ctype.h>isalpha, isdigit, isupper, toupper, tolower
<time.h>time, clock, difftime, strftime
<stdbool.h>bool, true, false (C99+)
<limits.h>INT_MAX, INT_MIN, CHAR_MAX, LONG_MAX constants
<errno.h>errno variable, error codes

Creating a Custom Header File

Custom header files help organize reusable functions into a library for a project. Three files are needed: the header, the implementation, and the main program.

mathutil.h — Header File (declarations only)


// mathutil.h
#ifndef MATHUTIL_H      // include guard — prevents double inclusion
#define MATHUTIL_H

#define PI 3.14159

int   square(int n);
int   cube(int n);
float circleArea(float r);

#endif

mathutil.c — Implementation File (function bodies)


// mathutil.c
#include "mathutil.h"

int   square(int n)        { return n * n; }
int   cube(int n)          { return n * n * n; }
float circleArea(float r)  { return PI * r * r; }

main.c — Main Program


// main.c
#include <stdio.h>
#include "mathutil.h"   // include custom header with double quotes

int main() {
    printf("Square of 5    = %d\n",   square(5));
    printf("Cube of 3      = %d\n",   cube(3));
    printf("Area of circle = %.2f\n", circleArea(7.0));
    return 0;
}

Compile both files together:


gcc main.c mathutil.c -o program
./program

Output


Square of 5    = 25
Cube of 3      = 27
Area of circle = 153.94

Include Guards — Why They Matter

Without include guards, if two files include the same header, the compiler sees duplicate declarations and throws an error. The guard pattern prevents this:


#ifndef HEADER_NAME_H
#define HEADER_NAME_H
// ... all declarations go here ...
#endif

Multi-File Programs in C

As programs grow larger, splitting them into multiple .c files keeps the code organized. Each file handles one concern of the program. All files are compiled separately and linked together by the linker.

Typical Project Structure


project/
├── main.c          ← entry point, calls other functions
├── student.h       ← struct and function declarations
├── student.c       ← student-related functions
└── utils.c         ← utility/helper functions

student.h


#ifndef STUDENT_H
#define STUDENT_H

typedef struct {
    char name[50];
    int  marks;
} Student;

void  printStudent(Student s);
float calcAverage(int marks[], int n);

#endif

student.c


#include <stdio.h>
#include "student.h"

void printStudent(Student s) {
    printf("Name: %-15s Marks: %d\n", s.name, s.marks);
}

float calcAverage(int marks[], int n) {
    int sum = 0;
    for (int i = 0; i < n; i++) sum += marks[i];
    return (float)sum / n;
}

main.c


#include <stdio.h>
#include <string.h>
#include "student.h"

int main() {
    Student s1, s2;

    strcpy(s1.name, "Alice");  s1.marks = 92;
    strcpy(s2.name, "Bob");    s2.marks = 78;

    printStudent(s1);
    printStudent(s2);

    int marks[] = {80, 90, 75, 88, 95};
    printf("Class Average: %.1f\n", calcAverage(marks, 5));

    return 0;
}

Compile all files:


gcc main.c student.c -o program
./program

Output


Name: Alice           Marks: 92
Name: Bob             Marks: 78
Class Average: 85.6

extern Keyword — Sharing Global Variables

To share a global variable across multiple files, define it in one file and declare it with extern in others:


// config.c
int maxStudents = 50;   // defined here

// main.c
extern int maxStudents; // declared here (no memory allocation)
printf("Max: %d\n", maxStudents);  // uses the value from config.c

Command Line Arguments

A C program can receive values directly from the terminal when it is launched. These are called command line arguments. They are passed through two special parameters of main():

  • argc (argument count) — total number of arguments including the program name itself
  • argv (argument vector) — array of strings, each element is one argument

int main(int argc, char *argv[]) { ... }

Example — Print All Arguments


#include <stdio.h>

int main(int argc, char *argv[]) {
    printf("Total arguments: %d\n", argc);
    for (int i = 0; i < argc; i++) {
        printf("argv[%d] = %s\n", i, argv[i]);
    }
    return 0;
}

Running


./program Hello World 42

Output


Total arguments: 4
argv[0] = ./program
argv[1] = Hello
argv[2] = World
argv[3] = 42

Practical Example — Simple Calculator via Command Line


#include <stdio.h>
#include <stdlib.h>   // for atof()

int main(int argc, char *argv[]) {
    if (argc != 4) {
        printf("Usage: ./calc <num1> <op> <num2>\n");
        printf("Example: ./calc 10 + 5\n");
        return 1;
    }

    float a  = atof(argv[1]);
    char  op = argv[2][0];
    float b  = atof(argv[3]);

    switch (op) {
        case '+': printf("Result: %.2f\n", a + b); break;
        case '-': printf("Result: %.2f\n", a - b); break;
        case '*': printf("Result: %.2f\n", a * b); break;
        case '/':
            if (b != 0) printf("Result: %.2f\n", a / b);
            else printf("Error: Division by zero!\n");
            break;
        default: printf("Unknown operator!\n");
    }
    return 0;
}

Running and Output


./calc 10 + 5    →  Result: 15.00
./calc 20 / 4    →  Result: 5.00
./calc 7 * 3     →  Result: 21.00

Bit Fields in C Structures

Bit fields allow packing multiple small integer values inside a single structure word. They are used in systems programming, hardware register definitions, embedded systems, and network protocol headers — anywhere memory is extremely limited.

A bit field is declared with a colon followed by the number of bits to use.

Syntax


struct Name {
    unsigned int member : number_of_bits;
};

Example — Hardware Status Register


#include <stdio.h>

struct DeviceStatus {
    unsigned int powerOn   : 1;   // 1 bit  → 0 or 1
    unsigned int errorFlag : 1;   // 1 bit  → 0 or 1
    unsigned int mode      : 3;   // 3 bits → 0 to 7
    unsigned int channel   : 4;   // 4 bits → 0 to 15
    unsigned int reserved  : 7;   // 7 bits → padding
};  // total = 16 bits = 2 bytes packed into 4-byte int

int main() {
    struct DeviceStatus dev;

    dev.powerOn   = 1;
    dev.errorFlag = 0;
    dev.mode      = 5;
    dev.channel   = 12;

    printf("Power ON   : %d\n",   dev.powerOn);
    printf("Error Flag : %d\n",   dev.errorFlag);
    printf("Mode       : %d\n",   dev.mode);
    printf("Channel    : %d\n",   dev.channel);
    printf("Struct size: %zu bytes\n", sizeof(dev));

    return 0;
}

Output


Power ON   : 1
Error Flag : 0
Mode       : 5
Channel    : 12
Struct size: 4 bytes

Without bit fields, four separate int variables would need 16 bytes. The bit field packs them all into 4 bytes — a 75% memory saving.

Bit Field Rules

  • Bit field members must be of type int, unsigned int, or signed int.
  • A bit field cannot span across two different storage units.
  • Address-of operator (&) cannot be applied to a bit field member.
  • Bit fields without names are used for padding/alignment.

Useful Math Library — <math.h>


#include <stdio.h>
#include <math.h>

int main() {
    printf("sqrt(144) = %.1f\n",  sqrt(144));   // 12.0
    printf("pow(2,10) = %.0f\n",  pow(2, 10));  // 1024
    printf("floor(4.9)= %.1f\n",  floor(4.9));  // 4.0
    printf("ceil(4.1) = %.1f\n",  ceil(4.1));   // 5.0
    printf("fabs(-7)  = %.1f\n",  fabs(-7.0));  // 7.0
    printf("log(100)  = %.4f\n",  log10(100));  // 2.0000
    return 0;
}

Compile with math library flag:


gcc program.c -o program -lm

Summary

Header files with include guards keep declarations organized and shareable across source files. Multi-file programs break large applications into logical modules, making them easier to maintain and collaborate on. Command line arguments allow programs to receive input at launch, making them flexible tools. Bit fields enable memory-efficient packing of small values inside structures — essential in embedded systems and hardware-level programming. Together, these techniques represent the way real-world professional C programs are built and structured.

Leave a Comment

Your email address will not be published. Required fields are marked *