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 File | Key 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, orsigned 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.
