Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Fundamentals of the C Programming Language

Tech May 10 4

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 like printf. Header files typically have a .h extension.
  • The main function is mandatory in every C program; it's the first function caled upon execution. There can only be one main function per project.
  • printf("Hello, C World!\n"); outputs the specified string to the standard output. The \n is 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

  1. Can include letters (uppercase and lowercase), digits, and underscores.
  2. Cannot start with a digit.
  3. C is case-sensitive (myVar is different from MyVar).
  4. Keywords (reserved words) cannot be used as variable names.
  5. 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:

  1. Literal Constants: Direct values in the code (e.g., 100, 3.14159).
  2. const-qualified Variables: Variables declared with the const keyword, making their values immutable after initialization.
  3. #define Preprocessor Constants: Symbolic constants defined using the #define directive.
  4. Enumeration Constants: Named integer constants defined within an enum type.
#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:

  1. Single-line comments (C99 standard and later): Start with // and extend to the end of the line.
  2. 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;
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.