0% found this document useful (0 votes)
4 views54 pages

C Programming

Error handling in C is performed manually by developers using various methods such as checking return values, using global variables like errno, and functions like perror() and strerror(). C lacks built-in exception handling, so programmers must implement error checks for file operations and other runtime errors. The document also discusses using the goto statement for error handling, along with common errors encountered during file operations.

Uploaded by

Hema Shree
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views54 pages

C Programming

Error handling in C is performed manually by developers using various methods such as checking return values, using global variables like errno, and functions like perror() and strerror(). C lacks built-in exception handling, so programmers must implement error checks for file operations and other runtime errors. The document also discusses using the goto statement for error handling, along with common errors encountered during file operations.

Uploaded by

Hema Shree
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Error Handling

Unlike other programming languages that have automatic error handling, In


C language error handling is to be manually done by the developers using
error-handling methods, debugging strategies, and functions like perror(),
strerror(), etc.

Error Handling in C
Last Updated : 6 Aug, 2025




In C programming, error handling is typically done using functions that
handle runtime errors, returning error codes or messages to notify the
programmer about the failure or incorrect operation.
 Since C does not provide built-in exception handling like other high-
level languages (e.g., try-catch in Java or Python), error handling relies
heavily on function return values, global variables, and system calls.
 A lot of C function calls return -1 or NULL or set an in case of an error
code as the global variable errno, so quick tests on these values are
easily done with an instance of ‘if statement’.
What is errno?
errno is a global variable defined in the <errno.h> header file that
indicates the error that occurred during a function call in C. When a
function fails, the errno variable is automatically set to a specific error
code, which helps identify the type of error encountered. Different values
of errno correspond to different types of errors, providing useful
information for error handling in C programs.

#include <errno.h>
#include <stdio.h>

int main() {

// If a file is opened which does not exist,


// then it will be an error and corresponding
// errno value will be set
FILE* fp;

// opening a file which does not exist


fp = fopen("[Link]", "r");
printf("Value of errno: %d\n", errno);

return 0;
}

Output
Value of errno: 2
Below is a list of a few different errno values and their corresponding
meaning:
errno
value Error

1 Operation not permitted

2 No such file or directory

3 No such process

4 Interrupted system call

5 I/O error

6 No such device or address

7 The argument list is too long

8 Exec format error

9 Bad file number

10 No child processes
errno
value Error

11 Try again

12 Out of memory

13 Permission denied

Different Methods for Error Handling

Different methods are used to handle different kinds of errors in C. Some


of the commonly used methods are:

1. Using if-else

In C, error handling is done manually since there is no built-in try-


catch block like in other programming languages. To manage errors, we
can use if-else statements to check for conditions and handle any
potential errors that may occur during program execution.
Example:

#include <errno.h>
#include <stdio.h>

int main() {
FILE* fp;

// opening a file which does not exist


fp = fopen("[Link]", "r");

if(fp == NULL){
printf("File openning error");
}else{
printf("File open successfully");
}
return 0;
}

Output
File openning error
Explanation: In the above program, we try to open a file in read mode
and use an if-else statement to check if the file was successfully opened.
If the file cannot be opened, the program will display a "File opening
error" message.

2. perror()

The perror() function is used to print an error message to the standard


error stream (stderr). It helps to display the error string based on the
global errno variable, which stores the error code set by system calls and
library functions.
Example

#include <errno.h>
#include <stdio.h>
#include <string.h>

int main(){
FILE* fp;

// Try opening a non-existent file, which sets errno


fp = fopen("[Link]", "r");

// Print the errno value after failed file opening


printf("Value of errno: %d\n", errno);
perror("Message from perror");

return 0;
}

Output
Value of errno: 2
Message from perror: No such file or directory
Explanation: This code attempts to open a non-existent file
using fopen(), which sets the errno variable with an error code. It then
prints the value of errno and uses perror() to display a descriptive error
message related to the failure, helping the user understand the cause of
the error, such as "No such file or directory."

3. strerror()

The strerror() function is also used to show the error description. This
function returns a pointer to the textual representation of the
current errno value.
Example

#include <errno.h>
#include <stdio.h>
#include <string.h>

int main() {
FILE* fp;

// Try opening a non-existent file, setting errno


fp = fopen("[Link]", "r");

// Print errno value and corresponding error message


printf("Value of errno: %d\n", errno);
printf("The error message is : %s", strerror(errno));

return 0;
}

Output
Value of errno: 2
The error message is : No such file or directory
Explanation: The code attempts to open a non-existent file, sets errno on
failure, and then prints both the error code and the corresponding error
message using strerror().

4. ferror()

The ferror() function is used to check if an error occurred during a file


operation. It returns a non-zero value if there was an error during the file
operation.
Example
#include <stdio.h>

int main() {
FILE *fptr = fopen("[Link]", "w");

// Write data to the file


fprintf(fptr, "Hello, GFG!");

// Check error after writing data into file


if(ferror(fptr)==0)
printf("Data written successfully.");
fclose(fptr);
return 0;
}

Output
Data written successfully.
Explanation: This code attempts to open a file and handles potential
errors using perror() if the file can't be opened. It reads the file character
by character, checks for errors during reading using ferror(), and prints an
appropriate message. The file is closed at the end, and the program exits
with a status indicating success or failure.

5. feof()

The feof() function checks whether the end of a file has been reached
during reading operations. It helps to identify when there is no more data
to read from the file.
Example

#include <stdio.h>

int main () {
FILE *fp = fopen("[Link]","r");
if (fp == NULL)
return 0;

do {
// Taking input single character at a time
char c = fgetc(fp);

// Checking for end of file


if (feof(fp))
break ;

printf("%c", c);
}while(1);

fclose(fp);
return(0);
}

[Link]
Welcome to GeeksforGeeks
Output
Welcome to GeeksforGeeks
Explanation: In this code, feof() is used to check if the end of the file
(EOF) has been reached while reading the file character by character
using fgetc(). If feof(fp) returns true, the while loop breaks, stopping
further reading of the file.

clearerr()

The clearerr() function is used to clear the error and EOF flags for a
stream. It allows recovery from errors and allows the stream to be reused
for further operations.
Example

#include <stdio.h>

int main() {
FILE *fptr = fopen("[Link]", "w+");
fprintf(fptr, "GeeksForGeeks!");
while (fgetc(fptr) != EOF);

if(feof(fptr)){
printf("EOF ancounter \n");
}

// Reset EOF using clearerr


clearerr(fptr);
if(!feof(fptr)){
printf("Reset the EOF successfully");
}

fclose(fptr);
return 0;
}

Output
EOF ancounter
Reset the EOF successfully
Explanation: The code opens a file in read mode, handles potential
errors during the file opening and operations, clears any error indicators
with clearerr(), and finally closes the file.
Exit Status
C programs use the exit() function to terminate the program and return a
status code to the operating system. The C standard specifies two
constants: EXIT_SUCCESS and EXIT_FAILURE, that may be passed to
exit() to indicate successful or unsuccessful termination, respectively.
These are macros defined in <stdlib.h> header file.
Example

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

int main(){
FILE* fp;

// Attempt to open a non-existent file in binary mode


fp = fopen("[Link]", "rb");

if (fp == NULL) {
printf("Value of errno: %d\n", errno);
printf("Error opening the file: %s\n",
strerror(errno));
perror("Error printed by perror");

// Exit the program with failure status


exit(EXIT_FAILURE);

// This line will not be printed because of exit()


printf("I will not be printed\n");
}

// If the file is opened successfully


else {
fclose(fp);
exit(EXIT_SUCCESS);
printf("I will not be printed\n");
}
return 0;
}

Output
Value of errno: 2
Error opening the file: No such file or directory
Error printed by perror: No such file or directory
Explanation: The code attempts to open a file using fopen(). If the file
doesn't exist, fopen() returns NULL, and the program prints the error
using errno, strerror(), and perror() to provide detailed information about
the failure. The program then exits with a failure status
using exit(EXIT_FAILURE). If the file is successfully opened, it is closed
and the program exits with a success status (exit(EXIT_SUCCESS)), but
no further code is executed after the exit() calls.
Error Handling without Predefined Methods
In the above discussion, we covered error handling using built-in methods,
but we can also handle some errors without relying on these methods,
such as division by zero, input validation, file opening errors, out-of-
range array access, and more.
Handling divide by zero errors is essential to avoid program crashes or
undefined behavior. You can check for a zero divisor before performing
division to prevent this error.
Example

#include <stdio.h>

int main() {
int num1 = 10, num2 = 0;

if (num2 == 0)
printf("Error: Division by zero is not allowed\n");
else
printf("Result: %d", num1 / num2);
return 0;
}
Output
Error: Division by zero is not allowed
Explanation: The program checks if the divisor num2 is zero before
performing the division. If it is, it prints an error message instead of
performing the division.

Using goto for Exception Handling in C


Last Updated : 13 Jan, 2025




Exceptions are runtime anomalies or abnormal conditions that a program
encounters during its execution. C doesn’t provide any specialized
functionality for this purpose like other programming languages such as
C++ or Java. However, In C, goto keyword is often used for the same
purpose. The goto statement can be used to jump from anywhere to
anywhere within a function.
Let's take a look at an example:

#include <stdio.h>

int main() {
FILE *file = NULL;

// Attempt to open the file


file = fopen("[Link]", "r");
if (file == NULL) {
printf("Error opening file\n");

// Jump to the error section if the file couldn't be


opened
goto error;
}
printf("File opened successfully!\n");
fclose(file);
return 0;

error:
// Error handling section
printf("Exiting\n");
return 1;
}

Output
Error opening file
Exiting
Explanation: The program attempts to open a file called [Link],
and if it fails (i.e., fopen() returns NULL), it jumps to the error label
using goto, prints an error message, and returns 1 to indicate failure. If
successful, it prints a success message, closes the file, and returns 0.
Why Use goto for Exception Handling?
The goto statement in C provides a way to jump to a labeled part of the
code. While generally discouraged in modern programming due to
readability and maintainability concerns, goto can be a clean solution for
error handling in specific scenarios like:
 Cleaning up allocated resources.
 Breaking out of nested loops or blocks.
 Handling errors in a single exit path.
Examples of goto for Exception Handling
The following examples demonstrate the use of goto for exception
handling in C:

Simulate try-catch Statements

The try catch statements are used for exception handling in C++ and
Java.

#include <stdio.h>
int main() {
FILE *file = NULL;
int result = 0;

file = fopen("[Link]", "r");


if (file == NULL) {
printf("Error opening file\n");
// Jump to the error label if file cannot be opened
goto error;
}

// Read data (simulating an error)


result = fread(NULL, 1, 100, file);
if (result == 0) {
printf("Error reading file\n");

// Jump to error if reading fails


goto error;
}

// Process data (simulating a successful operation)


printf("Successfull\n");

// Close the file and exit


fclose(file);
return 0;

error:

// Error handling section


if (file != NULL) {
fclose(file);
}
return 1;
}

Output
Error opening file

Handling Exception in File Processing

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

int processFile(const char *filename) {


FILE *file = NULL;
char *buffer = NULL;

// Open the file


file = fopen(filename, "r");
if (!file) {
fprintf(stderr, "Error: Failed to open file '%s'\n",
filename);
goto cleanup;
}

// Allocate memory for the buffer


buffer = (char *)malloc(1024);
if (!buffer) {
fprintf(stderr, "Error: Memory allocation failed\n");
goto cleanup;
}

// Simulate reading file content


if (fread(buffer, 1, 1024, file) == 0) {
fprintf(stderr, "Error: Failed to read file\n");
goto cleanup;
}

printf("File processed successfully.\n");

cleanup:
// Cleanup resources
if (buffer) free(buffer);
if (file) fclose(file);

// Return error status


return (file && buffer) ? 0 : -1;
}

int main() {
const char *filename = "[Link]";

if (processFile(filename) != 0) {
fprintf(stderr, "An error occurred while"
"processing the file.");
return 1;
}

return 0;
}

Output
Error: Failed to open file '[Link]'
An error occurred whileprocessing the file.

Limitations of goto in Exception Handling


Though it works fine, goto have some limitations as compared to the
specialized exception handling structures.
 Overuse can make the code harder to follow especially in larger
codebases.
 Misuse of goto may lead to bugs or spaghetti code.
 Techniques like returning error codes or using modern C libraries (e.g.,
setjmp and longjmp) can sometimes be better.
Conclusion
While goto is often considered outdated or bad practice, it can be a
practical tool for exception handling in C, especially when used judiciously
for managing cleanup and errors. By following best practices, such as
limiting its scope and keeping the code readable, goto can be a valuable
part of a C programmer's toolkit.

Error Handling During File Operations in C


Last Updated : 6 Aug, 2025




File operations are a common task in C programming, but they can
encounter various errors that need to be handled gracefully. Proper error
handling ensures that your program can handle unexpected situations,
such as missing files or insufficient permissions, without crashing. In this
article, we will learn how to handle some common errors during file
operations in C.
Here are some common errors that can occur during file operations:

Error Cause

File Not Found Trying to open a file that doesn’t exist.

Permission
Insufficient permissions to access the file.
Denied

Disk Full No space left on the disk for writing data.

File Already Attempting to create a file that already exists in w mode.


Error Cause

Exists

Invalid File
Using a null or invalid file pointer for file operations.
Pointer

End-of-File (EOF) Attempting to read past the end of the file.

Attempting to perform operations on a file that wasn’t opened


File Not Open successfully.

Failure to check for errors then the program may behave abnormally
therefore an unchecked error may result in premature termination for the
program or incorrect output.

Error Handling Techniques


Below are some standard error handling techniques:

1. File Not Found Error


A file not found error can occur when opening a file in read mode (r) or
append mode (a). Use fopen() and check for NULL. If it is, the error
message can be printed using perror() function.
#include <stdio.h>

int main() {

// Try to open file in


// read mode
FILE *file = fopen("[Link]", "r");

// Check if the file


// is opened/found
if (file == NULL) {
perror("Error");
return 1;
}
fclose(file);
return 0;
}

Output
Error: No such file or directory
In the above program, fopen() returns a NULL pointer because the file is
not present in the current directory, then the perror() function prints the
error message.

2. Handle Permission Denied Error


If the file exists but the program lacks the required permissions, fopen()
will fail and return NULL pointer. We can change the perror() output to
"permission denied" as shown in the below snippet.
FILE *file = fopen("/restricted/[Link]", "w");
if (file == NULL) {
perror("Permission denied");
}

3. Handle Disk Full Error

When writing to a file, ensure the disk has enough space. Errors during
file operations can be detected using ferror(). In the below program, we
assume that there is no space in memory to store any data.
#include <stdio.h>

int main() {
FILE *fptr = fopen("[Link]", "w");
if (fptr == NULL) {
perror("Error opening file");
return 1;
}

fprintf(fptr, "Writing to file");

// Check error after performing


// write operation
if (ferror(fptr)) {
perror("Error writing to file");
}
fclose(fptr);
return 0;
}

Output
Error writing to file: Permission Denied

4. Handle File Already Exists

When creating a new file with fopen() in w mode, the existing file will be
overwritten. To avoid this, we open a new file in wx mode because if file is
already present then fopen() return NULL and set the EEXIST value to
the errno. In the below program, we assume that "[Link]" file is already
present in current directory.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main() {
FILE *fptr;

// Try to open the file in


// write mode
fptr = fopen("[Link]", "wx");

if (fptr == NULL) {

// Check if the error is


// due to file already existing
if (errno == EEXIST)
printf("File already exist");
}

// If we reach here, the file


// was created successfully
fprintf(fptr, "This is a new file.");
fclose(fptr);
return 0;
}

Output
File already exist

5. Handle Invalid File Pointer

Always verify that the file pointer is not NULL before performing
operations like reading or writing.

FILE *file = NULL;


if (file == NULL) {
printf("Invalid file pointer. File operations cannot
proceed.\n");
}
6. Handle End-of-File (EOF)

When we are reading data from a file and the file pointer reaches the end
of the file, we can use the feof() function to handle the end of the file.
#include <stdio.h>

int main() {
FILE *file = fopen("[Link]", "r");

// Check for eof while reading


char ch;
while ((ch = fgetc(file)) != EOF)
putchar(ch);

// Use feof() to make sure


// EOF occurred or not
if (feof(file))
printf("End of file reached.");
else if (ferror(file))
printf("Error reading the file.");
fclose(file);
return 0;
}

Output
End of file reached.

7. Handle File Not Open

Whenever we attempt to open a file and the file cannot be opened due to
some error, the fopen() function returns NULL. We can handle this easily
using an if-else statement.
#include <stdio.h>
int main() {
FILE *file = fopen("[Link]", "r");
if (file == NULL) {
printf("File could not be opened.\n");
} else {
printf("File opened successfully.\n");
fclose(file);
}

return 0;
}
Output
File could not be opened.
File Closing Error

Sometimes, when we are closing a file using the fclose() function and it
fails to close the file due to an error, it returns -1.

#include <stdio.h>

int main() {
FILE *fptr = fopen("[Link]", "w");

fprintf(fptr, "Writing to file");

// Check file close properly


if(fclose(fptr) == -1)
printf("File closing error");
else
printf("File closed");
return 0;
}
Output
File closing error

C Program to Handle Divide by Zero


Last Updated : 23 Jul, 2025




In C programming, there is no built-in exception handling like in other
high-level languages such as C++, Java, or Python. However, you can still
handle exceptions using error checking, function return values, or by
using signal handlers.
There are 2 methods to handle divide-by-zero exception mentioned below:
 Manually Checking before Division
 Signal handling
Manually Checking before Division
The most used method is to check if the divisor (number that divides the
dividend) in the division is zero or not using if else statement.
Example:
#include <stdio.h>
#include <float.h>

int main() {
float a = 10, b = 0;
float res;

// Check division by zero


if(b == 0){
printf("Error: Division by zero");
}else{
res = a / b;
printf("%f", res);
}
return 0;
}

Output
Error: Division by zero
Explanation: The program checks for division by zero using if (b ==
0) before performing the division. If b is 0, it prints an error message.
Using Signal Handling
Signal handling can be used to catch runtime exceptions like divide-by-
zero errors. In case of floating-point errors, SIGFPE (Floating Point
Exception) is raised. We can create a signal handler function and assign it
to SIGFPE using signal() function. The setjump and longjmp can be
used to jump to the previous valid state of the program, allowing you to
handle the exception and resume execution. The SIGFPE can be cleared
by using the feclearexcept and fetestexcept functions from the fenv.h
header
Example:

#include <fenv.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <float.h>
jmp_buf recovery;

void handle_divide_by_zero(int sig) {

// Re-assign the signal handler


signal(SIGFPE, handle_divide_by_zero);
printf("Error: Division by zero\n");

// Jump to the recovery point


longjmp(recovery, 1);
}
int main() {
double a = 10, b = 0, res;
int recovery_status;

// Assign the signal handler


signal(SIGFPE, handle_divide_by_zero);

// Set a recovery point


recovery_status = setjmp(recovery);
if (recovery_status == 0) {
res = a / b;
if(fetestexcept(FE_DIVBYZERO)) {
feclearexcept(FE_DIVBYZERO);
raise(SIGFPE);
}
else {
printf("%f", res);
}
}
return 0;
}

Output
inf
Explanation: Program executes step by step as follow:
 The program sets up a signal handler for SIGFPE to catch floating-
point exceptions like division by zero.
 It attempts to divide a by b, where b is 0, which will cause a floating-
point exception.
 If the division by zero exception occurs and raise(SIGFPE) will trigger
the signal handler (handle_divide_by_zero()).
 The signal handler prints an error message and then uses longjmp() to
return to the recovery point, avoiding the program crashing.
 The program can then proceed without further errors (or cleanup
operations), as it has recovered from the division by zero.
Miscellaneous Concepts
This section explores various essential of C language that do not fit into a
single category but play a vital role in C programming and provide
advanced functionality to your program.

C Preprocessors
Last Updated : 20 Aug, 2025




Preprocessors are programs that process the source code before the
actual compilation begins. They are not part of the compilation process
but operate separately, allowing programmers to modify the code before
compilation.
 It is the first step that the C source code goes through when being
converted into an executable file.
 Main types of Preprocessor Directives are Macros, File Inclusion,
Conditional Compilation and Other directives like #undef, #pragma, etc.
 Mainly these directives are used to replace a given section of C code
with another C code. For example, if we write "#define PI 3.14", then PI
is replaced with 3.14 by the preprocessor.
Types of C Preprocessors
All the above preprocessors can be classified into 4 types:

Macros
Macros are used to define constants or create functions that are
substituted by the preprocessor before the code is compiled. The two
preprocessors #define and #undef are used to create and remove
macros in C.
#define token value
#undef token
where after preprocessing, the token will be expanded to its value in the
program.
Example:

#include <stdio.h>

// Macro Definition
#define LIMIT 5
int main(){
for (int i = 0; i < LIMIT; i++) {
printf("%d \n", i);
}
return 0;
}

Output
0
1
2
3
4
In the above program, before the compilation begins, the word LIMIT is
replaced with 5. The word 'LIMIT' in the macro definition is called a
macro template and '5' is macro expansion.
Note There is no semi-colon (;) at the end of the macro definition. Macro
definitions do not need a semi-colon to end.
There are also some Predefined Macros in C which are useful in providing
various functionalities to our program.
A macro defined previously can be undefined using #undef preprocessor.
For example, in the above code,

#include <stdio.h>

// Macro Definition
#define LIMIT 5

// Undefine macro
#undef LIMIT

int main(){
for (int i = 0; i < LIMIT; i++) {
printf("%d \n", i);
}
return 0;
}

Output:
./Solution.c: In function 'main':
./Solution.c:13:28: error: 'MAX' undeclared (first use in
this function)
printf("MAX is: %d\n", MAX);
^
./Solution.c:13:28: note: each undeclared identifier is
reported only once for each function it appears in

Macros With Arguments

We can also pass arguments to macros. These macros work similarly to


functions. For example,
#define foo(a, b) a + b
#define func(r) r * r
Let us understand this with a program:

#include <stdio.h>

// macro with parameter


#define AREA(l, b) (l * b)

int main(){
int a = 10, b = 5;

// Finding area using above macro


printf("%d", AREA(a, b));
return 0;
}

Output
Area of rectangle is: 50
Explanation: In the above program, the macro AREA(l, b) is defined to
calculate the area of a rectangle by multiplying its length (l) and breadth
(b). When AREA(a, b) is called, it expands to (a * b), and the result is
computed and printed.
Please refer Types of Macros in C for more examples and types.
File Inclusion
File inclusion allows you to include external files (header files, libraries,
etc.) into the current program. This is typically done using
the #include directive, which can include both system and user-defined
files.
Syntax
There are two ways to include header files.
#include <file_name>
#include "filename"
The '<' and '>' brackets tell the compiler to look for the file in
the standard directory while double quotes ( " " ) tell the compiler to
search for the header file in the source file's directory.
Example:

// Includes the standard I/O library


#include <stdio.h>

int main() {
printf("Hello World");

return 0;
}

Output
Hello World

Conditional Compilation
Conditional compilation allows you to include or exclude parts of the code
depending on certain conditions. This is useful for creating platform-
specific code or for debugging. There are the following conditional
preprocessor directives: #if, #ifdef, #ifndef, else, #elif and #endif
Syntax
The general syntax of conditional preprocessors is:
#if
// some code
#elif
// some more code
#else
// Some more code
#endif
#endif directive is used to close off the #if, #ifdef, and #ifndef opening
directives.
Example
#include <stdio.h>

// Defining a macro for PI


#define PI 3.14159

int main(){

// Check if PI is defined using #ifdef


#ifdef PI
printf("PI is defined\n");

// If PI is not defined, check if SQUARE is defined


#elif defined(SQUARE)
printf("Square is defined\n");

// If neither PI nor SQUARE is defined, trigger an error


#else
#error "Neither PI nor SQUARE is defined"
#endif

// Check if SQUARE is not defined using #ifndef


#ifndef SQUARE
printf("Square is not defined");

// If SQUARE is defined, print that it is defined


#else
printf("Square is defined");
#endif

return 0;
}

Output
PI is defined
Square is not defined
Explanation: This code uses conditional preprocessor directives (#ifdef,
#elif, and #ifndef) to check whether certain macros (PI and SQUARE)
are defined. Since PI is defined, the program prints "PI is defined", then
checks if SQUARE is not defined and prints "Square is not defined".
Other Directives
Apart from the primary preprocessor directives, C also provides other
directives to manage compiler behaviour and debugging.
#pragma:

Provides specific instructions to the compiler to control its behaviour. It is


used to disable warnings, set alignment, etc.
Syntax
#pragma directive
Some of the #pragma directives are discussed below:
1. #pragma startup: These directives help us to specify the functions
that are needed to run before program startup (before the control
passes to main()).
2. #pragma exit: These directives help us to specify the functions that
are needed to run just before the program exit (just before the control
returns from main()).
Example

#include <stdio.h>
void func1();
void func2();

// specifying funct1 to execute at start


#pragma startup func1

// specifying funct2 to execute before end


#pragma exit func2

void func1() { printf("Inside func1()\n"); }


void func2() { printf("Inside func2()\n"); }
int main(){
void func1();
void func2();
printf("Inside main()\n");

return 0;
}

Output
Inside main()
The above code will produce the output as given above when run on GCC
compilers while the expected output was:
Expected Output
Inside func1()
Inside main()
Inside func2()
This happens because GCC does not support #pragma startup or exit.
However, you can use the below code for the expected output on GCC
compilers.

#include <stdio.h>

void func1();
void func2();

void __attribute__((constructor)) func1();


void __attribute__((destructor)) func2();

void func1()
{
printf("Inside func1()\n");
}

void func2()
{
printf("Inside func2()\n");
}

int main()
{
printf("Inside main()\n");

return 0;
}

Output
Inside func1()
Inside main()
Inside func2()
In the above program, we have used some specific syntaxes so that one
of the functions executes before the main function and the other executes
after the main function.
Macros and its types in C
Last Updated : 15 Jul, 2025




In C programming, a macro is a symbolic name or constant that
represents a value, expression, or code snippet. They are defined using
the #define directive, and when encountered, the preprocessor substitutes
it with its defined content.
Example

#include <stdio.h>

// Macro definition
#define LIMIT 5

int main(){

// Print the value of macro defined


printf("LIMIT: %d", LIMIT);

return 0;
}

Output
LIMIT: 5
Explanation: In this code, a macro LIMIT is defined with the
value 5 using the #define directive. The macro LIMIT is then used in the
printf function to print its value, which is 5. The preprocessor replaces the
macro LIMIT with its defined value before the code is compiled, so the
output of the program will display the value 5.
Syntax
The general syntax to define a macro is:
#define MACRO_NAME value
where MACRO_NAME is the name of the macro and value is the code or
value that replaces the macro name. It is to be noted that macros don't
necessarily need to be in uppercase. Instead, it is a convention that
makes it easy to recognize.
Types Of Macros in C
There are two types of macros in C language:

1. Object-Like Macros
Object-like macros are the simplest type of macros. They replace the
macro name with a defined value or expression. These are used for
constants or simple values.

#include <stdio.h>

// Macro definition
#define DATE 31

int main(){

// Print the message


printf("Lockdown will be extended"
" upto %d-MAY-2020",
DATE);
return 0;
}

Output
Lockdown will be extended upto 31-MAY-2020
Explanation: In this program, the object-like macro DATE is defined
as 31. It is used in the printf function to insert the value 31 into the
message, which the preprocessor replaces before compilation.
2. Chain Macros
These macros involve chaining multiple macros together. This can be
done by combining different macros in a single macro definition, allowing
for more complex operations. In chain macros first of all parent macro is
expanded then the child macro is expanded.

#include <stdio.h>

// Macro definition
#define INSTAGRAM FOLLOWERS
#define FOLLOWERS 138
int main(){
printf("Geeks for Geeks have %dK"
" followers on Instagram",
INSTAGRAM);

return 0;
}

Output
Geeks for Geeks have 138K followers on Instagram
Explanation: INSTAGRAM is expanded first to produce FOLLOWERS.
Then the expanded macro is expanded to produce the outcome
as 138K. This is called the chaining of macros.
3. Multi-Line Macros
These macros span multiple lines for readability and organization. They
are often used when you need a more complex expression or code block.
To create a multi-line macro you have to use backslash \.

#include <stdio.h>

// Multi-line Macro definition


#define ELE 1, \
2, \
3

int main(){

// Array arr[] with elements


// defined in macros
int arr[] = { ELE };
for (int i = 0; i < 3; i++) {
printf("%d ", arr[i]);
}
return 0;
}

Output
1 2 3
Explanation: In this program, a multi-line macro ELE is defined with the
values 1, 2, 3, which is used to initialize the array arr[]. The preprocessor
replaces ELE with these values.
4. Function-Like Macros
These macros take parameters and behave like functions, allowing you to
define reusable logic for common operations. They are expanded at
compile time. A function-like macro is only lengthened if and only if its
name appears with a pair of parentheses after it. If we don't do this, the
function pointer will get the address of the real function and lead to
a syntax error.

#include <stdio.h>

// Function-like Macro definition


#define min(a, b) (((a) < (b)) ? (a) : (b))

int main(){

// Given two number a and b


int a = 18, b = 76;

printf("Minimum: %d", min(a, b));

return 0;
}

Output
Minimum: 18
Explanation: In this code, the function-like macro min(a, b) compares two
values and returns the smaller using the ternary operator. The values 18
and 76 are passed to the macro, which returns 18.

Header Files in C
Last Updated : 13 Sep, 2025




In C programming, a header file is a file that ends with the .h extension
and contains features like functions, data types, macros, etc that can be
used by any other C program by including that particular header file using
"#include" preprocessor.
C language uses header files to provide the standard libraries and their
components for use in programs.
Example:

// Adding standard input and output library by


// including stdio.h header file
#include <stdio.h>

int main() {

// printf() function from stdio.h header file


printf("Hello");
return 0;
}

Output
Hello
In the above program, we have included the stdio.h header file that
provides the standard input and output library in C.
Include Header Files
We have to include header files in our C program to use its features.
There are two ways to do that:
// for header files in system/default
// directory
#include <filename.h>

// for Header files in same directory as


// source file
#include "filename.h"
The #include preprocessor directs the compiler that the header file needs
to be processed before compilation and includes all the necessary data
types and function definitions.
C Header File

Types of C Header Files


There are two types of header files in C:
1. Standard / Pre-existing header files
2. Non-standard / User-defined header files

Standard Header Files in C

Standard header files contain the libraries defined in the ISO standard of
the C programming language. They are stored in the system directory of
the compiler and are present in all the C compilers from any vendor.
There are 31 standard header files in the latest version of C language.
Following is the list of some commonly used header files in C:

Header File Description

It contains information for adding diagnostics that aid program


<assert.h>
debugging.

It is used to perform error handling operations like errno(), strerror(),


<errno.h>
perror(), etc.
Header File Description

It contains a set of various platform-dependent constants related to


floating point values. These constants are proposed by ANSI C.
<float.h>
They make programs more portable. Some examples of constants
included in this header file are- e(exponent), b(base/radix), etc.

It is used to perform mathematical operations


<math.h>
like sqrt(), log2(), pow(), etc.

<signal.h> It is used to perform signal handling functions like signal() and raise().

It is used to perform standard argument functions like va_start() and


va_arg(). It is also used to indicate start of the
<stdarg.h>
variable-length argument list and to fetch the arguments from the
variable-length argument list in the program respectively.

It contains function prototypes for functions that test characters for


certain properties, and also function prototypes for
<ctype.h> functions that can be used to convert uppercase letters to lowercase
letters and vice versa.

It is used to perform input and output operations using functions


<stdio.h>
like scanf(), printf(), etc.

It contains standard utility functions like malloc(), realloc(), etc. It


<setjump.h> contains function prototypes for functions that allow bypassing
of the usual function call and return sequence.

It is used to perform various functionalities related to string


<string.h>
manipulation like strlen(), strcmp(), strcpy(), size(), etc.

<limits.h> It determines the various properties of the various variable types. The
Header File Description

macros defined in this header limits the values of


various variable types like char, int, and long. These limits specify that
a variable cannot store any value
beyond these limits, for example, an unsigned character can store up to
a maximum value of 255.

It is used to perform functions related to date()


and time() like setdate() and getdate(). It is also used to modify the
<time.h>
system date
and get the CPU time respectively.

It contains common type definitions used by C for performing


<stddef.h>
calculations.

It contains function prototypes and other information that enables a


program to be modified for the current locale on which it’s running.
<locale.h> It enables the computer system to handle different conventions for
expressing data such as times, dates, or large numbers throughout the
world.

Example
The below example demonstrates the use of some commonly used
header files in C.

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

int main(){
char s1[20] = "12345";
char s2[10] = "Geeks";
char s3[10] = "ForGeeks";
long int res;

// Find the value of 9^3 using a


// function in math.h library
res = pow(9, 3);
printf("Using math.h, "
"The value is: %ld\n",
res);

// Convert a string to long long int


// using a function in stdlib.h library
long int a = atol(s1);
printf("Using stdlib.h, the string");
printf(" to long int: %ld\n", a);

// Copy the string s3 into s2 using


// using a function in string.h library
strcpy(s2, s3);
printf("Using string.h, the strings"
" s2 and s3: %s %s\n",
s2, s3);
return 0;
}

Output
Using math.h, The value is: 729
Using stdlib.h, the string to long int: 12345
Using string.h, the strings s2 and s3: ForGeeks ForGeeks

Non-Standard Header Files

Non-standard header files are not part of the language's ISO standard.
They are generally all the header files defined by the programmers for
purposes like containing custom library functions etc or provided as
external libraries by different vendors. They are manually installed by the
user or maybe part of the compiler by some specific vendor.
There are lots of non-standard libraries for C language. Some commonly
used non-standard/user-defined header files are listed below:

Header File Description

<conio.h> It contains some useful console functions.

<gtk/gtk.h> It contains GNU's GUI library for C.


Example
The below example demonstrates the use of conio.h non-standard header
file.
#include<stdio.h>
#include<conio.h>

// Function to display a welcome message


void displayMessage() {
printf("Hello! Geek\n");
}
int main() {

// Using conio.h functions


printf("Press any key to print message \n");

// Wait for a key press


getch();

// Call the additional function after a key press


displayMessage();

return 0;
}

Output
Press any key to print message
Hello! Geek

Create your own Header File in C


Instead of writing a large and complex code again and again in different
programs, we can create our own header files and include them in our
program to use whenever we want. It enhances code functionality and
readability. These header files are generally kept inside the source file
directory and included using the second syntax to include header files, but
we can also install them in the compilers default directory.

time.h header file in C with Examples


Last Updated : 12 Jul, 2025




The time.h header file contains definitions of functions to get and
manipulate date and time information. It also includes functions, types,
and macros, which are used by programs to measure time, manipulate
dates, and format time information. It describes three time-related data
types.
1. clock_t: clock_t represents the processor time and is used to measure
the CPU clock cycles.
2. time_t: time_t represents the clock time as an integer which shows the
number of seconds since the beginning of the Unix Period( 1st
January, 1970), which is a part of the calendar time.
3. struct tm: struct tm holds the components of date and time such as
hours, minutes, seconds, day, month, year. The struct tm contains:
struct tm {
// seconds, range 0 to 59
int tm_sec;

// minutes, range 0 to 59
int tm_min;

// hours, range 0 to 23
int tm_hour;

// day of the month, range 1 to 31


int tm_mday;

// month, range 0 to 11
int tm_mon;

// The number of years since 1900


int tm_year;

// day of the week, range 0 to 6


int tm_wday;

// day in the year, range 0 to 365


int tm_yday;

// daylight saving time


int tm_isdst;
}

The time.h header file contains time realted operations like getting the
current time, converting between different time formats. It also
contains CLOCKS_PER_SEC macro which holds the number of times
does the system clock ticks per second.
Functions in Time Library
Function
Name Explanation

This function returns the date and time in the format


day month date hours:minutes:seconds year.
asctime() Eg: Sat Jul 27 11:26:03 2019.
asctime() function returns a string by taking struct tm variable as a
parameter.

clock() This function returns the processor time consumed by a program

This function returns the date and time in the format


day month date hours:minutes:seconds year
ctime()
Eg: Sat Jul 27 11:26:03 2019
time is printed based on the pointer returned by Calendar Time

difftime() This function returns the difference between the times provided.

This function prints the UTC (Coordinated Universal Time) Time and
gmtime() date.
Format for both gmtime() and asctime() is same

mktime() This function returns the calendar-time equivalent using struct tm.

This function returns the calendar-time equivalent using data-type


time()
time_t.

This function helps to format the string returned by other time


strftime()
functions using different format specifiers

Get Current Date and Time


To get the current date and time we use the combination
of time() , localtime(), and asctime() functions as shown:
 The time() function retrieves the current time since the beginning of
Unix period( 1st Jan, 1970) in the form of time_t value stores it in the
variable pointed by the pointer passed to it as an argument.
 The localtime() function to convert the time retrieved by time() function
into local time components such as hours, minutes, seconds, day,
month and year. It takes one parameter which is a pointer to a time
value that is to be converted in local time.
 The asctime() function to convert the obtained time into a string
representation of the local time in a human readable format which is
"Day Mon Date HH:MM:SS YYYY\n". The asctime() accepts a pointer
to a structure (struct tm) that contains the time component that is to be
converted.
Example

#include <stdio.h>
#include <time.h>

int main() {

// Structure to store local time


struct tm* ptr;

// Variable to store current time


time_t t;

// Get current time


t = time(NULL);

// Convert it to local time


ptr = localtime(&t);

// Get the string of local time


printf("%s", asctime(ptr));
return 0;
}

Output
Tue Apr 15 07:22:42 2025

Print Time in UTC (Coordinated Universal Time)


To print the UTC time, we use the gmtime() function that converts the
time obtained by the time() function into a Coordinated Universal Time
(also known as Greenwich Mean Time).
#include <stdio.h>
#include <time.h>

int main() {

// Structure to store local time


struct tm* ptr;

// Variable to store current time


time_t t;

// Get current time


t = time(NULL);

// Convert it to UTC time


ptr = gmtime(&t);

// Get the string of local time


printf("%s", asctime(ptr));
return 0;
}

Output
Tue Apr 15 12:44:17 2025

Find Time Difference


The time difference between the time_t variables recorded between some
time intervals is used to check the execution time of the part of the code.
Example: The program uses the difftime() functions defined in the time .h
header file. This function take two time value as the starting and ending
time and return the difference between them.

#include <stdio.h>
#include <time.h>

int main() {
time_t start, end;

// Record start time


start = time(NULL);
int a, b;
scanf("%d %d", &a, &b);
printf("Sum of %d and %d is %d\n",
a, b, a + b);

// Record endtime
end = time(NULL);

// Print time difference


printf("Time taken to print sum is %.2f seconds",
difftime(end, start));
}

Output
Sum of 0 and 0 is 0
Time taken to print sum is 0.00 seconds
Note: If user gives input slowly that time also add up for total execution
time.
Time using CPU Clock
The program uses clock() function defined in time.h which is used to find
the number of clock ticks during the executions of a code. Which allows
us to measure the time taken by CPU to execute the code.

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

int frequency_of_primes(int n)
{
// This function checks the number of
// primes less than the given parameter
int i, j;
int freq = n - 1;
for (i = 2; i <= n; ++i)
for (j = sqrt(i); j > 1; --j)
if (i % j == 0) {
--freq;
break;
}
return freq;
}

int main()
{
clock_t t;
int f;
t = clock();
f = frequency_of_primes(9999);
printf("The number of primes lower"
" than 10, 000 is: %d\n",
f);
t = clock() - t;
printf("No. of clicks %ld clicks (%f seconds).\n",
t, ((float)t) / CLOCKS_PER_SEC);
return 0;
}

Output
The number of primes lower than 10, 000 is: 1229
No. of clicks 2837 clicks (0.002837 seconds).

Print Time in hour::minute Format


The program uses strftime() function which formats the date and time as
string according to the specified format by the user. To define the format
for the time the string uses format specifiers such as %l for hour in 12
hour format, %M for minutes and %p to for AM or PM.

#include <stdio.h>
#include <time.h>
int main()
{
time_t rawtime;
struct tm* timeinfo;

// Used to store the time


// returned by localtime() function
char buffer[80];

time(&rawtime);
timeinfo = localtime(&rawtime);
strftime(buffer, 80,
"Time is %I:%M %p.",
timeinfo);

// strftime() function stores the


// current time as Hours : Minutes
//%I %M and %p-> format specifier
// of Hours minutes and am/pm respectively*/
// prints the formatted time
puts(buffer);

return 0;
}

Output
Time is 09:00AM.

Internal Linkage and External Linkage in C


Last Updated : 2 Apr, 2025




In C, linkage is a concept that describes how names/identifiers can or
cannot refer to the same entity throughout the whole program or a single
translation unit. The above sounds similar to scope, but it is not so. To
understand what the above means, let us dig deeper into the compilation
process.
Before moving to learn about linkage in C, we first need to
understand what a translation unit is.
A translation unit is a file containing source code, header files and other
dependencies. All of these sources are grouped together to form a single
translation unit which can then be used by the compiler to produce one
single executable object.
What is a Linkage?
Assume a C program that consists of multiple source code files. Each
source file is compiled one at a time. In the compilation process, the last
stage is linking where s multiple machine code files are used to produce
an executable object code. It is handled by the program called linker.
Linkage is a property that describes how variables should be linked
by the linker.
Should a variable be available for another file to use? Should a variable
be used only in the file declared? Both are decided by linkage. Linkage
thus allows you to couple names together on a per file basis.

Types of Linkage in C
There are 2 types of linkage in C:
Internal Linkage
An identifier implementing internal linkage is not accessible outside the
translation unit it is declared in. Any identifier within the unit can access
an identifier having internal linkage. It is implemented by the
keyword static. An internally linked identifier is stored in initialized or
uninitialized segment of RAM. For example,
Consider a source file: animals.c

#include <stdio.h>

// Variable with internal linkage


static int animals = 8;
The above code implements static linkage on identifier animals.
Consider another source file: feed.c is located in the same translation
unit using #include

#include <stdio.h>
#include "animals.c"

int main() {

// Accessing variable.
printf("%d", animals);
return 0;
}
On compiling and executing feed.c using the following command:
gcc feed.c -o feed
./feed
We get the output,
8
Now, consider that feed.c is located in a different translation unit (means
we are not including the animals.c using #include). Trying to compile it
using the following command:
gcc feed.c animals.c -o feed
./feed
Compiler will throw an error
feed.c: In function 'main':
feed.c:6:18: error: 'animals' undeclared (first use in this
function)
6 | printf("%d", animals);
| ^~~~~~~
feed.c:6:18: note: each undeclared identifier is reported
only once for each function it appears in

External Linkage
An identifier implementing external linkage is visible to every translation
unit. Externally linked identifiers are shared between translation units and
are considered to be located at the outermost level of the program. It is
the default linkage for globally scoped variables and functions.
The keyword extern implements external linkage. When we use the
keyword extern, we tell the linker to look for the definition elsewhere.
Thus, the declaration of an externally linked identifier does not take up
any space. Extern identifiers are generally stored in initialized/uninitialized
or text segment of RAM.
Take the above example of internal linkage and remove the static
keyword.
animals.c

#include <stdio.h>

// Variable with external linkage


int animals = 8;
As the variable animals is declared globally, it is accessible to all the
translational units.
Now, consider the file feed.c is in the different translational unit
feed.c

#include <stdio.h>

// Telling compiler that the variable have


// external linkage
extern int animals;

int main() {

// Accessing variable.
printf("%d", animals);
return 0;
}
Now, compiling and executing both files using the command:
gcc feed.c animals.c -o feed
./feed
We get the output:
8
As we can see, as the variable animals have external linkage, we were
able to access it in the other translation unit.
Example of External and Internal Linkage
Let's take a look at an interesting example of external and internal linkage.
Modify the file animals.c as shown:

#include <stdio.h>

// Variable with internal linkage


static int animals = 8;

// Function with external linkage


void printAnimals() {
printf("%d\n", animals);
}
Here, animals variable have internal linkage (as it is declared static) while
the function printAnimals() have external linkage (as it is declared
globally).
Update the feed.c file as well

#include <stdio.h>

// Telling compiler that the function have


// external linkage
extern void printAnimals();

int main() {

printAnimals()
return 0;
}
Guess what will happen?
If you guessed that the value of animals variable will be printed, then you
are correct. It is because we are not accessing the value of animals in
other translation unit. We are just accessing the function printAnimals()
which is externally linked and in turn belongs to the same translation unit
as animals. So it is able to access the value of the variable animals.

Storage Classes in C
Last Updated : 24 Jan, 2025




In C, storage classes define the lifetime, scope, and visibility of variables.
They specify where a variable is stored, how long its value is retained,
and how it can be accessed which help us to trace the existence of a
particular variable during the runtime of a program.
In C, there are four primary storage classes:
Table of Content
 auto
 register
 static
 extern
auto
This is the default storage class for all the variables declared inside a
function or a block. Auto variables can be only accessed within the
block/function they have been declared and not outside them (which
defines their scope).
Properties of auto Variables
 Scope: Local
 Default Value: Garbage Value
 Memory Location: RAM
 Lifetime: Till the end of its scope
Example:

#include <stdio.h>
int main() {

// auto is optional here, as it's the default storage


class
auto int x = 10;
printf("%d", x);
return 0;
}

Output
10
Explanation: The auto keyword is used to declare a local variable with
automatic storage. However, in C, local variables are automatically auto
by default, so specifying auto is optional.
auto keyword is not used in front of functions as functions are not limited
to block scope.

static
This storage class is used to declare static variables that have the
property of preserving their value even after they are out of their scope!
Hence, static variables preserve the value of their last use in their scope.
Properties of static Storage Class
 Scope: Local
 Default Value: Zero
 Memory Location: RAM
 Lifetime: Till the end of the program
Example:

#include <stdio.h>
void counter() {

// Static variable retains value between calls


static int count = 0;
count++;
printf("Count = %d\n", count);
}
int main() {

// Prints: Count = 1
counter();

// Prints: Count = 2
counter();
return 0;
}

Output
Count = 1
Count = 2
Explanation: The static variable count retains its value between function
calls. Unlike local variables, which are reinitialized each time the function
is called, static variables remember their previous value.
register
This storage class declares register variables that have the same
functionality as that of the auto variables. The only difference is that the
compiler tries to store these variables in the register of the microprocessor
if a free register is available making it much faster than any of the other
variables.
Properties of register Storage Class Objects
 Scope: Local
 Default Value: Garbage Value
 Memory Location: Register in CPU or RAM
 Lifetime: Till the end of its scope
Note: The compiler may ignore the suggestion based on available
registers.

#include <stdio.h>

int main() {

// Suggest to store in a register


register int i;
for (i = 0; i < 5; i++) {
printf("%d ", i);
}
return 0;
}

Output
0 1 2 3 4
Explanation: The register keyword suggests storing the variable in a
register for faster access, but the compiler may not always honor this
request based on available CPU registers.
extern
Extern storage class simply tells us that the variable is defined elsewhere
and not within the same block where it is used. Basically, the value is
assigned to it in a different block and this can be overwritten/changed in a
different block as well.
Also, a normal global variable can be made extern as well by placing the
‘extern’ keyword before its declaration/definition in any function/block.
Properties of extern Storage Class Objects
 Scope: Global
 Default Value: Zero
 Memory Location: RAM
 Lifetime: Till the end of the program.
For example, the below file contains a global variable.
printVar.c

// Global variable declaration


int globalVar;

Another file declares this variable as extern static that it is declared in


another file. It then prints this variable.
main.c

#include <stdio.h>

// Global variable
int globalVar = 100;

void printGlobalVar();

int main() {

// Prints: Global variable is: 100


printGlobalVar();
return 0;
}
To run this program, we have to compile both of the files together so
that main.c can get the definition of the variable globalVar. We use the
following command for GCC:
gcc main.c printVar.c -o main
Summary
The below table summarize the above storage classes:

You might also like