C notes

Published: (February 13, 2026 at 05:59 AM EST)
8 min read
Source: Dev.to

Source: Dev.to

// == Preprocessor directives for include and macro definitions ====================
// (The lines below demonstrate how to include headers and define macros)

#include "math_utils.h"   // custom header (must exist)
#include       // for portable int64 types
#include 
#include         // use this instead of primitive built‑in types
#include 
#include 
#include           // for time()

// -----------------------------------------------------------------------------
// Type aliases (typedefs)
// -----------------------------------------------------------------------------
// Signed integers
typedef int8_t   i8;
typedef int16_t  i16;
typedef int32_t  i32;
typedef int64_t  i64;

// Unsigned integers
typedef uint8_t  u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;

// Floating‑point numbers
typedef float    f32;
typedef double   f64;

// Boolean types (using integer aliases)
typedef i8   b8;
typedef i32  b32;

// -----------------------------------------------------------------------------
// Structures and typedefs
// -----------------------------------------------------------------------------
struct vec2f {
    f32 x;
    f64 y;
};

typedef struct {
    i8  a;
    i16 b;
} vec1f;

// -----------------------------------------------------------------------------
// Enumerations
// -----------------------------------------------------------------------------
typedef enum { TYPE_INT, TYPE_FLOAT } Type;

typedef enum {
    POSITION_LOW  = 0,
    POSITION_MID  = 1,
    POSITION_HIGH = 2
} Position;

// -----------------------------------------------------------------------------
// Variant type (enum + union)
// -----------------------------------------------------------------------------
typedef struct {
    Type type;
    union {
        int   i;
        float f;
    } value;
} Variant;

// -----------------------------------------------------------------------------
// Function prototype (C contract)
// -----------------------------------------------------------------------------
void greet(void);

// -----------------------------------------------------------------------------
// Macro definitions
// -----------------------------------------------------------------------------
#define PI  3.14
#define MAX(x, y) ((x) > (y) ? (x) : (y))

// -----------------------------------------------------------------------------
// Union example
// -----------------------------------------------------------------------------
union Data {
    int   i;
    float f;
};

// ===========================================================================
// Main program
// ===========================================================================
int main(void) {

    // -------------------------------------------------------------------------
    // Primitive variables
    // -------------------------------------------------------------------------
    int   my_age = 19;
    char  grade   = 'A';

    i32   age    = 90;
    i64   weight = 909;

    // -------------------------------------------------------------------------
    // Using macros
    // -------------------------------------------------------------------------
    f32 actual_age = PI;
    printf("Using Macros: %f\n", actual_age);

    i32 num1 = 6;
    i32 num2 = 8;
    printf("max of num1 and num2 is: %d\n", MAX(num1, num2));

    bool is_tall = true;
    printf("tall: %d\n", is_tall);
    b8 is_short = true;
    printf("short: %d\n", is_short);

    // -------------------------------------------------------------------------
    // Enums
    // -------------------------------------------------------------------------
    printf("\n--- Enums ---\n");
    Position pos = POSITION_HIGH;

    if (pos == POSITION_HIGH) {
        printf("Position is HIGH (%d)\n", pos);
    } else if (pos == POSITION_MID) {
        printf("Position is MID (%d)\n", pos);
    } else if (pos == POSITION_LOW) {
        printf("Position is LOW (%d)\n", pos);
    }

    // -------------------------------------------------------------------------
    // Random numbers (stdlib + time)
    // -------------------------------------------------------------------------
    srand((unsigned int)time(NULL));   // seed once

    int random_number = rand();                // 0 … RAND_MAX
    i32 random_0_9   = rand() % 10;             // 0 … 9
    i32 random_1_100 = (rand() % 100) + 1;     // 1 … 100

    printf("Random number (0 - RAND_MAX): %d\n", random_number);
    printf("Random number (0 - 9): %d\n", random_0_9);
    printf("Random number (1 - 100): %d\n", random_1_100);

    // -------------------------------------------------------------------------
    // Structures
    // -------------------------------------------------------------------------
    struct vec2f v;
    v.x = 18.0f;
    v.y = 19.0;

    // Struct with typedef – designated initializer
    vec1f v1 = {13, 14};
    printf("v1 vector is %d, %d \n", v1.a, v1.b);
    printf("Size of struct: %zu bytes\n", sizeof(v1));

    vec1f v3 = {
        .a = 89,   // if a is not set it becomes zero automatically
        .b = 89    // positional arguments
    };
    printf("Vector v3 = \n", v3.a, v3.b);

    // Compound literal (NOT a cast)
    v3 = (vec1f){ .a = 9, .b = 10 };
    printf("Vector v3 after compound literal = \n", v3.a, v3.b);

    // Print struct members individually
    printf("vector is (%.2f, %.2lf)\n", v.x, v.y);
    printf("Vector v1 = \n", v1.a, v1.b);

    printf("age is: %d\n", age);
    printf("Weight is: %" PRId64 "\n", weight);
    // PRId64 expands to either "lld" or "ld" depending on the platform

    // -------------------------------------------------------------------------
    // Unions
    // -------------------------------------------------------------------------
    union Data d;
    d.i = 10;
    printf("d.i = %d\n", d.i);

    d.f = 3.14f;   // overwrites same memory
    printf("d.f = %f\n", d.f);
    printf("Size of union: %zu bytes\n", sizeof(d));

    // -------------------------------------------------------------------------
    // Variant (enum + union)
    // -------------------------------------------------------------------------
    Variant va;
    va.type        = TYPE_INT;
    va.value.i     = 42;

    if (va.type == TYPE_INT) {
        printf("int = %d\n", va.value.i);
    }

    // -------------------------------------------------------------------------
    // Pointers
    // -------------------------------------------------------------------------
    i32 xx  = 89;                 // variable stored in RAM
    i32 *pxx = &xx;               // pointer to xx
    printf("memory address of xx is %p and the real value is %d\n", (void *)pxx, xx);

    *pxx = 130;                   // modify the value via the pointer
    printf("after change – address %p, value %d\n", (void *)pxx, xx);

    // Pointer to struct
    vec1f v4  = {.a = 19, .b = 23};
    vec1f *pv4 = &v4;
    printf("initial vector before pointer %d %d\n", v4.a, v4.b);

    // Method 1 – explicit dereference
    (*pv4).a = 20;
    (*pv4).b = 16;
    printf("after explicit deref %d %d\n", v4.a, v4.b);

    // Method 2 – arrow operator
    pv4->a = 25;
    pv4->b = 16;
    printf("after arrow operator %d %d\n", v4.a, v4.b);

    // -------------------------------------------------------------------------
    // Arrays
    // -------------------------------------------------------------------------
    i32 numbers[] = {1, 2, 3, 4};
    printf("index 0 is: %d\n", numbers[0]);

    // Loop through the array
    for (i32 i = 0; i < 3; ++i) {
        printf("%d ", numbers[i]);
    }
    printf("\n");

    // Array name decays to pointer to first element
    *numbers = 6;               // numbers[0] = 6
    *(numbers + 1) = 90;        // numbers[1] = 90

    for (i32 i = 0; i < (i32)(sizeof(numbers) / sizeof(numbers[0])); ++i) {
        printf("%d ", numbers[i]);
    }
    printf("\n");

    // -------------------------------------------------------------------------
    // Dynamic memory (malloc)
    // -------------------------------------------------------------------------
    printf("\n--- Dynamic Memory (malloc) ---\n");
    i32 count = 4;

    // Allocate memory for 4 i32 values
    i32 *dyn_array = (i32 *)malloc(count * sizeof(i32));
    if (!dyn_array) {
        perror("malloc failed");
        return EXIT_FAILURE;
    }

    // Initialise and print
    for (i32 i = 0; i < count; ++i) {
        dyn_array[i] = i * 10;
        printf("%d ", dyn_array[i]);
    }
    printf("\n");

    // Clean up
    free(dyn_array);

    return 0;
}

Elements

i32 *dyn_numbers = malloc(count * sizeof(i32));
/* If i32 = 4 bytes and count = 4
   malloc(4 * 4) = malloc(16 bytes) */

if (dyn_numbers == NULL) {
    printf("Memory allocation failed!\n");
    return 1;
}

/* assign values */
for (i32 i = 0; i < count; i++) {
    dyn_numbers[i] = (i + 1) * 10;
}

/* print values */
for (i32 i = 0; i < count; i++) {
    printf("%d ", dyn_numbers[i]);
}
printf("\n");

/* always free heap memory */
free(dyn_numbers);
dyn_numbers = NULL;   /* good practice */

Using calloc (zero‑initialized)

printf("\n--- Dynamic Memory (calloc) ---\n");

i32 *zero_numbers = calloc(4, sizeof(i32));

if (!zero_numbers) {
    printf("Calloc failed!\n");
    return 1;               /* or return EXIT_FAILURE */
}

for (i32 i = 0; i < 4; i++) {
    printf("%d ", zero_numbers[i]);   /* prints 0 0 0 0 */
}
printf("\n");

free(zero_numbers);
zero_numbers = NULL;

realloc (resize memory)

printf("\n--- Realloc Example ---\n");

i32 *resize_array = malloc(2 * sizeof(i32));

resize_array[0] = 1;
resize_array[1] = 2;

/* increase size to 4 elements */
resize_array = realloc(resize_array, 4 * sizeof(i32));

resize_array[2] = 3;
resize_array[3] = 4;

for (i32 i = 0; i < 4; i++) {
    printf("%d ", resize_array[i]);
}
printf("\n");

free(resize_array);
resize_array = NULL;

Functions

/* ---------------- Functions ---------------- */
greet();

/* import files from different source */
i32 res = add(2, 5);
printf("Imported math utility function result: %d\n", res);

/* math_utils.h
#ifndef MATH_UTILS_H          // Include guard (prevents double inclusion)
#define MATH_UTILS_H

// Function declarations (prototypes)
int add(int a, int b);
int multiply(int a, int b);

#endif
*/

/* math_utils.c
#include "math_utils.h"

// Function definitions
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
*/

/* compile & run
gcc main.c math_utils.c -o program && ./program
*/

return 0;
}

/* C processes files from top to bottom; you need a contract to tell C this */
void greet() { printf("hello there"); }

/* compile with sanitizers and warnings
gcc -Wall -Wextra -g -fsanitize=address -o main main.c && ./main
*/

/* What each flag does:
- **-Wall**   enable common warnings  
- **-Wextra**  enable extra warnings  
- **-g**     include debug symbols  
- **-fsanitize=address** enable AddressSanitizer (useful for learning and debugging)  

Example with stricter settings:
gcc -Wall -Wextra -Werror -O0 -g -fsanitize=address -o main main.c
*/

Format Specifiers

SpecifierType
%dint
%ppointer
%ffloat
%lfdouble
%lldlong long

Compilation Examples

# basic compile & run
gcc main.c math_utils.c -o program && ./program

# with warnings and sanitizers
gcc main.c math_utils.c -Wall -Wextra -pedantic -o program && ./program

struct vs union

struct

  • Memory layout – each member has its own independent memory location; members are stored contiguously (with possible padding for alignment).
  • Size – sum of the sizes of all members plus any padding.
  • Usage – all members can be accessed simultaneously; ideal for records where every field is needed (e.g., a student record, an employee, a 2‑D point).

union

  • Memory layout – all members share the same starting address; only one member holds a valid value at any time.
  • Size – equal to the size of its largest member.
  • Usage – saves memory when a variable can hold one of several types but never more than one at once (common in embedded systems). Writing to one member overwrites any previously stored value in another member.
0 views
Back to Blog

Related posts

Read more »

Module-01--Interview Question _JAVA

1. How many data types are in Java? - Primitive Data Types 8 total: byte, short, int, long, float, double, char, boolean - Non‑Primitive Reference Data Types:...

I Fixed Windows Native Development

Build Requirements: Install Visual Studio > If you’re lucky enough not to know this yet, I envy you. Unfortunately, at this point even Boromir knows… Well put,...