File Handling and C Preprocessors
File Handling and C Preprocessors
---
1. Introduction to Files
Definition
Files in C programming are used to store data permanently in a structured manner. Unlike
variables and arrays that lose their value once the program terminates, files ensure the
persistence of data by saving it to a storage device.
Organized Data Storage: Data is stored in a structured way, making it easy to manage.
File Types in C
1. Text Files
2. Binary Files
2. File Operations in C
C provides several standard library functions to perform operations on files. Key operations
include:
---
3. Read the data using file I/O functions (fgetc, fgets, fscanf).
---
int main() {
FILE *file;
char ch;
if (file == NULL) {
printf("File not found!\n");
return 1;
}
return 0;
}
Output Explanation:
If example.txt contains Hello World, the program prints Hello World character by character.
---
#include <stdio.h>
int main() {
FILE *file;
char line[100];
return 0;
}
Output Explanation:
If example.txt contains:
Hello
World
Hello
World
---
#include <stdio.h>
int main() {
FILE *file;
char name[20];
int age;
return 0;
}
Output Explanation:
If data.txt contains:
Alice 30
Bob 25
---
4. Writing to a File
---
#include <stdio.h>
int main() {
FILE *file;
char text[] = "Hello, World!";
int i = 0;
if (file == NULL) {
printf("Error creating file!\n");
return 1;
}
return 0;
}
Output Explanation:
Hello, World!
---
#include <stdio.h>
int main() {
FILE *file;
char *lines[] = {"First line", "Second line", "Third line"};
int i;
if (file == NULL) {
printf("Error creating file!\n");
return 1;
}
return 0;
}
Output Explanation:
Creates output.txt containing:
First line
Second line
Third line
---
#include <stdio.h>
int main() {
FILE *file;
char name[] = "Alice";
int age = 30;
if (file == NULL) {
printf("Error creating file!\n");
return 1;
}
return 0;
}
Output Explanation:
Alice 30
---
5. Summary
Functions: Master fopen, fclose, fgetc, fgets, fputc, fputs, and fprintf for effective file handling.
Error Handling: Always check if fopen() returns NULL to handle file access errors gracefully.
---
---
1. fgetc()
fgetc() reads a single character from a file and advances the file pointer. It is particularly useful
when reading files character by character.
Syntax:
Returns the character read (as an int) or EOF if the end of the file or an error occurs.
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "r");
if (file == NULL) {
printf("File could not be opened.\n");
return 1;
}
char ch;
while ((ch = fgetc(file)) != EOF) {
printf("%c", ch);
}
fclose(file);
return 0;
}
Explanation:
Output (Example):
Hello, World!
---
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "r");
if (file == NULL) {
printf("File could not be opened.\n");
return 1;
}
int count = 0;
while (fgetc(file) != EOF) {
count++;
}
printf("Number of characters: %d\n", count);
fclose(file);
return 0;
}
Output:
Number of characters: 13
---
2. fgets()
fgets() reads a string (including spaces) from a file until a newline character (\n), the specified
maximum number of characters, or EOF.
Syntax:
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "r");
if (file == NULL) {
printf("File could not be opened.\n");
return 1;
}
char buffer[50];
if (fgets(buffer, 50, file) != NULL) {
printf("Line: %s\n", buffer);
}
fclose(file);
return 0;
}
Output (Example):
Line: Hello, World!
---
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "r");
if (file == NULL) {
printf("File could not be opened.\n");
return 1;
}
char buffer[50];
while (fgets(buffer, 50, file) != NULL) {
printf("%s", buffer);
}
fclose(file);
return 0;
}
Explanation:
Output (Example):
Hello, World!
Welcome to C programming.
---
3. fputc()
#include <stdio.h>
int main() {
FILE *file = fopen("output.txt", "w");
if (file == NULL) {
printf("File could not be opened.\n");
return 1;
}
fputc('A', file);
fclose(file);
printf("Character written to file.\n");
return 0;
}
---
#include <stdio.h>
int main() {
FILE *file = fopen("output.txt", "w");
if (file == NULL) {
printf("File could not be opened.\n");
return 1;
}
char str[] = "Hello";
for (int i = 0; str[i] != '\0'; i++) {
fputc(str[i], file);
}
fclose(file);
printf("String written to file.\n");
return 0;
}
Hello
---
4. fputs()
fputs() writes a string to a file. Unlike fputc, it writes the entire string, excluding the null
terminator.
Syntax:
#include <stdio.h>
int main() {
FILE *file = fopen("output.txt", "w");
if (file == NULL) {
printf("File could not be opened.\n");
return 1;
}
fputs("Hello, World!", file);
fclose(file);
printf("String written to file.\n");
return 0;
}
Hello, World!
---
#include <stdio.h>
int main() {
FILE *file = fopen("output.txt", "a");
if (file == NULL) {
printf("File could not be opened.\n");
return 1;
}
fputs("\nWelcome!", file);
fclose(file);
printf("String appended to file.\n");
return 0;
}
Hello, World!
Welcome!
---
5. fprintf()
fprintf() is used to write formatted data to a file, similar to printf but for files.
Syntax:
#include <stdio.h>
int main() {
FILE *file = fopen("output.txt", "w");
if (file == NULL) {
printf("File could not be opened.\n");
return 1;
}
fprintf(file, "Name: %s, Age: %d\n", "Alice", 25);
fclose(file);
printf("Formatted text written to file.\n");
return 0;
}
---
#include <stdio.h>
int main() {
FILE *file = fopen("log.txt", "w");
if (file == NULL) {
printf("File could not be opened.\n");
return 1;
}
for (int i = 1; i <= 3; i++) {
fprintf(file, "Log Entry %d: Status OK\n", i);
}
fclose(file);
printf("Log entries written to file.\n");
return 0;
}
---
Error Handling in C
Error handling refers to the process of detecting, managing, and recovering from errors in a
program. In C, errors can occur during program execution due to reasons like invalid input, file
not found, or division by zero. C provides several ways to handle errors, such as:
Key Concepts
---
#include <stdio.h>
int main() {
int a = 10, b = 0;
if (b == 0) {
printf("Error: Division by zero is not allowed.\n");
} else {
printf("Result: %d\n", a / b);
}
return 0;
}
Explanation:
Output:
---
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main() {
FILE *file = fopen("nonexistent.txt", "r");
if (file == NULL) {
printf("Error: %s\n", strerror(errno));
} else {
fclose(file);
}
return 0;
}
Explanation:
When fopen fails, errno is set, and strerror(errno) provides a descriptive error message.
Output:
---
Example 3: Using perror()
#include <stdio.h>
int main() {
FILE *file = fopen("nonexistent.txt", "r");
if (file == NULL) {
perror("File Error");
} else {
fclose(file);
}
return 0;
}
Explanation:
When the file cannot be opened, perror() prints a descriptive error message prefixed with "File
Error".
Output:
---
#include <stdio.h>
int main() {
int num;
printf("Enter a number: ");
if (scanf("%d", &num) != 1) {
printf("Error: Invalid input. Please enter a valid number.\n");
} else {
printf("You entered: %d\n", num);
}
return 0;
}
Explanation:
scanf returns the number of inputs successfully read. If it’s not 1, an error is reported.
---
The remove() function in C is used to delete a file from the filesystem. It is part of the <stdio.h>
library. If successful, it returns 0; otherwise, it returns a non-zero value.
Syntax:
---
#include <stdio.h>
int main() {
if (remove("example.txt") == 0) {
printf("File deleted successfully.\n");
} else {
perror("Error deleting file");
}
return 0;
}
Explanation:
---
#include <stdio.h>
int main() {
if (remove("nonexistent.txt") != 0) {
perror("Error deleting file");
}
return 0;
}
Explanation:
Since nonexistent.txt does not exist, remove() fails, and perror() prints an error message.
Output:
---
#include <stdio.h>
int main() {
if (remove("readonly.txt") == 0) {
printf("File deleted successfully.\n");
} else {
perror("Error deleting file");
}
return 0;
}
Explanation:
If the file readonly.txt is write-protected, remove() fails, and an error is printed.
---
#include <stdio.h>
int main() {
char filename[100];
printf("Enter the name of the file to delete: ");
scanf("%s", filename);
if (remove(filename) == 0) {
printf("File '%s' deleted successfully.\n", filename);
} else {
perror("Error deleting file");
}
return 0;
}
Explanation:
The program attempts to delete the file and reports success or failure.
---
Renaming Files
The rename() function in C is used to change the name of a file or move it to a new location. It is
defined in <stdio.h>.
Syntax:
---
#include <stdio.h>
int main() {
if (rename("oldfile.txt", "newfile.txt") == 0) {
printf("File renamed successfully.\n");
} else {
perror("Error renaming file");
}
return 0;
}
Explanation:
Output:
---
#include <stdio.h>
int main() {
if (rename("nonexistent.txt", "newfile.txt") != 0) {
perror("Error renaming file");
}
return 0;
}
Explanation:
Output:
---
#include <stdio.h>
int main() {
if (rename("example.txt", "backup/example.txt") == 0) {
printf("File moved successfully.\n");
} else {
perror("Error moving file");
}
return 0;
}
Explanation:
Output:
---
Example 4: User Input for Renaming
#include <stdio.h>
int main() {
char oldname[100], newname[100];
printf("Enter the current file name: ");
scanf("%s", oldname);
printf("Enter the new file name: ");
scanf("%s", newname);
if (rename(oldname, newname) == 0) {
printf("File renamed to '%s'.\n", newname);
} else {
perror("Error renaming file");
}
return 0;
}
Explanation:
The program attempts to rename the file and reports success or failure.
---
Introduction to C Preprocessors
C preprocessors are a set of directives provided by the C language to process the source code
before it is passed to the compiler. These directives begin with the # symbol and are executed
by the preprocessor, which is a part of the compilation process. The preprocessor directives
allow us to include files, define constants, perform conditional compilation, and more.
The preprocessor does not produce object code; it only prepares the source code for the
compiler by performing textual substitutions, removing comments, and handling other tasks
defined by the directives.
---
---
The #define directive is used to define a constant or a macro. A macro is a fragment of code
that can be substituted throughout the program.
Syntax
Key Points
It is a textual substitution; the compiler replaces the identifier with the value wherever it appears.
It has no type-checking.
---
Examples of #define
#include <stdio.h>
#define PI 3.14159
int main() {
printf("The value of PI is: %f\n", PI);
return 0;
}
Explanation:
Output:
---
#include <stdio.h>
#define SQUARE(x) (x * x)
int main() {
int num = 5;
printf("The square of %d is: %d\n", num, SQUARE(num));
return 0;
}
Explanation:
Output:
---
#include <stdio.h>
#define AREA_RECTANGLE(length, breadth) \
(length * breadth)
int main() {
int length = 10, breadth = 5;
printf("The area of the rectangle is: %d\n", AREA_RECTANGLE(length, breadth));
return 0;
}
Explanation:
The backslash (\) allows the macro definition to span multiple lines.
Output:
---
int main() {
#ifdef DEBUG
printf("Debug mode is ON\n");
#else
printf("Debug mode is OFF\n");
#endif
return 0;
}
Explanation:
Since DEBUG is defined, the printf statement inside the #ifdef block is executed.
Output:
Debug mode is ON
---
The #include directive is used to include the contents of a file (usually header files) into the
program. This helps in reusing code and maintaining modularity.
Syntax
#include <filename>
#include "filename"
Key Points
---
Examples of #include
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
Explanation:
Output:
Hello, World!
---
#include <stdio.h>
#include <math.h>
int main() {
double num = 25.0;
printf("The square root of %.2f is %.2f\n", num, sqrt(num));
return 0;
}
Explanation:
#include <stdio.h> provides access to printf.
Output:
---
File: myheader.h
File: main.c
#include <stdio.h>
#include "myheader.h"
int main() {
printf("%s\n", MESSAGE);
return 0;
}
Explanation:
Output:
Welcome to C Programming!
---
int main() {
printf("Logarithm of 10: %.2f\n", log10(10));
return 0;
}
Explanation:
The #include directives ensure that necessary files are included in the program.
Output:
---
Summary
#define: Used to define constants and macros. Simplifies repetitive tasks and improves code
readability.
#include: Used to include standard or user-defined files, making functions and macros
accessible.
Preprocessor directives enhance the flexibility, reusability, and modularity of programs, making
them a critical component of C programming.
Conditional Compilation Directives
Conditional compilation directives are preprocessor directives in C that allow specific portions of
code to be included or excluded during the compilation process based on certain conditions.
These directives make the code flexible, platform-independent, and more manageable,
especially when working on large projects or cross-platform applications.
Conditional compilation directives enable the programmer to control the inclusion or exclusion of
code fragments based on conditions evaluated at compile-time. These conditions are typically
related to whether a specific macro is defined or not. They are particularly useful for:
1. #ifdef
2. #ifndef
3. #endif
4. #else
These directives are evaluated by the C Preprocessor, which processes them before the
compiler translates the program.
---
1. #ifdef Directive
The #ifdef directive checks if a macro is defined. If the macro is defined, the code following
#ifdef is included in the program. Otherwise, it is skipped.
Syntax
#ifdef MACRO_NAME
// Code to include if MACRO_NAME is defined
#endif
Example 1
#include <stdio.h>
#define DEBUG_MODE
int main() {
#ifdef DEBUG_MODE
printf("Debugging is enabled.\n");
#endif
printf("Program is running normally.\n");
return 0;
}
Explanation:
Output:
Debugging is enabled.
Program is running normally.
Example 2
#include <stdio.h>
Explanation:
Output:
Example 3
#include <stdio.h>
#define ENABLE_LOGS
int main() {
#ifdef ENABLE_LOGS
printf("Logging feature is enabled.\n");
#endif
printf("Application initialized.\n");
return 0;
}
Explanation:
Since ENABLE_LOGS is defined, the logging message will appear in the output.
Output:
Example 4
#include <stdio.h>
#define FLAG
int main() {
#ifdef FLAG
printf("Flag is defined.\n");
#endif
printf("End of program.\n");
return 0;
}
Explanation:
Output:
Flag is defined.
End of program.
---
2. #ifndef Directive
The #ifndef directive checks if a macro is not defined. If the macro is not defined, the code
following #ifndef is included. Otherwise, it is skipped.
Syntax
#ifndef MACRO_NAME
// Code to include if MACRO_NAME is not defined
#endif
Example 1
#include <stdio.h>
int main() {
#ifndef FEATURE_X
printf("Feature X is not defined.\n");
#endif
return 0;
}
Explanation:
Output:
Example 2
#include <stdio.h>
#define FEATURE_Y
int main() {
#ifndef FEATURE_Y
printf("Feature Y is not defined.\n");
#else
printf("Feature Y is defined.\n");
#endif
return 0;
}
Explanation:
The code block after #ifndef is skipped, and the #else block is executed.
Output:
Feature Y is defined.
Example 3
#include <stdio.h>
int main() {
#ifndef DEBUG
printf("Debug mode is not enabled.\n");
#endif
return 0;
}
Explanation:
Since DEBUG is not defined, the message "Debug mode is not enabled." is included in the
output.
Output:
Example 4
#include <stdio.h>
#define PRODUCTION
int main() {
#ifndef TEST
printf("Test environment is not active.\n");
#endif
return 0;
}
Explanation:
Output:
---
3. #endif Directive
The #endif directive marks the end of a conditional directive block. It is always paired with #ifdef
or #ifndef.
Example
#include <stdio.h>
#define MODE
int main() {
#ifdef MODE
printf("Mode is defined.\n");
#endif
return 0;
}
Explanation:
Output:
Mode is defined.
---
4. #else Directive
The #else directive specifies an alternate code block to be included if the condition of the #ifdef
or #ifndef directive is false.
Syntax
#ifdef MACRO_NAME
// Code if MACRO_NAME is defined
#else
// Code if MACRO_NAME is not defined
#endif
Example 1
#include <stdio.h>
#define WINDOWS
int main() {
#ifdef WINDOWS
printf("Windows environment detected.\n");
#else
printf("Non-Windows environment detected.\n");
#endif
return 0;
}
Explanation:
Output:
Example 2
#include <stdio.h>
int main() {
#ifdef UNIX
printf("UNIX system.\n");
#else
printf("Not a UNIX system.\n");
#endif
return 0;
}
Explanation:
Output:
#include <stdio.h>
#define USE_GUI
int main() {
#ifdef USE_GUI
printf("Graphical User Interface enabled.\n");
#else
printf("Command Line Interface enabled.\n");
#endif
return 0;
}
Explanation:
Output:
Example 4
#include <stdio.h>
int main() {
#ifdef FEATURE_A
printf("Feature A is available.\n");
#else
printf("Feature A is not available.\n");
#endif
return 0;
}
Explanation:
Output:
Feature A is not available.
---
Conclusion
Conditional compilation directives such as #ifdef, #ifndef, #else, and #endif are powerful tools
that help manage code effectively. They allow programmers to write adaptable and maintainable
programs, especially when dealing with platform-dependent code or feature toggling. Always
remember to include an #endif to close your conditional blocks for clarity and error-free
compilation.
Introduction to Predefined Macro Names in C
Predefined macros in C are special macros that are defined by the compiler itself. These
macros provide information about the compilation environment, such as the current file name,
the date and time of compilation, or the current line number in the code. Unlike user-defined
macros, these macros are not required to be explicitly defined in the program. They are
automatically available to the programmer and can be very useful for debugging, logging, or
providing dynamic information during execution.
2. __LINE__: Represents the current line number in the source code as an integer.
---
1. __FILE__
Definition:
__FILE__ is a predefined macro that contains the name of the current source file. It helps in
logging and debugging by indicating in which file an error or operation occurred.
Example Codes:
1. Basic Example:
#include <stdio.h>
int main() {
printf("This code is in file: %s\n", __FILE__);
return 0;
}
Output:
Explanation: The __FILE__ macro outputs the name of the current file (e.g., "example.c"). This
is useful for identifying where a piece of code is executed.
2. Error Reporting:
#include <stdio.h>
void reportError() {
printf("Error occurred in file: %s\n", __FILE__);
}
int main() {
reportError();
return 0;
}
Output:
Explanation: The __FILE__ macro is used to dynamically display the file name when reporting
errors.
#include <stdio.h>
int main() {
FILE *fp = fopen(__FILE__, "r");
if (fp) {
printf("Successfully opened file: %s\n", __FILE__);
fclose(fp);
}
return 0;
}
Output:
Successfully opened file: example.c
Explanation: The macro is used to open the current source file dynamically.
#include <stdio.h>
int main() {
printf("File: %s, Line: %d\n", __FILE__, __LINE__);
return 0;
}
Output:
Explanation: The __FILE__ and __LINE__ macros are combined to give detailed information
about the file and line number.
---
2. __LINE__
Definition:
__LINE__ provides the current line number in the source code file. It is helpful for tracking
where specific operations are being executed.
Example Codes:
#include <stdio.h>
int main() {
printf("This is line number: %d\n", __LINE__);
return 0;
}
Output:
#include <stdio.h>
void debugLine() {
printf("Debugging at line: %d\n", __LINE__);
}
int main() {
debugLine();
return 0;
}
Output:
Debugging at line: 6
#include <stdio.h>
int main() {
#if __LINE__ < 10
printf("Code is in the first 10 lines.\n");
#else
printf("Code is beyond line 10.\n");
#endif
return 0;
}
Output:
#include <stdio.h>
void function() {
printf("Function called at line: %d\n", __LINE__);
}
int main() {
printf("Main starts at line: %d\n", __LINE__);
function();
return 0;
}
Output:
---
3. __DATE__
Definition:
__DATE__ contains the current compilation date in the format "MMM DD YYYY".
Example Codes:
#include <stdio.h>
int main() {
printf("This file was compiled on: %s\n", __DATE__);
return 0;
}
Output:
#include <stdio.h>
void logInfo() {
printf("Log generated on: %s\n", __DATE__);
}
int main() {
logInfo();
return 0;
}
Output:
#include <stdio.h>
int main() {
printf("Compiled on: %s at %s\n", __DATE__, __TIME__);
return 0;
}
Output:
#include <stdio.h>
int main() {
printf("Build Information - Date: %s\n", __DATE__);
return 0;
}
Output:
---
4. __TIME__
Definition:
__TIME__ contains the current compilation time in the format "HH:MM:SS".
Example Codes:
Output:
#include <stdio.h>
void logEvent() {
printf("Event logged at: %s\n", __TIME__);
}
int main() {
logEvent();
return 0;
}
Output:
#include <stdio.h>
int main() {
printf("Program compiled at: %s\n", __TIME__);
for (int i = 0; i < 3; i++) {
printf("Iteration %d at compile time.\n", i);
}
return 0;
}
Output:
#include <stdio.h>
int main() {
printf("Debug started at: %s\n", __TIME__);
return 0;
}
Output:
---
Summary
Predefined macros like __FILE__, __LINE__, __DATE__, and __TIME__ provide dynamic
information about the compilation environment.
These macros are valuable for debugging, logging, and tracking program execution.
They help make the code more flexible and provide metadata about the program.
By understanding and applying these macros effectively, programmers can improve the
maintainability and debuggability of their C programs.