Skip to content

Instantly share code, notes, and snippets.

@baijum
Last active July 1, 2025 06:50
Show Gist options
  • Save baijum/62c345006a830902284b010f616e2e9a to your computer and use it in GitHub Desktop.
Save baijum/62c345006a830902284b010f616e2e9a to your computer and use it in GitHub Desktop.

C Programming Tutorial

This tutorial provides an introduction to the C programming language, a powerful and widely used language known for its performance and flexibility.

Getting Started: Your First Program

To begin programming in C, you will need a compiler. A compiler is a program that translates your C code into an executable file that your computer can run. Popular compilers include GCC (GNU Compiler Collection) and Clang.

Important: This tutorial uses C23 (ISO/IEC 9899:2024) features. You must compile all examples with the -std=c23 flag. Modern features like bool, constexpr, nullptr, [[attributes]], and #embed require C23 standard compliance.

Let's start with the classic "Hello, World!" program. This simple program prints a message to the screen and is a great way to ensure your compiler is working correctly.

#include <stdio.h>

int main(void) {
    printf("Hello, World!\n");
    return 0;
}

To compile and run this program:

  1. Save the code in a file named hello.c.

  2. Open a terminal or command prompt.

  3. Compile the code using your compiler with C23 standard. For example, with GCC: gcc -std=c23 -Wall -Wextra -pedantic hello.c -o hello Or with Clang: clang -std=c23 -Wall -Wextra -pedantic hello.c -o hello

  4. Run the executable program: ./hello

You should see the output: Hello, World!

Compiler flags explained:

  • -std=c23: Enforces C23 standard compliance (required for modern C features)
  • -Wall: Enables most common warning messages (e.g., uninitialized variables, unused variables)
  • -Wextra: Enables additional warnings not covered by -Wall (e.g., missing function parameters, comparison between signed and unsigned)
  • -pedantic: Issues warnings for code that doesn't strictly conform to the C standard

Variables and Data Types

Variables are used to store data. In C, every variable has a type, which determines the size and layout of the variable's memory.

Basic Data Types

Here are some of the most common data types in C:

  • int: Used for integers (whole numbers).
  • float and double: Used for floating-point numbers (numbers with a fractional part). double has more precision than float.
  • char: Used for single characters.
  • bool: A boolean type that can hold one of two values: true or false.
#include <stdio.h>

int main(void) {
    int age = 30;
    float temperature = 98.6f;
    char grade = 'A';
    bool is_student = true;

    printf("Age: %d\n", age);
    printf("Temperature: %.1f\n", temperature);
    printf("Grade: %c\n", grade);

    if (is_student) {
        printf("The user is a student.\n");
    }

    return 0;
}

Constants

You can define constants whose values cannot be changed.

  • const: The traditional way to declare a constant.
  • constexpr: A newer way to declare an object whose value is fixed at compile time, which can enable more compiler optimizations.
  • nullptr: This keyword represents a null pointer constant, providing a safer alternative to the NULL macro.
#include <stdio.h>

int get_user_input(void) {
    return 42; // Simulated user input
}

int main(void) {
    // constexpr: Compile-time constant
    constexpr double PI = 22/7.0;
    constexpr int BUFFER_SIZE = 100;

    // const: Runtime constant (value set once, cannot change)
    const int user_value = get_user_input();

    // Array size using constexpr (must be compile-time constant)
    char buffer[BUFFER_SIZE];

    // nullptr: Safe null pointer
    int *ptr = nullptr;

    printf("PI (compile-time): %f\n", PI);
    printf("User value (runtime const): %d\n", user_value);
    printf("Buffer size: %d\n", BUFFER_SIZE);

    if (ptr == nullptr) {
        printf("Pointer is safely null.\n");
    }

    return 0;
}

Control Flow

Control flow statements allow you to direct the execution of your program.

  • if-else: Executes code conditionally.

    int score = 85;
    if (score >= 90) {
        printf("Excellent!\n");
    } else if (score >= 50) {
        printf("Pass.\n");
    } else {
        printf("Fail.\n");
    }
  • switch: Allows you to select one of many code blocks to be executed. The [[fallthrough]] attribute can be used to explicitly indicate that control should flow from one case to the next.

    int option = 2;
    switch (option) {
        case 1:
            printf("Option 1 selected.\n");
            break;
        case 2:
            printf("Option 2 selected, falling through...\n");
            [[fallthrough]];
        case 3:
            printf("...Option 2 or 3 selected.\n");
            break;
        default:
            printf("Invalid option.\n");
            break;
    }
  • Loops (for, while, do-while): Execute a block of code repeatedly.

    // for loop
    for (int i = 0; i < 5; i++) {
        printf("%d ", i);
    }
    printf("\n");
    
    // while loop
    int j = 0;
    while (j < 5) {
        printf("%d ", j);
        j++;
    }
    printf("\n");

Functions

Newer standards introduce attributes that can provide more information about a function's behavior.

  • [[nodiscard]]: Use this attribute to indicate that a function's return value should not be ignored. The compiler is encouraged to issue a warning if it is.
  • [[deprecated]]: Marks a function as obsolete, discouraging its use.
  • [[noreturn]]: Informs the compiler that a function will not return to its caller, for example, if it calls exit().
#include <stdio.h>
#include <stdlib.h>

[[nodiscard]]
int get_value(void) {
    return 42;
}

[[noreturn]]
void terminate_program(void) {
    printf("Exiting...\n");
    exit(0);
}

int main(void) {
    get_value(); // Compiler is encouraged to warn that the return value is ignored.

    int value = get_value();
    printf("The value is: %d\n", value);

    terminate_program();

    // This line will never be reached
    printf("This will not be printed.\n");

    return 0;
}

The Preprocessor

The C preprocessor modifies the source code before compilation. Common preprocessor directives include #include and #define.

  • #include: Includes the content of a header file, such as <stdio.h> for standard input/output functions.
  • #define: Creates a macro, which is a fragment of code that has been given a name.
  • #embed: A newer directive that allows you to embed the content of a file directly into your program as an array of characters. This is useful for including binary assets like images or lookup tables without external tools.
#include <stdio.h>

// Embed a simple text file
const unsigned char message[] = {
    #embed "message.txt"
};

int main(void) {
    // Print the embedded message
    printf("Embedded message: %s\n", message);
    return 0;
}

(Assuming message.txt contains the string "Hello from an embedded file!")

Arrays, Pointers, and Structures

  • Arrays: An array is a collection of items of the same type stored in contiguous memory locations.
  • Pointers: A pointer is a variable that stores the memory address of another variable. They are fundamental to C for tasks like dynamic memory allocation and efficient data manipulation.
  • Structures (struct): A structure allows you to group different data types together under a single name.
#include <stdio.h>
#include <string.h>

// Define a structure for a student
struct Student {
    char name[50];
    int id;
    float gpa;
};

void print_student(struct Student *s) {
    printf("Student Name: %s\n", s->name);
    printf("Student ID: %d\n", s->id);
    printf("GPA: %.2f\n", s->gpa);
}

int main(void) {
    // Array of integers
    int scores[] = {88, 92, 75, 95};
    printf("First score: %d\n", scores[0]);

    // Pointer to an integer
    int my_number = 42;
    int *ptr_to_num = &my_number;
    printf("Value via pointer: %d\n", *ptr_to_num);

    // Using a structure
    struct Student student1;
    strcpy(student1.name, "Jane Doe");
    student1.id = 12345;
    student1.gpa = 3.8f;

    print_student(&student1);

    return 0;
}

This tutorial has covered some of the core features of the C language, including features that make it more robust and modern. With these fundamentals, you can begin to explore more advanced topics and build more complex applications. Happy coding!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment