Fundamentals of the C Programming Language
The C programming language is a powerful, general-purpose language widely used for system-level programming and application development. It was designed to provide efficient memory manipulation, generate minimal machine code, and operate without extensive runtime support. Despite offering low-level control, C maintains excellent cross-platform compatibility.
Your First C Program
Creating your initial C program is a common first step. A typical C program consists of functions, with the main function serving as the entry point where execution begins. Statements are the smallest units with in a C program. While various integrated development environments (IDEs) like Visual Studio (VS) offer a complete development suite, lighter code editors like VS Code can also be used effectively with appropriate extensions.
Here’s a classic "Hello, World!" example:
#include <stdio.h> // Include standard input-output library
int main() {
// Print "Hello, C World!" to the console
printf("Hello, C World!\n");
return 0; // Indicate successful execution
}
In this code:
#include <stdio.h>is a preprocessor directive that includes the standard input/output header file, necessary for functions likeprintf. Header files typically have a.hextension.- The
mainfunction is mandatory in every C program; it's the first function caled upon execution. There can only be onemainfunction per project. printf("Hello, C World!\n");outputs the specified string to the standard output. The\nis an escape sequence for a newline character.return 0;indicates that the program terminated successfully.
Fundamental Data Types
C provides various data types to represent different kinds of values, allowing for a rich representation of real-world information. Each data type occupies a specific amount of memory.
char // Character type (e.g., 'A', 'b')
short // Short integer type
int // Standard integer type
long // Long integer type
long long // Extra long integer type
float // Single-precision floating-point number
double // Double-precision floating-point number
Memory is typically measured in bits and bytes. The common conversion units are:
- 1 Byte (B) = 8 bits
- 1 Kilobyte (KB) = 1024 Bytes
- 1 Megabyte (MB) = 1024 KB
- 1 Gigabyte (GB) = 1024 MB
- 1 Terabyte (TB) = 1024 GB
- 1 Petabyte (PB) = 1024 TB
The sizeof operator can be used to determine the memory footprint of a data type or variable in bytes.
#include <stdio.h>
int main() {
printf("Size of char: %zu bytes\n", sizeof(char));
printf("Size of int: %zu bytes\n", sizeof(int));
printf("Size of float: %zu bytes\n", sizeof(float));
printf("Size of double: %zu bytes\n", sizeof(double));
return 0;
}
Variables and Constants
In programming, we often deal with values that change (variables) and values that remain fixed (constants).
Variables
Variables are named storage locations that can hold data that may change during program execution. They are declared by specifying a type and a name, and can be optionally initialized.
int userAge = 30; // Integer variable for age
float itemPrice = 19.99f; // Single-precision float for price (note 'f' suffix)
char initialChar = 'J'; // Character variable for an initial
Variable Naming Conventions
- Can include letters (uppercase and lowercase), digits, and underscores.
- Cannot start with a digit.
- C is case-sensitive (
myVaris different fromMyVar). - Keywords (reserved words) cannot be used as variable names.
- Aim for descriptive and meaningful names.
Variable Scope and Lifetime
Variables can be classified as local or global, which affects their visibility (scope) and duration (lifetime).
#include <stdio.h>
int globalIdentifier = 100; // Global variable: declared outside any function
int main() {
int localIdentifier = 50; // Local variable: declared inside main
// If a local variable has the same name as a global variable, the local one takes precedence
int globalIdentifier = 200; // This is a new local variable, shadowing the global one
printf("Local globalIdentifier: %d\n", globalIdentifier); // Prints 200
// To access the global one, you'd need to explicitly qualify it if shadowed or use a different name.
printf("Local variable: %d\n", localIdentifier); // Prints 50
return 0;
}
Scope: The region of code where a variable is accessible. Local variables are confined to the block or function where they are defined, while global variables are accessible throughout the entire program from their point of declaration.
Lifetime: The period during which a variable exists in memory. Local variables typically have a lifetime tied to their scope (created on entry, destroyed on exit). Global variables exist for the entire duration of the program.
When accessing global variables defined in other source files, the extern keyword is used to declare their existence without redefining them.
Variable Input
The scanf function is commonly used for reading input from the user. However, modern Visual Studio versions might issue a security warning (C4996) when using scanf due to potential buffer overflows. A common workaround for learning purposes is to add #define _CRT_SECURE_NO_WARNINGS 1 at the top of your source file or project settings to disable this specific warning.
#include <stdio.h>
#define _CRT_SECURE_NO_WARNINGS 1 // Suppress scanf security warning in MSVC
int main() {
int val1 = 0;
int val2 = 0;
int sumResult = 0;
printf("Enter two integer values: ");
if (scanf("%d %d", &val1, &val2) == 2) { // Check if two integers were successfully read
sumResult = val1 + val2;
printf("The sum is: %d\n", sumResult);
} else {
printf("Invalid input. Please enter two integers.\n");
}
return 0;
}
Constants
Constants represent fixed values that do not change during program execution. C offers several ways to define constants:
- Literal Constants: Direct values in the code (e.g.,
100,3.14159). const-qualified Variables: Variables declared with theconstkeyword, making their values immutable after initialization.#definePreprocessor Constants: Symbolic constants defined using the#definedirective.- Enumeration Constants: Named integer constants defined within an
enumtype.
#include <stdio.h>
// 3. #define preprocessor constant
#define MAX_ITEMS 50
// 4. Enumeration constants
enum Color {
RED, // Default value 0
GREEN, // Default value 1
BLUE // Default value 2
};
int main() {
// 1. Literal constants
int count = 10;
float pi_approx = 3.14f;
// 2. const-qualified variable
const int DAYS_IN_WEEK = 7;
// DAYS_IN_WEEK = 8; // This would cause a compilation error
printf("Maximum items: %d\n", MAX_ITEMS);
printf("Days in week: %d\n", DAYS_IN_WEEK);
printf("Color RED value: %d\n", RED);
printf("Color GREEN value: %d\n", GREEN);
printf("Color BLUE value: %d\n", BLUE);
return 0;
}
Strings and Escape Sequences
String Definition and Length
In C, a "string" is a sequence of characters terminated by a null character (\0). String literals are enclosed in double quotes (e.g., "hello").
The strlen function (from <string.h>) calculates the length of a string by counting characters until it encounters the null terminator, which is not included in the length.
#include <stdio.h>
#include <string.h> // For strlen
int main() {
char greeting[] = "Hello"; // String literal, automatically null-terminated
char charsOnly[] = {'W', 'o', 'r', 'l', 'd'}; // Character array, NOT null-terminated
printf("Greeting: %s (Length: %zu)\n", greeting, strlen(greeting));
// Printing charsOnly might lead to undefined behavior as printf seeks a '\0'
// printf("CharsOnly: %s (Length: %zu)\n", charsOnly, strlen(charsOnly)); // Dangerous!
// To make charsOnly a valid string:
char properString[] = {'P', 'r', 'o', 'p', 'e', 'r', '\0'};
printf("Proper String: %s (Length: %zu)\n", properString, strlen(properString));
return 0;
}
It's crucial to understand that a character array without an explicit \0 at the end is not a C string, and passing it to string-handling functions like printf("%s", ...) or strlen will result in undefined behavior.
Escape Sequences
Escape sequences are special character combinations used within string and character literals to represent non-printable characters or characters that have special meaning in C.
#include <stdio.h>
int main() {
printf("Line 1\nLine 2\n"); // \n for newline
printf("Item\tQuantity\n"); // \t for horizontal tab
printf("This is a backslash: \\ \n"); // \\ for a literal backslash
printf("A single quote: \'\n"); // \' for a literal single quote
printf("A double quote: \"\n"); // \" for a literal double quote
// Example: Print a file path
printf("File path: C:\\Program Files\\MyFolder\\file.txt\n");
// Octal and Hexadecimal representations
printf("Octal 101: %c (ASCII decimal %d)\n", '\101', '\101'); // \ddd (octal) -> 'A'
printf("Hex 41: %c (ASCII decimal %d)\n", '\x41', '\x41'); // \xdd (hexadecimal) -> 'A'
return 0;
}
Common escape sequences include:
Comments
Comments are non-executable text within source code, used to explain code or temporarily disable parts of it. They are ignored by the compiler.
C supports two main types of comments:
- Single-line comments (C99 standard and later): Start with
//and extend to the end of the line. - Multi-line comments (C-style): Start with
/*and end with*/. These cannot be nested.
#include <stdio.h>
int main() {
int value = 100; // This is a single-line comment (C99 style)
/*
* This is a multi-line comment.
* It can span across several lines.
* Note: C-style comments do not support nesting.
*/
printf("Value: %d\n", value);
/*
// int anotherValue = 200; // This line is commented out
*/ // The outer comment ends here, preventing nesting of /* */
return 0;
}
Control Flow: Selection and Iteration
C programs execute instructions sequentially by default. However, control flow statements allow for conditional execution (selection) and repetitive execution (iteration or looping).
Selection Statements (Conditionals)
Selection statements allow a program to choose different execution paths based on conditions. The most common is the if-else statement.
#include <stdio.h>
int main() {
int userChoice = 0;
printf("Do you want to continue (1 for Yes, 0 for No)? ");
scanf("%d", &userChoice);
if (userChoice == 1) {
printf("Proceeding with the action!\n");
} else {
printf("Action cancelled.\n");
}
return 0;
}
Iteration Statements (Loops)
Iteration statements allow a block of code to be executed repeatedly as long as a certain condition is met. The while loop is a fundamental iteration construct.
#include <stdio.h>
int main() {
int counter = 0;
printf("Starting repetitive task...\n");
while (counter < 5) { // Loop will execute as long as counter is less than 5
printf("Task iteration: %d\n", counter + 1);
counter++; // Increment counter in each iteration
}
printf("Task finished after %d iterations.\n", counter);
return 0;
}
Other loop types include for and do-while, which provide different control structures for iteration.
Functions
Functions are self-contained blocks of code designed to perform a specific task. They promote modularity, reusability, and make programs easier to organize and debug.
Consider a simple addition task without functions:
#include <stdio.h>
int main() {
int valA = 10;
int valB = 20;
int sum = valA + valB;
printf("Sum of %d and %d is: %d\n", valA, valB, sum);
int valC = 15;
int valD = 25;
sum = valC + valD; // Repeated logic
printf("Sum of %d and %d is: %d\n", valC, valD, sum);
return 0;
}
By encapsulating the addition logic into a function, we can reuse it without duplicating code:
#include <stdio.h>
// Function to add two integers and return their sum
int addTwoNumbers(int operand1, int operand2) {
return operand1 + operand2;
}
int main() {
int result1 = addTwoNumbers(10, 20);
int result2 = addTwoNumbers(15, 25);
int result3 = addTwoNumbers(100, 50);
printf("Result 1: %d\n", result1);
printf("Result 2: %d\n", result2);
printf("Result 3: %d\n", result3);
return 0;
}
Arrays
An array is a collection of elements of the same data type, stored in contiguous memory locations. Elements are accessed using an index.
Array Declaration and Initialization
Arrays are declared by specifying the data type, name, and size (number of elements) within square brackets []. They can be initialized during declaration.
#include <stdio.h>
int main() {
// Array of 5 integers, initialized
int numbers[5] = {10, 20, 30, 40, 50};
// Character array (string)
char city[] = "London"; // Size automatically determined by "London" + '\0'
// Double array
double temperatures[] = {25.5, 26.1, 24.9}; // Size automatically determined by elements
// Determine array size using sizeof
size_t numElements = sizeof(numbers) / sizeof(numbers[0]);
printf("Number of elements in 'numbers' array: %zu\n", numElements);
return 0;
}
Array Indexing
Array elements are accessed using a zero-based index. The first element is at index 0, the second at index 1, and so on. Attempting to access an index outside the array's bounds (e.g., numbers[5] for an array of size 5) leads to out-of-bounds access, which can cause unpredictable behavior or program crashes.
#include <stdio.h>
int main() {
int dataValues[4] = {100, 200, 300, 400};
printf("Element at index 0: %d\n", dataValues[0]); // Accesses 100
printf("Element at index 2: %d\n", dataValues[2]); // Accesses 300
// Attempting to access dataValues[4] would be out of bounds
// printf("Element at index 4: %d\n", dataValues[4]); // Undefined behavior!
return 0;
}
Array Usage Example
#include <stdio.h>
#define _CRT_SECURE_NO_WARNINGS 1 // Suppress scanf security warning in MSVC
int main() {
int scores[3] = {0}; // Initialize all elements to 0
int k = 0;
printf("Enter 3 scores:\n");
while (k < 3) {
printf("Score %d: ", k + 1);
if (scanf("%d", &scores[k]) != 1) {
printf("Invalid input. Please enter an integer.\n");
// Clear input buffer for next attempt (basic error handling)
while (getchar() != '\n');
continue;
}
k++;
}
printf("\nStored scores: ");
for (k = 0; k < 3; k++) {
printf("%d ", scores[k]);
}
printf("\n");
return 0;
}
Operators
Operators are symbols that tell the compiler to perform specific mathematical, relational, or logical manipulations.
Arithmetic Operators
Perform basic mathematical operations.
+(Addition)-(Subtraction)*(Multiplication)/(Division)%(Modulo - remainder of division)
#include <stdio.h>
int main() {
int quotient = 7 / 2; // Integer division: 3
float resultFloat = 7.0f / 2; // Floating-point division: 3.5
int remainder = 7 % 2; // Modulo: 1 (operands must be integers)
printf("Integer division (7/2): %d\n", quotient);
printf("Float division (7.0f/2): %.1f\n", resultFloat);
printf("Remainder (7%%2): %d\n", remainder);
return 0;
}
Assignment Operators
Used to assign values to variables.
=(Simple assignment)+=,-=,*=,/=,%=(Compound assignments)
int x = 10; // x is 10
x += 5; // x becomes 15 (equivalent to x = x + 5)
Unary Operators
Operate on a single operand.
+(Unary plus, usually does nothing)-(Unary minus, negates a value)++(Increment, adds 1)--(Decrement, subtracts 1)!(Logical NOT, negates a boolean value)~(Bitwise NOT, inverts all bits)&(Address-of, gets memory address)*(Dereference, accesses value at address)sizeof(Size of type or variable in bytes)(type)(Type cast, converts to another type)
#include <stdio.h>
int main() {
int num = 5;
printf("Original num: %d\n", num);
// Increment/Decrement
int post_inc = num++; // post_inc gets 5, then num becomes 6
int pre_dec = --num; // num becomes 5, then pre_dec gets 5
printf("Post-increment: %d, Pre-decrement: %d, num now: %d\n", post_inc, pre_dec, num); // 5, 5, 5
// Logical NOT
int isTrue = 1;
int isFalse = 0;
printf("!isTrue: %d, !isFalse: %d\n", !isTrue, !isFalse); // 0, 1
// sizeof operator
printf("Size of num: %zu bytes\n", sizeof(num)); // Size of int, typically 4
double data_array[10];
printf("Size of data_array: %zu bytes\n", sizeof(data_array)); // Total size of array
printf("Number of elements in data_array: %zu\n", sizeof(data_array) / sizeof(data_array[0]));
// Type casting
double pi = 3.14159;
int truncated_pi = (int)pi; // Converts double to int, truncating decimal part
printf("Truncated pi: %d\n", truncated_pi); // 3
return 0;
}
Relational Operators
Used for comparison, yielding a true (non-zero) or false (zero) result.
==(Equal to)!=(Not equal to)<(Less than)>(Greater than)<=(Less than or equal to)>=(Greater than or equal to)
Important: Comparing C-style strings requires functions like strcmp from <string.h>, not the == operator, as == would compare their memory addresses, not their content.
#include <stdio.h>
#include <string.h> // For strcmp
int main() {
int val_a = 10, val_b = 20;
char str1[] = "apple";
char str2[] = "apple";
char str3[] = "banana";
if (val_a == val_b) printf("val_a is equal to val_b\n");
if (val_a != val_b) printf("val_a is not equal to val_b\n");
// Incorrect string comparison
if (str1 == str2) printf("str1 addresses are equal (INCORRECT for content comparison)\n");
else printf("str1 addresses are not equal\n");
// Correct string comparison
if (strcmp(str1, str2) == 0) printf("str1 content is equal to str2 content\n");
if (strcmp(str1, str3) != 0) printf("str1 content is not equal to str3 content\n");
return 0;
}
Logical Operators
Combine or modify boolean expressions.
&&(Logical AND): True if both operands are true.||(Logical OR): True if at least one operand is true.
#include <stdio.h>
int main() {
int condition1 = (10 > 5); // True (1)
int condition2 = (20 < 10); // False (0)
// Logical AND
if (condition1 && !condition2) { // True && True
printf("Both conditions are met.\n");
}
// Logical OR
if (condition1 || condition2) { // True || False
printf("At least one condition is met.\n");
}
return 0;
}
Conditional (Ternary) Operator
A concise way to write simple if-else statements: condition ? expression_if_true : expression_if_false;
#include <stdio.h>
int main() {
int score = 75;
char* grade = (score >= 60) ? "Pass" : "Fail";
printf("Student's grade: %s\n", grade);
return 0;
}
Comma Operator
Evaluates expressions from left to right, and the value of the entire expression is the value of the rightmost expression.
#include <stdio.h>
int main() {
int x = 1, y = 2, z = 3;
int result = (x++, y = x + 1, z = y * 2); // x becomes 2, y becomes 3, z becomes 6
printf("Result: %d (x: %d, y: %d, z: %d)\n", result, x, y, z); // Output: 6 (x: 2, y: 3, z: 6)
return 0;
}
Other Operators
- Subscript Operator
[]: Used to access elements of an array (e.g.,myArray[index]). - Function Call Operator
(): Used to invoke a function (e.g.,myFunction(arg1, arg2)).
Common Keywords
Keywords are reserved words in C that have predefined meanings and cannot be used as identifiers (variable names, function names, etc.).
While a full list is extensive, here are some key ones:
auto, break, case, char, const, continue, default, do, double, else, enum, extern, float, for, goto, if, int, long, register, return, short, signed, sizeof, static, struct, switch, typedef, union, unsigned, void, volatile, while.
Note that common functions like printf, scanf, and preprocessor directives like #include, #define are not keywords themselves.
auto Keyword
Historically, auto explicitly declared a variable with automatic storage duration, which is the default for local variables. In modern C, it's rarely used explicitly as local variables are auto by default.
int calculateSum(int p1, int p2) {
// auto int tempSum = p1 + p2; // 'auto' is implicit and often omitted
int tempSum = p1 + p2;
return tempSum;
}
register Keyword
The register keyword is a hint to the compiler to store a variable in a CPU register for faster access. However, compilers often optimize register usage independently, so its effect in modern C is minimal and often ignored.
register int loopCount = 0; // Suggests compiler to store loopCount in a register
typedef Keyword
typedef allows you to create an alias (a new name) for an existing data type. This can improve code readability and portability.
#include <stdio.h>
// Create an alias 'uint32' for 'unsigned int'
typedef unsigned int uint32;
int main() {
unsigned int standardVar = 100;
uint32 aliasedVar = 200; // Using the new alias
printf("Standard variable: %u\n", standardVar);
printf("Aliased variable: %u\n", aliasedVar);
return 0;
}
static Keyword
The static keyword has different meanings depending on where it is applied.
static with Local Variables
When applied to a local variable, static changes its storage duration from automatic to static. This means the variable persists throughout the program's lifetime, retaining its value between function calls, but its scope remains local.
#include <stdio.h>
void print_increment() {
static int persistentCounter = 0; // Static local variable
persistentCounter++;
printf("Persistent Counter: %d\n", persistentCounter);
}
void print_normal_increment() {
int volatileCounter = 0; // Normal local variable
volatileCounter++;
printf("Volatile Counter: %d\n", volatileCounter);
}
int main() {
printf("--- Static Local Variable --- \n");
for (int i = 0; i < 3; i++) {
print_increment(); // Outputs 1, 2, 3
}
printf("\n--- Normal Local Variable --- \n");
for (int i = 0; i < 3; i++) {
print_normal_increment(); // Outputs 1, 1, 1
}
return 0;
}
static with Global Variables and Functions
When applied to a global variable or a function, static restricts its visibility (linkage) to the current source file (translation unit). This means the variable or function cannot be accessed from other source files using extern, effectively making it "private" to its defining file. This helps prevent naming conflicts in larger projects.
file1.c:
// static global variable, only visible in file1.c
static int privateGlobalValue = 10;
// static function, only callable from file1.c
static void privateHelperFunction() {
printf("Called private helper in file1.c\n");
}
void publicFunction() {
printf("Public function in file1.c accessing privateGlobalValue: %d\n", privateGlobalValue);
privateHelperFunction();
}
file2.c:
#include <stdio.h>
// Attempting to access privateGlobalValue or privateHelperFunction from file1.c
// extern int privateGlobalValue; // Error: unresolved external symbol
// extern void privateHelperFunction(); // Error: unresolved external symbol
extern void publicFunction(); // This public function can be accessed
int main() {
// printf("%d\n", privateGlobalValue); // Would cause a linker error
publicFunction(); // Works
return 0;
}
Preprocesssor Directives: #define
The C preprocessor modifies the source code before compilation. #define is a common directive used to define symbolic constants or macros.
Defining Symbolic Constants
#define allows you to create a name for a constant value. The preprocessor replaces every occurrence of the name with its value before compilation.
#include <stdio.h>
#define PI_VALUE 3.14159
#define GREETING "Hello from a constant!"
int main() {
printf("Value of PI: %.5f\n", PI_VALUE);
printf("%s\n", GREETING);
int bufferSize = 10;
int dataBuffer[bufferSize]; // Using a variable for array size (C99 feature)
// int dataBuffer[MAX_SIZE]; // If MAX_SIZE was defined as a constant
return 0;
}
Defining Macros
#define can also be used to create function-like macros. These are text replacements, not actual functions, and care must be taken with parentheses to avoid unexpected behavior due to operator precedence.
#include <stdio.h>
// Macro to add two numbers
#define SUM(a, b) ((a) + (b))
// Macro to find the maximum of two numbers
#define FIND_MAX(x, y) ((x) > (y) ? (x) : (y))
int main() {
int num1 = 10, num2 = 5;
int sum_result = SUM(num1, num2); // Replaced as ((10) + (5))
int max_value = FIND_MAX(num1, num2); // Replaced as ((10) > (5) ? (10) : (5))
printf("Sum: %d\n", sum_result);
printf("Max value: %d\n", max_value);
// Demonstrate potential macro issue without parentheses (e.g. 5 * SUM(2,3) would be 5 * 2 + 3 if not ((a)+(b)))
printf("5 * SUM(2,3) = %d\n", 5 * SUM(2,3)); // Correctly evaluates to 5 * 5 = 25
return 0;
}
Pointers
Pointers are fundamental to C programming. A pointer is a variable that stores the memory address of another variable. They enable direct memory manipulation, which is crucial for dynamic memory allocation, efficient array processing, and complex data structures.
Memory Addresses
Computer memory is organized as a sequence of uniquely addressable storage units, typically bytes. Each byte has a unique numerical address. The 'address-of' operator (&) retrieves the memory address of a variable.
#include <stdio.h>
int main() {
int valueData = 42;
printf("Value of valueData: %d\n", valueData);
printf("Memory address of valueData: %p\n", (void*)&valueData); // %p for printing addresses
return 0;
}
A pointer variable is declared with an asterisk (*) before its name, indicating that it will store an address. The type before the asterisk specifies the type of data the pointer "points to."
int numValue = 100;
int* ptrToNum = &numValue; // ptrToNum now holds the address of numValue
Dereferencing Pointers
The dereference operator (*) is used to access the value stored at the memory address pointed to by a pointer variable. This is also called "indirection."
#include <stdio.h>
int main() {
int myData = 25;
int* dataPointer = &myData; // dataPointer stores the address of myData
printf("Value of myData: %d\n", myData);
printf("Address stored in dataPointer: %p\n", (void*)dataPointer);
printf("Value at address pointed to by dataPointer (dereferenced): %d\n", *dataPointer); // Accesses myData's value
*dataPointer = 50; // Modify myData's value through the pointer
printf("New value of myData after dereferencing: %d\n", myData); // myData is now 50
return 0;
}
Pointer Size
The size of a pointer variable itself is constant on a given system architecture, regardless of the data type it points to. This size is determined by the system's address bus width (e.g., 32-bit systems typically use 4 bytes for addresses, 64-bit systems use 8 bytes).
#include <stdio.h>
int main() {
printf("Size of int* : %zu bytes\n", sizeof(int*));
printf("Size of char*: %zu bytes\n", sizeof(char*));
printf("Size of double*: %zu bytes\n", sizeof(double*));
printf("Size of void*: %zu bytes\n", sizeof(void*)); // Generic pointer type
return 0;
}
You'll observe that all these pointer types typically have the same size (e.g., 4 or 8 bytes), as they all store a memory address.
Structures (struct)
C provides structures (struct) as a way to group variables of different data types under a single name. This allows you to create custom data types to represent complex real-world entities, similar to objects in object-oriented languages (though without methods or inheritance).
Consider representing information about a book. It might have a title (string), author (string), number of pages (integer), and price (float). A struct allows you to bundle these related attributes.
#include <stdio.h>
#include <string.h> // For strcpy
// Define a structure named 'Book'
struct Book {
char title[50]; // Character array for book title
char author[30]; // Character array for author's name
int pages; // Integer for number of pages
float price; // Float for the book's price
};
int main() {
// Declare and initialize a struct variable
struct Book myFirstBook = {"The C Chronicles", "Dr. Coder", 450, 39.99f};
// Access members using the dot (.) operator
printf("Book Title: %s\n", myFirstBook.title);
printf("Author: %s\n", myFirstBook.author);
printf("Pages: %d\n", myFirstBook.pages);
printf("Price: %.2f\n", myFirstBook.price);
// You can also create another struct variable and assign values
struct Book anotherBook;
strcpy(anotherBook.title, "Pointers Unveiled"); // Use strcpy for string assignment
strcpy(anotherBook.author, "A. Pointer");
anotherBook.pages = 280;
anotherBook.price = 24.50f;
printf("\nAnother Book Title: %s\n", anotherBook.title);
printf("Another Book Author: %s\n", anotherBook.author);
return 0;
}