Skip to content

2.2 Dive into C

1. Header Files

A header file in C is a file with a .h extension that contains declarations of functions, variables, macros, and data types, which can be shared across multiple .c source files. The actual definitions (i.e., the implementation of functions) go into .c files, while header files allow the code to be reused and organized efficiently.

1.1 What Goes in a Header File?

  1. Function Declarations (Prototypes):
  • These specify the function signature but not the body. They allow the compiler to know about a function’s existence before it is defined.
// Example of function declaration in a header file
void greet(void);
// Note that the function definition (function signature + body)
// is in the correponding .c file
  1. Macros

    • Common macros, constants, or inline code can be defined in a header file.
    #define PI 3.14159
    #define SQUARE(x) (x * x)
  2. Data Type Definitions (typedef):

    • Custom data types can be declared in the header for reuse in various .c files.
typedef struct {
int x;
int y;
} Point;
  1. Global Variables Declarations:
    • Declaring external global variables that are defined in other .c files.
extern int global_counter;
  1. Structure Definitions:
    • You can define structures, unions, and enums that need to be shared between files.
typedef struct {
char name[50];
int age;
} Person;
  1. Include/Header Guards:
    • Header files typically use include/header guards to prevent multiple inclusions in a single compilation unit, which would lead to redefinition errors.
#ifndef MYHEADER_H
#define MYHEADER_H
// Declarations and definitions go here
#endif

1.2 How to Use a Header File

  1. Create a Header File:
    • You create a .h file (e.g., myheader.h) that contains the declarations needed by other files.
myheader.h
#ifndef MYHEADER_H
#define MYHEADER_H
void greet(void); // Function declaration
#endif
  1. Include the Header in Source Files:
    • In your .c source files, you include the header file with the #include directive.
main.c
#include <stdio.h>
#include "myheader.h" // Including the custom header file
int main() {
greet(); // Calling the function declared in the header
return 0;
}
  1. Provide Definitions in a Separate .c File:
    • The actual implementation of the functions declared in the header goes in the .c file.
myheader.c
#include <stdio.h>
#include "myheader.h"
void greet(void) {
printf("Hello, World!\n");
}

1.3 Why Use Header Files?

  1. Code Reusability: Functions and variables declared in a header file can be used in multiple .c files.
  2. Code Organization: Header files help organize large programs by separating declarations from implementation.
  3. Modularity: Changes to a declaration in a header file automatically propagate to all files that include it.

2. Static Functions / Variables

In C, the static keyword is used to limit the scope and lifetime of both functions and variables. Its behavior differs slightly depending on whether it is applied to a function or a variable.

2.1 Static Functions

A static function is a function that is limited in scope to the file where it is defined. This means that a static function cannot be called from other files, even if they include the function’s declaration in a header file.

  • File-level scope: A static function is visible only within the file it is defined in, making it “private” to that file.

Example

file1.c
#include <stdio.h>
static void myFunction() {
printf("This is a static function.\n");
}
int main() {
myFunction(); // This works because we're in the same file.
return 0;
}
// file2.c
extern void myFunction();
int main() {
myFunction(); // Error: myFunction is not visible here due to static.
return 0;
}

In this example, myFunction() is declared static, so it is accessible only within file1.c. Attempting to use it in file2.c results in an error.

Another Example

#include <stdio.h>
void checkDate();
/* You can declare this function (prototype) in any
other .c file and use it without any issue. */
void nonstatic_testMain()
{
printf("nonstatic_testMain\n");
}
/* This function is private to main.c file. Cannot be
declared (prototype) and used in any other .c file.
An error will be reported by the linker if you do so
because the linker cannot locate the definition in
main.c file. Remember it is hidden! therefore util.c
cannot see it! */
static void static_testMain()
{
printf("nonstatic_testMain\n");
}
/* static function declaration (prototype) can be moved
to a seperate header file. But still, static will still
make the function private to where it is defined */
int main()
{
checkDate();
return 0;
}
/* compile the code with
gcc -Wall -ansi -pedantic main.c util.c -o prog */

2.2 Static Variables

2.2.1 Static Local Variables

A static local variable inside a function retains its value across multiple calls to the function. Normally, a local variable’s value is lost when the function exits, but a static variable persists for the lifetime of the program.

  • Persistence: The value of a static local variable is retained between function calls.
  • Initialization: Static local variables are initialized only once, and if not explicitly initialized, they are set to 0 by default.

Example of Static Local Variable:

#include <stdio.h>
void incrementCounter() {
static int counter = 0; // This variable is initialized only once
counter++;
printf("Counter: %d\n", counter);
}
int main() {
incrementCounter(); // Output: Counter: 1
incrementCounter(); // Output: Counter: 2
incrementCounter(); // Output: Counter: 3
return 0;
}

Here, counter is a static local variable, so it retains its value across multiple calls to incrementCounter().

Another Example

#include <stdio.h>
int add()
{
/* This statement will only be execute once and once only
when the function add() is invoked for the first time.
The static local variable num will stay alive until the
end of the program (not the function-end). */
static int num = 0;
num = num + 1;
printf("Result: %d\n", num); /* will print 1, 2, 3 if add()
is invoked thrice */
return num;
}
int add2()
{
static int num;
num = 0;
num = num + 1;
printf("Result: %d\n", num); /* will print 1, 1, 1 if add()
is invoked thrice */
return num;
}
int main()
{
checkDate();
add();
add();
add();
add2();
add2();
add2();
return 0;
}

2.2.2 Static Global Variables

A static global variable (a variable declared outside of any function but marked as static) has its scope limited to the file in which it is declared, similar to static functions.

  • File-level scope: The static global variable can only be accessed within the file where it is declared.

Example

file1.c
#include <stdio.h>
static int globalCounter = 0; // This variable is limited to this file.
void incrementGlobalCounter() {
globalCounter++;
printf("Global counter: %d\n", globalCounter);
}
// file2.c
extern int globalCounter; // This won't work because globalCounter is static.
int main() {
globalCounter = 10; // Error: globalCounter is not visible here.
return 0;
}

Here, globalCounter is static, so it cannot be accessed from file2.c.

2.3 Key Differences

  1. Scope:

    • Static Function: Only visible within the file where it’s defined.
    • Static Local Variable: Visible only within the function, but persists across function calls.
    • Static Global Variable: Visible only within the file where it’s defined.
  2. Lifetime:

    • Static Local Variable: Retains its value between function calls.
    • Static Global Variable: Exists for the duration of the program but is confined to the file scope.

2.4 Use Cases

  • Static functions: Useful for encapsulating functions and preventing them from being used outside the file, ensuring file-level modularity.
  • Static variables: Useful when you need to maintain state across function calls (static local) or restrict the visibility of global variables to the file (static global).