<< Home

Chapter 3: The C Language Basics

This chapter introduces the essential syntax of C: what makes up a program, how functions are defined and called, how to use comments and names, how input and output work, and how expressions form the flow of computation. From here onward, you will start writing code that actually does things.

The structure of a C program

A C program is a set of declarations and function definitions, with one function named main acting as the entry point. Before functions, you may include headers and declare global variables. The general form looks like this:

#include <stdio.h>

int main(void)
{
  printf("Program skeleton\n");
  return 0;
}

Programs are usually composed of several translation units (.c files) compiled separately and linked together. Each file can include one or more header files (.h) that declare shared interfaces.

Typical source layout

Here's how you might typically layout C source code:

FilePurpose
main.cContains main() and program logic
util.cContains helper functions
util.hDeclares helper function prototypes
MakefileDescribes how to build the program
💡 Place shared declarations in headers, and include them wherever needed. This promotes reuse and consistency across multiple source files.

Functions and the main() entry point

Functions are the building blocks of a C program. Each function has a return type, a name, and (optionally) parameters. The function main() is special because execution begins there. It can take zero or two arguments and must return an int value.

Defining and calling functions

Functions are defined and called like this:

#include <stdio.h>

int add(int a, int b)
{
  return a + b;
}

int main(void)
{
  int sum = add(3, 4);
  printf("Sum is %d\n", sum);
  return 0;
}

The function add() receives two integers, computes a result, and returns it. main() then prints the result using printf(). Every function definition must specify a return type. If there is no meaningful value to return, use void.

Return codes and conventions

By convention, returning 0 from main() indicates success. Any nonzero value signals an error. This pattern lets shell scripts and other programs test whether your program ran successfully.

⚠️ If a function is declared to return a value but reaches the end without a return statement, the result is undefined. Always return explicitly.

Comments, identifiers, and keywords

Comments document your code for readers (including future you). Identifiers name variables, functions, and other symbols. Keywords are reserved by the language and cannot be used as identifiers.

Comment styles

You have a choice of commenting styles:

/* This is a block comment
   that can span multiple lines */

int main(void)
{
  // This is a single-line comment
  printf("Comments ignored by compiler\n");
  return 0;
}

Use comments sparingly but clearly. They should explain *why* code exists, not restate what it does.

Valid identifiers

An identifier can contain letters, digits, and underscores, but it must not start with a digit. Identifiers are case-sensitive. Examples: count, file_index, MAX_SIZE.

Common keywords

Here are some of the more common C keywords:

CategoryExamples
Typesint, char, float, double, void
Controlif, else, for, while, switch, break, continue
Storagestatic, extern, register, auto, const, volatile
Otherreturn, sizeof, typedef, struct, enum, union
💡 Avoid naming identifiers too closely to keywords (for example, integer instead of int) to reduce confusion and improve readability.

Basic I/O and formatted output

C’s standard input and output library, <stdio.h>, provides printf() for writing text and scanf() for reading it. Both use format strings containing placeholders that match variable types.

Printing values

This is how you print values to the screen:

#include <stdio.h>

int main(void)
{
  int age = 30;
  double pi = 3.14159;
  printf("Age: %d\n", age);
  printf("Pi: %.2f\n", pi);
  return 0;
}

Each % marker is replaced by the corresponding argument. The \n creates a newline. Format specifiers control how values are shown: %d for integers, %f for floating-point numbers, %s for strings, and so on.

Reading input

You can read input like this:

#include <stdio.h>

int main(void)
{
  int number;
  printf("Enter a number: ");
  scanf("%d", &number);
  printf("You entered %d\n", number);
  return 0;
}

scanf() requires the address of the variable (hence the & operator). If the format and input do not match, undefined results may occur, so validate carefully.

⚠️ scanf() can be unsafe if not used cautiously. For robust programs, prefer line-based input (fgets() and sscanf()) when processing strings.

Common format specifiers

Here are some of the more common format specifiers:

SpecifierMeaning
%dint
%ldlong
%ffloat or double (depending on context)
%.2ffloating-point with 2 decimal places
%snull-terminated string
%csingle character
💡 Always include a newline at the end of your printf() output when printing to a terminal; many systems buffer text until a newline is written.

Statements, expressions, and blocks

Statements are the executable steps of a program. Expressions compute values. A block (a group of statements in braces) counts as a single compound statement. Understanding this hierarchy is essential to writing readable and structured C code.

Simple and compound statements

Examples of simple and compound statements:

int x = 10;  // simple statement
x = x + 5;   // expression statement

if (x > 12)
{
  printf("x is greater than 12\n");  // part of a compound block
}

Braces define a block scope. Variables declared inside a block are visible only within it. Indentation and consistent style make nested structures easier to follow.

Expressions and side effects

An expression has a value. An expression statement uses that value or triggers a side effect. For example, x++ increments x as a side effect. Control statements such as if or while depend on expressions that evaluate to true (nonzero) or false (zero).

⚠️ Because C allows assignment within expressions (for example if (x = 5)), it’s easy to introduce logic bugs. Use parentheses and clear comparisons (==) to avoid mistakes.

Program flow at a glance

Each statement executes in sequence unless control flow alters it. You’ll explore conditionals and loops in the next chapter, but the foundation (expressions forming statements within blocks) is already in place here.