This tutorial provides an introduction to the C programming language, a powerful and widely used language known for its performance and flexibility.
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;
}
-
Save the code in a file named
hello.c
. -
Open a terminal or command prompt.
-
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
-
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 are used to store data. In C, every variable has a type, which determines the size and layout of the variable's memory.
Here are some of the most common data types in C:
int
: Used for integers (whole numbers).float
anddouble
: Used for floating-point numbers (numbers with a fractional part).double
has more precision thanfloat
.char
: Used for single characters.bool
: A boolean type that can hold one of two values:true
orfalse
.
#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;
}
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 theNULL
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 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");
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 callsexit()
.
#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 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: 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!