C Programming Tutorial
Welcome to the C Programming Tutorial! This tutorial covers the basics of C programming, starting from the fundamentals and going all the way to complex topics. C is one of the most widely used programming languages, known for its simplicity, efficiency, and flexibility. Whether you're a beginner or someone looking to strengthen your programming skills, this guide will serve as a solid foundation.
What is C Language?
C is a powerful, general-purpose programming language. It was developed by Dennis Ritchie in 1972 at AT&T Bell Labs. C is known for its simplicity, structured approach, and close relationship with hardware, making it a great choice for system-level programming.
History of C
The C language was created to improve the B language, which was itself derived from BCPL (Basic Combined Programming Language). Over time, C evolved, and its features laid the foundation for many other programming languages, such as C++, Java, and C#.
Features of C
- Simple and Efficient - C is easy to learn and has a clean syntax.
- Portable - Code written in C can be run on different platforms with minimal changes.
- Fast Execution - C is close to assembly language, making it highly efficient.
- Memory Management - Direct access to memory and low-level manipulation using pointers.
- Rich Library - A variety of built-in functions for handling complex tasks.
How to Install C
To start programming in C, you need a compiler. A compiler translates your code into executable form. Here are some popular options:
- GCC (GNU Compiler Collection) - Available on Linux and macOS.
- MinGW - A minimal version of GCC for Windows.
- Turbo C++ - An older compiler, still popular for educational purposes.
- Code::Blocks - An IDE that includes GCC compiler.
For Windows users, it's recommended to install Code::Blocks with the GCC compiler for a simple setup. For Linux and macOS, GCC is typically pre-installed or can be easily installed via terminal.
First C Program
Let's write your first C program. This program will simply print "Hello, World!" to the screen:
#include <stdio.h>
int main() {
printf("Hello, World!\\n");
return 0;
}
Here's a breakdown:
#include <stdio.h>- This line includes the standard input-output header file.int main()- The main function where the program starts execution.printf("Hello, World!\\n")- A standard function to print text to the screen.return 0;- Returns 0 to indicate that the program executed successfully.
Compilation Process in C
The compilation process involves several stages to transform your C code into an executable file:
- Preprocessing - Handles directives like
#includeand macro definitions. - Compilation - Translates the preprocessed code into assembly language.
- Assembly - Converts assembly code into machine code.
- Linking - Combines object code files into a single executable, resolving references to external functions.
To compile a C program using GCC:
gcc filename.c -o outputname
Replace filename.c with your C file name and outputname with the desired name of the executable.
printf and scanf
The printf and scanf functions are crucial for input and output operations in C:
#include <stdio.h>
int main() {
int number;
printf("Enter a number: ");
scanf("%d", &number);
printf("You entered: %d\\n", number);
return 0;
}
In this example:
printfis used to print text and variables to the console.scanfis used to read input from the user. The%dformat specifier is for integers.&number- The&operator provides the memory address ofnumberforscanfto store the input.
Variables in C
Variables in C are used to store data that can be modified during program execution. They act as containers for storing data values. A variable has a data type, a name, and a value. Here's how to declare a variable in C:
int age = 25; // Integer variable float height = 5.9; // Floating-point variable char initial = 'A'; // Character variable
Key points about variables:
- Variables must be declared before use.
- Variable names must begin with a letter or underscore (
_). - Variables can store different types of data, like integers, floating-point numbers, and characters.
Data Types in C
Data types in C specify the type of data that a variable can hold. There are several data types in C, which are categorized into basic, derived, and user-defined types.
- Basic Data Types:
int- Used for integers (e.g.,int num = 5;)float- Used for single-precision floating-point numbers (e.g.,float price = 10.99;)double- Used for double-precision floating-point numbers (e.g.,double distance = 23.4567;)char- Used for characters (e.g.,char grade = 'A';)
- Derived Data Types:
Array- Collection of variables of the same type.Pointer- Stores memory addresses of other variables.Structure- User-defined data type that groups different types.
- User-defined Data Types:
enum- Enumeration, a user-defined type for a set of named integer constants.typedef- Defines a new name for existing data types.
int a = 10; // Integer char b = 'C'; // Character float c = 5.25; // Float double d = 10.987; // Double
Keywords in C
Keywords are reserved words in C that have special meaning to the compiler. They cannot be used as identifiers (variable names). C has 32 keywords, including:
int- Defines an integer variable.float- Defines a floating-point variable.if- Used for conditional statements.return- Returns a value from a function.void- Indicates that a function does not return a value.while,for,do- Used for loops.
int main() {
int num = 5; // 'int' is a keyword
return 0; // 'return' is a keyword
}
C Identifiers
Identifiers are names given to variables, functions, arrays, etc., to identify them in the program. They must follow these rules:
- Identifiers must begin with a letter (A-Z, a-z) or an underscore (
_). - They can contain letters, digits (0-9), and underscores, but no special characters.
- They are case-sensitive (
countandCountare different identifiers). - Keywords cannot be used as identifiers.
int count = 10; // Valid identifier float _value = 23.45; // Valid identifier int 9number = 5; // Invalid identifier, cannot start with a digit
C Operators
Operators are symbols that perform operations on variables and values. C provides several types of operators:
- Arithmetic Operators:
+,-,*,/,%(addition, subtraction, multiplication, division, modulo) - Relational Operators:
==,!=,<,>,<=,>=(comparison) - Logical Operators:
&&,||,!(logical AND, OR, NOT) - Bitwise Operators:
&,|,^,~,<<,>>(operations on bits) - Assignment Operators:
=,+=,-=,*=,/=,%=(assign values) - Increment/Decrement Operators:
++,--(increment or decrement a value by 1)
int a = 5, b = 10;
int sum = a + b; // Arithmetic Operator
if (a > b) { // Relational Operator
printf("a is greater than b");
}
a += 2; // Assignment Operator
a++; // Increment Operator
C Comments
Comments in C are used to add notes or explanations within the code, making it easier to understand. Comments are ignored by the compiler, meaning they don’t affect the program's execution.
- Single-line comment: Begins with
//and continues to the end of the line. - Multi-line comment: Enclosed within
/*and*/to span multiple lines.
// This is a single-line comment
/*
This is a multi-line comment.
It spans multiple lines.
*/
C Format Specifier
Format specifiers in C are used in input/output functions like printf() and scanf() to define the type of data being handled. Common format specifiers include:
%d- Integer%f- Floating-point number%c- Single character%s- String%lf- Double-precision floating-point number%xor%X- Hexadecimal number
int age = 25;
float height = 5.8;
char initial = 'A';
printf("Age: %d, Height: %.1f, Initial: %c", age, height, initial);
C Escape Sequence
Escape sequences in C are special characters used to represent certain whitespace, control characters, or special formatting inside strings. Each escape sequence starts with a backslash (\). Common escape sequences are:
\\n- New line\\t- Horizontal tab\\\"- Double quote\\\'- Single quote\\\\- Backslash
printf("Hello\\nWorld!"); // Outputs: Hello
// World!
printf("Name:\\tJohn Doe"); // Outputs: Name: John Doe
printf("He said \\"Hi!\\""); // Outputs: He said "Hi!"
ASCII Value in C
In C, every character has a corresponding ASCII (American Standard Code for Information Interchange) value. The ASCII value of characters can be obtained using `%d` format specifier with a character. This can be useful for operations involving character encoding.
char ch = 'A';
printf("The ASCII value of %c is %d", ch, ch); // Outputs: The ASCII value of A is 65
Here, the character `'A'` has an ASCII value of `65`, and using `%d` allows you to print its numeric value.
Constants in C
Constants in C are fixed values that do not change during program execution. They can be defined using the `const` keyword or `#define` preprocessor directive.
- Using `const` keyword: Declares a variable as constant. Its value cannot be altered.
- Using `#define` directive: Defines a symbolic constant. It is a preprocessor directive and not a variable.
const int MAX_AGE = 100; // Using const keyword
#define PI 3.14159 // Using #define directive
printf("Max Age: %d", MAX_AGE); // Outputs: Max Age: 100
printf("Value of PI: %.2f", PI); // Outputs: Value of PI: 3.14
The `const` keyword makes a variable immutable, while `#define` creates a constant that the preprocessor replaces before compilation.
Literals in C
Literals in C are fixed values assigned to variables. These are constant values that do not change during program execution. There are several types of literals:
- Integer Literals: Whole numbers without a fractional part. Example: `10`, `-42`, `0`.
- Floating-point Literals: Numbers with a fractional part. Example: `3.14`, `-0.001`.
- Character Literals: A single character enclosed in single quotes. Example: `'a'`, `'Z'`.
- String Literals: A sequence of characters enclosed in double quotes. Example: `"Hello"`, `"C Programming"`.
int age = 25; // Integer literal float pi = 3.14; // Floating-point literal char grade = 'A'; // Character literal char name[] = "John"; // String literal
Tokens in C
In C, tokens are the smallest building blocks of a program. There are five major categories of tokens:
- Keywords: Reserved words with special meanings. Example: `int`, `if`, `return`.
- Identifiers: Names given to variables, functions, etc. Example: `age`, `main`, `myFunction`.
- Constants: Fixed values. Example: `10`, `3.14`.
- Operators: Symbols for operations. Example: `+`, `-`, `*`, `&&`.
- Special Symbols: Punctuation symbols. Example: `;`, `{}`, `[]`.
int number = 10; // 'int' is a keyword, 'number' is an identifier, '10' is a constant.
Boolean in C
The C language does not have a native `boolean` data type like some other programming languages. However, C99 introduced {`
- `0` is considered `false`.
- Non-zero values are considered `true`.
#include < stdbool.h>
bool isEven = true;
int isOdd = 0; // 0 is false
if (isEven) {
printf("The number is even.");
}
Static in C
The `static` keyword in C can be used with variables and functions. It has several purposes:
- Static Local Variables: Retain their value between function calls.
- Static Global Variables: Limit the variable's scope to the file in which it's declared.
- Static Functions: Limit the function's visibility to its own file.
void count() {
static int counter = 0; // Retains value between calls
counter++;
printf("Counter: %d\\n", counter);
}
In this example, `counter` retains its value across multiple calls to the `count()` function.
Programming Errors in C
Errors in C programming are broadly categorized into three types:
- Syntax Errors: Errors due to incorrect code structure. Example: missing semicolon.
- Logical Errors: Errors in the algorithm or logic, leading to incorrect results.
- Runtime Errors: Errors that occur during program execution. Example: dividing by zero.
int result = 10 / 0; // Runtime Error: Division by zero
Compile-time vs Runtime Errors
Errors in C can occur during compilation or during program execution:
- Compile-time Errors: Detected by the compiler before execution. Example: Syntax errors.
- Runtime Errors: Occur during the execution of the program. Example: Invalid memory access.
// Compile-time error example int number = "Hello"; // Type mismatch, detected during compilation // Runtime error example int arr[5]; arr[10] = 50; // Array index out of bounds, detected at runtime
Conditional Operator in C
The **Conditional Operator** in C is a shorthand way to write simple `if-else` statements. It uses the syntax: condition ? expr1 : expr2. If the condition is true, `expr1` is executed; otherwise, `expr2` is executed.
int x = 10, y = 20;
int max = (x > y) ? x : y; // max will be 20
printf("Maximum: %d\\n", max);
Bitwise Operators in C
Bitwise Operators in C operate directly on bits and are used for performing binary calculations. Common bitwise operators include:
&(AND): Sets each bit to 1 if both bits are 1.|(OR): Sets each bit to 1 if at least one bit is 1.^(XOR): Sets each bit to 1 if only one of the bits is 1.~(NOT): Inverts all bits (1 becomes 0, and 0 becomes 1).<<(Left Shift): Shifts bits to the left, filling with 0s.>>(Right Shift): Shifts bits to the right, filling with the sign bit (for signed numbers) or 0 (for unsigned).
int a = 5; // 0101 in binary
int b = 3; // 0011 in binary
int andResult = a & b; // 0101 & 0011 = 0001 -> 1
int orResult = a | b; // 0101 | 0011 = 0111 -> 7
int xorResult = a ^ b; // 0101 ^ 0011 = 0110 -> 6
int notResult = ~a; // ~0101 = 1010 -> -6 (in 2's complement form)
int leftShift = a << 1; // 0101 << 1 = 1010 -> 10
int rightShift = a >> 1; // 0101 >> 1 = 0010 -> 2
printf("AND: %d, OR: %d, XOR: %d, NOT: %d, Left Shift: %d, Right Shift: %d\\n",
andResult, orResult, xorResult, notResult, leftShift, rightShift);
2's Complement in C
2's Complement is a method for representing signed integers in binary, allowing both positive and negative values. To get the 2's complement of a number:
- Invert all bits (1 becomes 0, and 0 becomes 1).
- Add 1 to the result.
For example, in an 8-bit system, the 2's complement of 5 (0000 0101) is -5, represented by inverting all bits (1111 1010) and adding 1 (1111 1011).
int x = 5;
int twosComplement = ~x + 1; // -5 in 2's complement form
printf("2's Complement of %d: %d\\n", x, twosComplement);
C Control Statements
Control Statements in C are used to determine the flow of program execution. These statements allow decisions to be made in code based on conditions. The most commonly used control statements are if-else and switch.
C if-else
The if-else statement is used for conditional branching in C. It evaluates a condition and executes code blocks based on whether the condition is true or false. The basic syntax is:
if (condition) {
// Code to execute if condition is true
} else {
// Code to execute if condition is false
}
You can also use multiple conditions using else if statements.
int num = 20;
if (num > 0) {
printf("Positive number");
} else if (num == 0) {
printf("Zero");
} else {
printf("Negative number");
}
C switch
The switch statement is used for multi-way branching. It evaluates an expression and executes the code block that matches the given case. If no match is found, the default case is executed (if it is defined). Here's the syntax:
switch (expression) {
case constant1:
// Code to execute if expression equals constant1
break;
case constant2:
// Code to execute if expression equals constant2
break;
...
default:
// Code to execute if none of the cases match
}
The break statement is used to exit the switch block once a case is matched, preventing the execution of subsequent cases.
char grade = 'B';
switch (grade) {
case 'A':
printf("Excellent!");
break;
case 'B':
printf("Well done!");
break;
case 'C':
printf("Good!");
break;
default:
printf("Invalid grade");
}
if-else vs switch
Both if-else and switch statements are used for decision-making in C, but they have distinct use cases:
- if-else is suitable for complex conditions involving relational operators (>, <, ==, etc.) and logical operators (&&, ||).
- switch is ideal when there are multiple fixed values to compare, as it can make the code cleaner and more readable.
-
Performance:
switchmay perform faster thanif-elsein some cases because it can be optimized by the compiler using jump tables for integer comparisons. -
Flexibility:
if-elseis more flexible since it allows complex conditions, whileswitchis limited to simple comparisons of integers or characters.
Here's an example showing when to use if-else vs switch:
// Using if-else
int x = 10;
if (x > 0 && x < 20) {
printf("x is between 1 and 19");
} else {
printf("x is out of range");
}
// Using switch
int choice = 2;
switch (choice) {
case 1:
printf("Choice is 1");
break;
case 2:
printf("Choice is 2");
break;
default:
printf("Invalid choice");
}
C Loops
Loops in C are used to execute a block of code repeatedly, based on a given condition. They are essential for iterating over data, performing repetitive tasks, and managing control flow efficiently. There are three main types of loops in C:
C do-while Loop
The do-while loop is similar to the while loop, but it guarantees that the code block is executed at least once, regardless of the condition. The condition is evaluated after the code block has executed. The syntax is:
do {
// Code to execute
} while (condition);
Here’s an example of a do-while loop:
int count = 1;
do {
printf("Count: %d\\n", count);
count++;
} while (count <= 5);
In this example, the do-while loop will print the count from 1 to 5.
C while Loop
The while loop is a control structure that repeatedly executes a block of code as long as the given condition is true. The condition is checked before executing the code block, making it possible for the loop to skip execution if the condition is initially false. The syntax is:
while (condition) {
// Code to execute
}
Here’s an example of a while loop:
int count = 1;
while (count <= 5) {
printf("Count: %d\\n", count);
count++;
}
In this example, the while loop will print the count from 1 to 5.
C for Loop
The for loop is a control structure used for iterating over a block of code a fixed number of times. It is often preferred when the number of iterations is known beforehand. The syntax is:
for (initialization; condition; increment/decrement) {
// Code to execute
}
Here’s an example of a for loop:
for (int i = 1; i <= 5; i++) {
printf("Iteration: %d\\n", i);
}
In this example, the for loop will iterate 5 times, printing the iteration number each time.
Nested Loops in C
Nested Loops in C are loops inside other loops. The inner loop is executed completely each time the outer loop runs once. They are particularly useful for working with multi-dimensional data structures like 2D arrays. Here’s a simple example of nested for loops:
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
printf("i = %d, j = %d\\n", i, j);
}
}
In this example, the inner loop will iterate three times for each single iteration of the outer loop, resulting in 9 total iterations.
Infinite Loop in C
An Infinite Loop continues to execute indefinitely because its condition always evaluates to true. Use infinite loops with caution, typically in scenarios where the loop is controlled by an external condition or a break statement. Here’s an example using a while loop:
while (1) {
// Code will run indefinitely
printf("This is an infinite loop\\n");
}
In this example, the condition is always 1 (true), so the loop will run forever unless interrupted manually or using a break.
C break
The break statement is used to immediately exit a loop, regardless of the iteration count or loop condition. It's often used when a specific condition is met within a loop. Here's an example:
for (int i = 1; i <= 10; i++) {
if (i == 5) {
break; // Exit the loop when i is 5
}
printf("i = %d\\n", i);
}
This loop will print numbers 1 to 4, then exit when i is 5.
C continue
The continue statement skips the current iteration of a loop and continues with the next iteration. It’s useful when certain conditions need to be bypassed. Here's an example:
for (int i = 1; i <= 5; i++) {
if (i == 3) {
continue; // Skip the iteration when i is 3
}
printf("i = %d\n", i);
}
This loop will print numbers 1, 2, 4, and 5, skipping 3.
C goto
The goto statement provides a way to jump to a labeled section of code. Although it’s not commonly used due to readability concerns, it can be helpful in specific scenarios where structured loops are insufficient. Here’s an example:
int number = 0;
start:
printf("Enter a positive number (0 to exit): ");
scanf("%d", &number);
if (number < 0) {
printf("Negative number, try again!\n");
goto start; // Jump back to the label 'start'
}
In this example, if a negative number is entered, the program jumps back to the start label to retry.
Type Casting in C
Type Casting in C is the conversion of a variable from one data type to another. It can be implicit (automatically done by the compiler) or explicit (manually specified by the programmer). Here’s an example of explicit type casting:
int a = 10;
int b = 3;
float result;
result = (float)a / b; // Casting 'a' to float
printf("Result = %.2f\n", result);
In this example, a is cast to a float before division, ensuring a floating-point result.
Function in C
What is a Function in C?
A Function in C is a block of code that performs a specific task. Functions help organize code, promote reusability, and make programs easier to read and maintain. Each function has a unique name, which is used to invoke it. Here’s a basic structure of a C function:
// Function declaration
void greet();
// Main function
int main() {
greet(); // Function call
return 0;
}
// Function definition
void greet() {
printf("Hello, World!\n");
}
In this example, the function greet() is defined to print a message and is called from the main() function.
Function Call: Call by Value & Call by Reference
Functions in C can be called using two methods: Call by Value and Call by Reference.
Call by Value
In Call by Value, a copy of the actual parameter is passed to the function. Changes made to the parameter inside the function do not affect the original value. Here's an example:
void increment(int x) {
x = x + 1; // Change is local to this function
}
int main() {
int num = 10;
increment(num);
printf("Value of num: %d\n", num); // Output: 10
return 0;
}
In this example, num remains unchanged after calling increment(num) because only a copy was modified.
Call by Reference
In Call by Reference, the address of the actual parameter is passed to the function. Changes made inside the function affect the original parameter. Here's an example:
void increment(int *x) {
*x = *x + 1; // Change affects the original value
}
int main() {
int num = 10;
increment(&num);
printf("Value of num: %d\n", num); // Output: 11
return 0;
}
In this example, num is incremented because its memory address was modified in the increment function.
Recursion in C
Recursion is when a function calls itself to solve a smaller instance of the problem. It is useful for problems that can be broken down into similar sub-problems, like calculating a factorial or solving the Fibonacci sequence. Here’s an example of a recursive function to calculate a factorial:
int factorial(int n) {
if (n == 0) {
return 1; // Base case
} else {
return n * factorial(n - 1); // Recursive call
}
}
int main() {
int number = 5;
printf("Factorial of %d is %d\n", number, factorial(number)); // Output: 120
return 0;
}
In this example, the function factorial() calls itself with a reduced value until the base case n == 0 is reached.
Storage Classes in C
Storage Classes define the scope, visibility, and lifetime of variables and functions in C. There are four primary storage classes:
1. Auto
The `auto` storage class is the default for local variables. It defines variables that are visible only within the block they are declared. Their lifetime is limited to the block scope.
void function() {
auto int localVar = 10; // Local to this block
}
2. Static
The `static` storage class retains a variable's value even after the block ends. It allows data persistence within functions. Here’s an example:
void counter() {
static int count = 0; // Static variable retains its value
count++;
printf("Count: %d\\n", count);
}
int main() {
counter(); // Output: Count: 1
counter(); // Output: Count: 2
return 0;
}
3. Extern
The `extern` storage class is used to declare a global variable or function that is defined in another file or at a later point in the program. It tells the compiler that the variable exists but is defined elsewhere.
extern int globalVar; // Declared, defined elsewhere
4. Register
The `register` storage class hints that the variable will be heavily used, and requests the compiler to store it in a CPU register for faster access. However, this is just a suggestion and may not be honored by the compiler.
void process() {
register int counter = 0; // Request to store in CPU register
}
Pointers in C
A Pointer in C is a variable that stores the memory address of another variable. Pointers are powerful and allow direct manipulation of memory, enabling tasks like dynamic memory allocation and creating complex data structures. Here’s a basic example:
int main() {
int num = 10;
int *ptr = # // 'ptr' stores the address of 'num'
printf("Value of num: %d\\n", num);
printf("Address of num: %p\\n", ptr);
printf("Value at address stored in ptr: %d\\n", *ptr); // Dereferencing
return 0;
}
In this example, `ptr` is a pointer that holds the address of `num`. The `*` operator is used to dereference the pointer, accessing the value stored at that address.
Pointer to Pointer
A Pointer to Pointer is a pointer that stores the address of another pointer. This is often used for handling multi-level data structures. Here’s an example:
int main() {
int num = 20;
int *ptr = # // Pointer to 'num'
int **ptr2 = &ptr; // Pointer to pointer 'ptr'
printf("Value of num: %d\\n", num);
printf("Value using ptr: %d\\n", *ptr);
printf("Value using ptr2: %d\\n", **ptr2); // Double dereferencing
return 0;
}
In this example, `ptr2` is a pointer to the pointer `ptr`. Using `**ptr2` allows access to the value of `num`.
Pointer Arithmetic
Pointer Arithmetic involves operations like incrementing, decrementing, and adding integers to pointers. These operations are based on the data type size. Here's an example demonstrating basic pointer arithmetic:
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr; // Points to the first element of 'arr'
printf("Value at ptr: %d\\n", *ptr); // Output: 10
ptr++; // Move to the next integer (4 bytes ahead)
printf("Value at ptr after increment: %d\\n", *ptr); // Output: 20
ptr += 2; // Move two integers ahead
printf("Value at ptr after += 2: %d\\n", *ptr); // Output: 40
return 0;
}
In this example, `ptr++` increases the pointer to point to the next integer, adjusting the address by the size of an `int` (typically 4 bytes).
Dangling Pointers in C
A Dangling Pointer occurs when a pointer still references a memory location that has been freed or is no longer valid. Accessing such a pointer can cause undefined behavior. Here’s an example:
int* createDanglingPointer() {
int x = 42;
return &x; // Returning address of local variable (Dangerous)
}
int main() {
int *danglingPtr = createDanglingPointer();
// 'x' no longer exists, danglingPtr is now a dangling pointer
printf("Dangling Pointer Value: %d\\n", *danglingPtr); // Undefined behavior
return 0;
}
In this example, `danglingPtr` points to a local variable `x` that goes out of scope when `createDanglingPointer` returns, leaving `danglingPtr` dangling.
sizeof() Operator in C
The `sizeof()` operator in C is used to determine the size (in bytes) of a data type or variable. This is useful for understanding memory requirements and managing dynamic memory allocation. Here’s a basic usage:
int main() {
int num;
double d;
char c;
printf("Size of int: %zu bytes\\n", sizeof(num)); // Typically 4 bytes
printf("Size of double: %zu bytes\\n", sizeof(d)); // Typically 8 bytes
printf("Size of char: %zu byte\\n", sizeof(c)); // Typically 1 byte
printf("Size of int array[10]: %zu bytes\\n", sizeof(int[10])); // 40 bytes if int is 4 bytes
return 0;
}
The `sizeof()` operator returns the number of bytes occupied by the specified data type or variable.
const Pointer in C
A const pointer in C can be of two types:
- Pointer to a
constvalue: The value being pointed to cannot be modified. constpointer: The pointer itself cannot be reassigned to point elsewhere.
int main() {
int x = 10;
int y = 20;
// Pointer to a const value
const int *ptr1 = &x;
// *ptr1 = 15; // Error: cannot modify the value of x through ptr1
ptr1 = &y; // OK: can change the address ptr1 points to
// Const pointer
int *const ptr2 = &x;
*ptr2 = 15; // OK: can modify the value of x through ptr2
// ptr2 = &y; // Error: cannot change the address ptr2 points to
return 0;
}
In the example above, `const int *ptr1` prevents modifying the value, while `int *const ptr2` prevents changing the address.
void Pointer in C
A void pointer (or generic pointer) in C can point to any data type, but it cannot be directly dereferenced without type casting. Here's an example:
int main() {
int x = 42;
char y = 'A';
void *ptr;
ptr = &x; // void pointer pointing to an integer
printf("Value of x: %d\\n", *(int *)ptr); // Type casting to dereference
ptr = &y; // void pointer pointing to a char
printf("Value of y: %c\\n", *(char *)ptr); // Type casting to dereference
return 0;
}
The void pointer is versatile, but requires explicit type casting to access the value it points to.
Dereferencing a Pointer in C
Dereferencing a pointer means accessing the value stored at the address it points to using the * operator. Here’s an example:
int main() {
int num = 100;
int *ptr = #
printf("Address of num: %p\\n", ptr);
printf("Value of num using pointer: %d\\n", *ptr); // Dereferencing ptr
return 0;
}
In the example, *ptr is used to access the value at the address stored in ptr.
Null Pointer in C
A null pointer is a special pointer that doesn't point to any valid memory location. It is used as a sentinel value to indicate that the pointer is not intended to reference any object or memory. Example:
int main() {
int *ptr = NULL; // Initialize with NULL
if (ptr == NULL) {
printf("Pointer is null, no memory assigned.\\n");
} else {
printf("Pointer is not null.\\n");
}
return 0;
}
The NULL macro is defined in <stddef.h> and is used to initialize or check if a pointer is null.
Function Pointer in C
A Function Pointer is a pointer that points to the address of a function. It can be used to call functions dynamically. Here's an example:
void display() {
printf("Hello, World!\\n");
}
int main() {
void (*funcPtr)() = display; // Function pointer pointing to 'display'
funcPtr(); // Calling the function using the pointer
return 0;
}
Here, funcPtr is a pointer to the display function. It can be used to invoke display dynamically.
Function Pointer as Argument in C
A function pointer can also be passed as an argument to another function, enabling callback mechanisms. Here’s an example:
void greet() {
printf("Greetings!\\n");
}
void execute(void (*func)()) {
func(); // Calling the function via pointer
}
int main() {
execute(greet); // Passing function pointer to another function
return 0;
}
In this example, execute takes a function pointer as an argument and invokes it, allowing dynamic function calls.
Dynamic Memory in C
In C, dynamic memory management allows you to allocate memory at runtime, instead of compile time. This is useful when you don’t know the size of the data in advance. The standard library provides functions to manage dynamic memory: malloc, calloc, realloc, and free.
malloc() in C
The malloc (Memory Allocation) function allocates a specified number of bytes of memory and returns a pointer to the allocated memory. The memory is uninitialized, meaning it contains garbage values. Here's an example:
#include < stdio.h>
#include < stdlib.h>
int main() {
int *ptr;
int n = 5;
// Allocating memory for 5 integers
ptr = (int *)malloc(n * sizeof(int));
// Check if memory has been successfully allocated
if (ptr == NULL) {
printf("Memory not allocated.\\n");
exit(0);
} else {
printf("Memory successfully allocated using malloc.\\n");
// Initialize and display memory content
for (int i = 0; i < n; ++i) {
ptr[i] = i + 1;
printf("%d ", ptr[i]);
}
}
free(ptr); // Freeing the allocated memory
return 0;
}
In this example, malloc is used to allocate memory for 5 integers. Always check if memory allocation is successful before using it.
calloc() in C
The calloc (Contiguous Allocation) function also allocates memory but initializes all bits to zero. It requires two parameters: the number of elements and the size of each element. Here's an example:
#include < stdio.h>
#include < stdlib.h>
int main() {
int *ptr;
int n = 5;
// Allocating memory for 5 integers using calloc
ptr = (int *)calloc(n, sizeof(int));
// Check if memory has been successfully allocated
if (ptr == NULL) {
printf("Memory not allocated.\\n");
exit(0);
} else {
printf("Memory successfully allocated using calloc.\\n");
// Displaying memory content (initialized to 0)
for (int i = 0; i < n; ++i) {
printf("%d ", ptr[i]);
}
}
free(ptr); // Freeing the allocated memory
return 0;
}
In this example, calloc allocates memory for 5 integers, and each element is initialized to 0.
realloc() in C
The realloc function is used to resize a previously allocated memory block without losing the data. It takes two parameters: a pointer to the existing memory and the new size. Here's an example:
#include < stdio.h>
#include < stdlib.h>
int main() {
int *ptr;
int n = 5;
// Allocating memory for 5 integers
ptr = (int *)malloc(n * sizeof(int));
// Check if memory has been successfully allocated
if (ptr == NULL) {
printf("Memory not allocated.\\n");
exit(0);
}
// Resizing the allocated memory to 10 integers
n = 10;
ptr = (int *)realloc(ptr, n * sizeof(int));
if (ptr == NULL) {
printf("Memory reallocation failed.\\n");
exit(0);
} else {
printf("Memory successfully reallocated using realloc.\\n");
// Initialize and display memory content
for (int i = 0; i < n; ++i) {
ptr[i] = i + 1;
printf("%d ", ptr[i]);
}
}
free(ptr); // Freeing the allocated memory
return 0;
}
In the example, realloc changes the memory size from 5 to 10 integers. If resizing is successful, the data from the original memory is preserved.
free() in C
The free function is used to deallocate dynamically allocated memory. It takes a pointer as an argument and frees the memory it points to, making it available for other uses. Always free memory after it's no longer needed to avoid memory leaks. Here's an example:
#include < stdio.h>
#include < stdlib.h>
int main() {
int *ptr = (int *)malloc(5 * sizeof(int));
if (ptr == NULL) {
printf("Memory not allocated.\\n");
exit(0);
}
// Using the allocated memory
for (int i = 0; i < 5; ++i) {
ptr[i] = i + 1;
printf("%d ", ptr[i]);
}
free(ptr); // Freeing the allocated memory
printf("\\nMemory has been successfully freed.\\n");
return 0;
}
In this example, the free function releases the allocated memory after its usage.
Strings in C
In C, a string is an array of characters terminated by a null character \0. Strings can be manipulated using character arrays and standard string functions provided by the C Standard Library.
gets() & puts() in C
The gets() function is used to read a string from the user, while puts() is used to display a string. Here's an example:
#include < stdio.h>
int main() {
char str[100];
// Reading a string using gets()
printf("Enter a string: ");
gets(str); // Warning: Use of gets() is unsafe, prefer fgets()
// Displaying the string using puts()
printf("You entered: ");
puts(str);
return 0;
}
In this example, gets() reads a line of text from the user, and puts() prints it back. Note that gets() is unsafe due to potential buffer overflow issues; consider using fgets() for safer input handling.
String Functions in C
The C Standard Library provides various functions to perform operations on strings, such as calculating length, copying, concatenating, comparing, and manipulating case.
strlen() in C
The strlen() function returns the length of a string (excluding the null character). Here's an example:
#include < stdio.h>
#include < string.h>
int main() {
char str[] = "Hello, World!";
int length = strlen(str);
printf("The length of the string is: %d\\n", length);
return 0;
}
strcpy() in C
The strcpy() function copies one string to another. Here's an example:
#include < stdio.h>
#include < string.h>
int main() {
char source[] = "Hello, C!";
char destination[50];
// Copying source to destination
strcpy(destination, source);
printf("Copied string: %s\\n", destination);
return 0;
}
strcat() in C
The strcat() function concatenates (appends) one string to the end of another. Here's an example:
#include < stdio.h>
#include < string.h>
int main() {
char greeting[50] = "Hello";
char name[] = ", C Programmer!";
// Concatenating name to greeting
strcat(greeting, name);
printf("Greeting: %s\\n", greeting);
return 0;
}
strcmp() in C
The strcmp() function compares two strings. It returns 0 if they are equal, a positive value if the first string is greater, and a negative value if the second string is greater. Here's an example:
#include < stdio.h>
#include < string.h>
int main() {
char str1[] = "Hello";
char str2[] = "World";
int result = strcmp(str1, str2);
if (result == 0) {
printf("The strings are equal.\\n");
} else {
printf("The strings are not equal.\\n");
}
return 0;
}
strrev() in C
The strrev() function reverses a string. Here's an example:
#include < stdio.h>
#include < string.h>
int main() {
char str[] = "Hello";
// Reversing the string
strrev(str);
printf("Reversed string: %s\\n", str);
return 0;
}
strlwr() in C
The strlwr() function converts a string to lowercase. Here's an example:
#include < stdio.h>
#include < string.h>
int main() {
char str[] = "HELLO WORLD";
// Converting to lowercase
strlwr(str);
printf("Lowercase string: %s\\n", str);
return 0;
}
strupr() in C
The strupr() function converts a string to uppercase. Here's an example:
#include < stdio.h>
#include < string.h>
int main() {
char str[] = "hello world";
// Converting to uppercase
strupr(str);
printf("Uppercase string: %s\\n", str);
return 0;
}
strstr() in C
The strstr() function finds the first occurrence of a substring in a string. Here's an example:
#include < stdio.h>
#include < string.h>
int main() {
char str[] = "Hello, World!";
char *subStr = "World";
// Finding the substring
char *position = strstr(str, subStr);
if (position != NULL) {
printf("Substring found at position: %ld\\n", position - str);
} else {
printf("Substring not found.\\n");
}
return 0;
}
Math in C
The C Standard Library provides a set of mathematical functions defined in the <math.h> header file. These functions allow you to perform common mathematical operations like trigonometric calculations, power functions, rounding, and more.
Common Math Functions in C
Below is a list of commonly used math functions in C:
sqrt()- Square Rootpow()- Powerabs()- Absolute Valueceil()- Round Upfloor()- Round Downround()- Round to Nearestsin()- Sinecos()- Cosinetan()- Tangentlog()- Natural Logarithmlog10()- Base-10 Logarithmexp()- Exponential (e^x)
Square Root: sqrt()
The sqrt() function calculates the square root of a number. Here's an example:
#include < stdio.h>
#include < math.h>
int main() {
double num = 25.0;
double result = sqrt(num);
printf("Square root of %.2f is %.2f\\n", num, result);
return 0;
}
Power: pow()
The pow() function is used to raise a number to a specified power. Here's an example:
#include < stdio.h>
#include < math.h>
int main() {
double base = 5.0;
double exponent = 3.0;
double result = pow(base, exponent);
printf("%.2f raised to the power of %.2f is %.2f\\n", base, exponent, result);
return 0;
}
Absolute Value: abs()
The abs() function returns the absolute value of an integer. Here's an example:
#include < stdio.h>
#include < stdlib.h>
int main() {
int num = -42;
int result = abs(num);
printf("The absolute value of %d is %d\\n", num, result);
return 0;
}
Rounding: ceil() & floor()
The ceil() function rounds a number up to the nearest integer, while floor() rounds it down. Here's an example:
#include < stdio.h>
#include < math.h>
int main() {
double num = 4.56;
printf("Ceil of %.2f is %.2f\\n", num, ceil(num));
printf("Floor of %.2f is %.2f\\n", num, floor(num));
return 0;
}
Trigonometric Functions: sin(), cos(), tan()
Trigonometric functions like sin(), cos(), and tan() are used to calculate the sine, cosine, and tangent of an angle (in radians). Here's an example:
#include < stdio.h>
#include < math.h>
int main() {
double angle = 0.5; // Angle in radians
printf("sin(%.2f) = %.2f\\n", angle, sin(angle));
printf("cos(%.2f) = %.2f\\n", angle, cos(angle));
printf("tan(%.2f) = %.2f\\n", angle, tan(angle));
return 0;
}
Logarithmic & Exponential Functions: log(), exp()
The log() function returns the natural logarithm of a number, while exp() calculates the exponential value (e^x). Here's an example:
#include < stdio.h>
#include < math.h>
int main() {
double num = 2.0;
printf("Natural log of %.2f is %.2f\\n", num, log(num));
printf("Exponential of %.2f is %.2f\\n", num, exp(num));
return 0;
}
Rounding to Nearest: round()
The round() function rounds a floating-point number to the nearest integer. Here's an example:
#include < stdio.h>
#include < math.h>
int main() {
double num = 5.67;
printf("Rounded value of %.2f is %.0f\\n", num, round(num));
return 0;
}
C Structures and Unions
In C programming, Structures and Unions are user-defined data types that allow the grouping of different data types under a single name. Structures help manage related data, while unions are used to save memory by sharing space between members.
C Structure
A Structure in C is a collection of variables of different data types grouped together under a single name. Each variable in a structure is known as a member.
#include < stdio.h>
// Defining a structure
struct Person {
char name[50];
int age;
float salary;
};
int main() {
// Creating a structure variable
struct Person person1;
// Assigning values to members
person1.age = 25;
person1.salary = 55000.50;
strcpy(person1.name, "John Doe");
// Displaying structure values
printf("Name: %s\\n", person1.name);
printf("Age: %d\\n", person1.age);
printf("Salary: %.2f\\n", person1.salary);
return 0;
}
typedef in C
The typedef keyword is used in C to give a new name (alias) to an existing data type. It can simplify the usage of complex types like structures.
#include < stdio.h>
// Using typedef to create an alias for struct
typedef struct {
char name[50];
int age;
} Student;
int main() {
// Using the alias to declare a variable
Student student1;
student1.age = 20;
strcpy(student1.name, "Alice");
printf("Student Name: %s\\n", student1.name);
printf("Student Age: %d\\n", student1.age);
return 0;
}
C Array of Structures
You can create an array of structures to store data for multiple entities. Each element of the array is a structure.
#include < stdio.h>
struct Book {
char title[100];
char author[50];
int pages;
};
int main() {
// Array of structures
struct Book library[3] = {
{"Book One", "Author A", 300},
{"Book Two", "Author B", 250},
{"Book Three", "Author C", 400}
};
for (int i = 0; i < 3; i++) {
printf("Title: %s, Author: %s, Pages: %d\\n",
library[i].title, library[i].author, library[i].pages);
}
return 0;
}
C Nested Structure
A Nested Structure in C is a structure that contains another structure as a member. This is useful for creating complex data types.
#include < stdio.h>
struct Address {
char city[50];
int zipCode;
};
struct Employee {
char name[50];
struct Address address;
};
int main() {
struct Employee emp;
strcpy(emp.name, "Bob");
strcpy(emp.address.city, "New York");
emp.address.zipCode = 10001;
printf("Employee Name: %s\\n", emp.name);
printf("City: %s, Zip Code: %d\\n", emp.address.city, emp.address.zipCode);
return 0;
}
Structure Padding in C
Structure Padding refers to the extra bytes added by the compiler between structure members to align data in memory. This improves access speed but can increase the memory size of a structure.
#include < stdio.h>
struct Padded {
char a; // 1 byte
int b; // 4 bytes
char c; // 1 byte
}; // Padding may be added for alignment
int main() {
printf("Size of Padded structure: %lu bytes\\n", sizeof(struct Padded));
return 0;
}
C Union
A Union in C is similar to a structure, but it allows storing different data types in the same memory location. The memory size of a union is equal to the size of its largest member.
#include < stdio.h>
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data data;
data.i = 10;
printf("data.i: %d\\n", data.i);
data.f = 220.5;
printf("data.f: %.2f\\n", data.f);
strcpy(data.str, "Hello");
printf("data.str: %s\\n", data.str);
// Note: Assigning a new value overwrites the previous one in a union.
return 0;
}
C File Handling
Introduction to C File Handling
In C, file handling refers to the process of reading from and writing to files. C provides a set of built-in functions to perform these operations. These functions are defined in the stdio.h library and allow interaction with files in various modes (read, write, append, etc.).
C fprintf() and fscanf()
The fprintf() function is used to write formatted data to a file, similar to printf() for console output. The fscanf() function is used to read formatted data from a file, similar to scanf().
#include < stdio.h>
int main() {
FILE *file = fopen("example.txt", "w"); // Open file for writing
if (file == NULL) {
printf("Unable to open file\\n");
return 1;
}
// Writing data to the file
fprintf(file, "Hello, World!\\n");
fprintf(file, "This is a test file.\\n");
fclose(file); // Close the file
// Reading from the file
file = fopen("example.txt", "r"); // Open file for reading
if (file == NULL) {
printf("Unable to open file\\n");
return 1;
}
char buffer[100];
while (fscanf(file, "%[^\n]%*c", buffer) != EOF) {
printf("%s\\n", buffer);
}
fclose(file); // Close the file
return 0;
}
C fputc() and fgetc()
The fputc() function is used to write a single character to a file, while the fgetc() function is used to read a single character from a file.
#include < stdio.h>
int main() {
FILE *file = fopen("example.txt", "w"); // Open file for writing
if (file == NULL) {
printf("Unable to open file\\n");
return 1;
}
// Writing a single character to the file
fputc('A', file);
fputc('\\n', file);
fputc('B', file);
fputc('\\n', file);
fclose(file); // Close the file
// Reading a single character from the file
file = fopen("example.txt", "r"); // Open file for reading
if (file == NULL) {
printf("Unable to open file\\n");
return 1;
}
char ch;
while ((ch = fgetc(file)) != EOF) {
printf("%c", ch);
}
fclose(file); // Close the file
return 0;
}
C fputs() and fgets()
The fputs() function is used to write a string to a file, while fgets() is used to read a line from a file.
#include < stdio.h>
int main() {
FILE *file = fopen("example.txt", "w"); // Open file for writing
if (file == NULL) {
printf("Unable to open file\\n");
return 1;
}
// Writing a string to the file
fputs("Hello, File Handling in C!\\n", file);
fputs("We are learning file operations.\\n", file);
fclose(file); // Close the file
// Reading from the file
file = fopen("example.txt", "r"); // Open file for reading
if (file == NULL) {
printf("Unable to open file\\n");
return 1;
}
char buffer[100];
while (fgets(buffer, sizeof(buffer), file) != NULL) {
printf("%s", buffer);
}
fclose(file); // Close the file
return 0;
}
C fseek(), rewind(), ftell()
The fseek() function is used to move the file pointer to a specific position in a file. The rewind() function is used to reset the file pointer to the beginning of the file. The ftell() function returns the current position of the file pointer.
#include < stdio.h>
int main() {
FILE *file = fopen("example.txt", "w+"); // Open file for both reading and writing
if (file == NULL) {
printf("Unable to open file\\n");
return 1;
}
// Writing to the file
fputs("This is a test for fseek, ftell and rewind.\\n", file);
// Moving the file pointer and checking the position
fseek(file, 10, SEEK_SET);
long position = ftell(file);
printf("Current file position: %ld\\n", position);
// Rewinding the file
rewind(file);
printf("File pointer moved to the beginning after rewind.\\n");
fclose(file); // Close the file
return 0;
}
C Preprocessor
The C preprocessor is a tool that processes the source code before compilation. It performs various tasks such as macro substitution, file inclusion, conditional compilation, and more. Preprocessor directives are lines of code that begin with the # symbol and tell the preprocessor to perform specific actions before the actual compilation of the program.
C Macros
Macros are preprocessor directives used to define code snippets that can be reused throughout the program. These are defined using the #define directive.
#include < stdio.h>
#define PI 3.14
#define SQUARE(x) ((x) * (x))
int main() {
printf("Value of PI: %f\\n", PI);
printf("Square of 5: %d\\n", SQUARE(5));
return 0;
}
C #include
The #include directive is used to include external files, such as header files, into your program. This is commonly used to include standard libraries or custom header files.
#include < stdio.h > // Standard Library
int main() {
printf("Hello, World!\\n");
return 0;
}
C #define
The #define directive is used to define a macro or constant in C. It replaces all occurrences of the macro with the specified value or code during preprocessing.
#define MAX 100
int main() {
int arr[MAX];
printf("Array size: %d\\n", MAX);
return 0;
}
C #undef
The #undef directive is used to undefine a previously defined macro. This is useful if you want to redefine a macro or prevent its use.
#define MAX 100
#undef MAX // Undefining the macro
int main() {
// Uncommenting the next line will cause an error because MAX is undefined
// printf("Max value: %d\\n", MAX);
return 0;
}
C #ifdef and #ifndef
The #ifdef (if defined) and #ifndef (if not defined) directives are used for conditional compilation. These allow you to include or exclude code depending on whether a macro is defined.
#define DEBUG
#ifdef DEBUG
printf("Debugging is enabled.\\n");
#endif
#ifndef RELEASE
printf("Release version is not defined.\\n");
#endif
int main() {
return 0;
}
C #if and #else
The #if and #else directives allow conditional compilation based on expressions. If the expression evaluates to true, the code block following #if is included. Otherwise, the code after #else is included.
#define VALUE 10
#if VALUE > 5
printf("VALUE is greater than 5.\\n");
#else
printf("VALUE is not greater than 5.\\n");
#endif
int main() {
return 0;
}
C #error
The #error directive is used to generate a compile-time error with a custom error message. This can be useful for checking certain conditions during compilation.
#ifndef MAX
#error "MAX is not defined!"
#endif
int main() {
return 0;
}
C #pragma
The #pragma directive is used to provide additional instructions to the compiler. The functionality of #pragma is compiler-dependent, and it is often used to control optimization, warning messages, or other compiler-specific behavior.
#pragma warning(disable: 4996) // Disable specific warning
int main() {
char str[50];
printf("Enter some text: ");
gets(str); // Deprecated function, but warning is disabled
return 0;
}
C Command Line Arguments
Command-line arguments are values passed to a program when it is executed from the command line or terminal. These arguments are useful for passing data such as filenames, options, or configuration settings without modifying the program’s code. In C, the main() function can accept command-line arguments via its parameters int argc and char *argv[].
Command Line Arguments Structure
The main() function in C can be defined as:
int main(int argc, char *argv[])
- argc (Argument Count): It stores the number of command-line arguments passed to the program, including the program name.
- argv (Argument Vector): It is an array of strings that holds the actual arguments passed to the program.
#include < stdio.h>
int main(int argc, char *argv[]) {
printf("Number of arguments: %d\\n", argc);
for (int i = 0; i < argc; i++) {
printf("Argument %d: %s\\n", i, argv[i]);
}
return 0;
}
Example of Command Line Arguments
Here’s an example of how command-line arguments work. If you compile the above program and run it from the command line like so:
./program arg1 arg2 arg3
The output will be:
Number of arguments: 4 Argument 0: ./program Argument 1: arg1 Argument 2: arg2 Argument 3: arg3
Practical Use Case of Command Line Arguments
Command-line arguments are frequently used in real-world applications. For example, they can be used to pass file names to a program, specify flags for debugging or mode selection, or configure settings dynamically. Let’s look at a more practical example of a program that takes two numbers and adds them:
#include < stdio.h>
#include < stdlib.h>
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Please provide two numbers as arguments!\\n");
return 1;
}
int num1 = atoi(argv[1]); // Convert argument to integer
int num2 = atoi(argv[2]); // Convert argument to integer
printf("Sum: %d\\n", num1 + num2);
return 0;
}
In this example, the program adds two numbers passed as arguments. You can run the program like this:
./add_numbers 5 10
The output will be:
Sum: 15