student@ubuntu:~$
c-foundations Lesson 3 10 min read

Introduction to C

From Java to C — your first program, gcc, and the four-stage compilation pipeline

Reading: Hanly & Koffman: §1.1–1.4 (pp. 14–35), §2.4 (pp. 69–71)

Quick check before you start: Can you explain the difference between javac and gcc? If not, read on. If you can describe the four stages of C compilation, skip to Program Structure.

Practice this topic: C Basics skill drill

After this lesson, you will be able to:

  • Write, compile, and run a minimal C program
  • Explain the four stages of gcc compilation (preprocess, compile, assemble, link)
  • Use gcc -Wall -o to compile with warnings enabled
  • Identify the role of #include, main, return, and function prototypes

From Java to C

In CSCD 210, your workflow was:

Hello.java → javac → Hello.class → JVM → CPU

The JVM translated bytecode to machine instructions at runtime. In C, there is no JVM:

hello.c → gcc → hello (executable) → CPU

gcc compiles your source code directly into a native binary that the CPU executes. No interpreter, no virtual machine, no garbage collector. This is why C is fast — and why mistakes are less forgiving.


Your First C Program

Create a file called hello.c:

#include <stdio.h>

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

Compile and run:

gcc -Wall -o hello hello.c
./hello
# Hello, world!

Breaking down the gcc command:

Part Meaning
gcc The GNU C Compiler
-Wall Enable all common warnings — always use this
-o hello Name the output file hello
hello.c The source file

Why ./hello instead of just hello? The current directory (.) is not in your PATH by default. The ./ tells the shell to look in the current directory.


The Four-Stage Pipeline

gcc does not go straight from source to binary. It runs four stages:

hello.c → Preprocessor → Compiler → Assembler → Linker → hello
          (.c → .i)      (.i → .s)  (.s → .o)   (.o → executable)

1. Preprocessing (gcc -E)

The preprocessor handles directives that start with #:

  • #include <stdio.h> — pastes the contents of stdio.h into your file
  • #define PI 3.14 — replaces every PI with 3.14

This is text substitution. No compilation happens yet.

2. Compilation (gcc -S)

The compiler translates C code into assembly language — human-readable instructions for the CPU.

3. Assembly (gcc -c)

The assembler converts assembly into machine code, producing an object file (.o). This is binary, but not yet a complete program.

4. Linking

The linker combines your .o file with library code (like printf from the C standard library) to produce the final executable.

You can see each stage:

gcc -E hello.c -o hello.i    # preprocessed output
gcc -S hello.c -o hello.s    # assembly code
gcc -c hello.c -o hello.o    # object file
gcc hello.o -o hello          # link into executable

In practice, gcc -Wall -o hello hello.c runs all four stages automatically.


Program Structure

Every C program you write this quarter follows this pattern:

/* 1. Preprocessor directives */
#include <stdio.h>

/* 2. Function prototypes */
int add(int a, int b);

/* 3. Main function */
int main(void)
{
    int result = add(3, 4);
    printf("Sum: %d\n", result);
    return 0;
}

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

Key differences from Java

Java C
System.out.println() printf()
public static void main(String[] args) int main(void)
Automatic garbage collection Manual memory management
Classes, objects, inheritance Functions, structs, pointers
import java.util.* #include <stdio.h>

Why prototypes?

C reads top to bottom in a single pass. If main calls add but add is defined below main, the compiler has not seen add yet. A prototype declares the function signature before main so the compiler knows what to expect:

int add(int a, int b);   /* prototype — no body, just the signature */

Without a prototype, gcc -Wall produces a warning about an implicit function declaration. Always declare before you use.

return 0

main returns an int — the program’s exit status. return 0; means success, matching the Unix convention you learned in the processes lesson. A non-zero return signals an error.


Check Your Understanding
What does the -Wall flag tell gcc to do?
ACompile all .c files in the current directory
BLink against all available libraries
CEnable all common compiler warnings
DProduce assembly output instead of an executable
Answer: C. -Wall stands for "Warn all" — it enables a broad set of compiler warnings that catch common mistakes like unused variables, missing return values, and implicit function declarations. It does not generate errors (your code still compiles), but you should treat warnings as bugs and fix them. Always compile with -Wall.

What Comes Next

You compiled and ran your first C program. Next, you will learn about C variables, data types, and printf/scanf for input and output.