Learn C Programming

Lesson 1 of 8 · Meet C: Your First Programs, Symbol by Symbol

Module 1 · Meet C: Your First Programs, Symbol by Symbol

Your First C Program, Line by Line

FreeReading

In this lesson

  • Run your first C program in the Playground and read its output.
  • Name every part of the skeleton: directive, function header, body, statement, return.
  • Predict what a program prints when two printf calls share one newline.

Alice types five lines, presses Run, and the screen says Hello, C. She is pleased for about four seconds. Then she deletes the line return 0;, presses Run again, and it still says Hello, C.

So what was that line for? A program that runs is not the same thing as a program you understand. This lesson takes the five lines apart, one word at a time, and puts Alice's line back with a reason.

C has 32 keywords. Your first program uses two of them, so the other 30 can wait.

The five lines, once

Here is the whole program. Do not try to understand it yet. Let its shape settle in your eyes first.

#include <stdio.h>

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

Four things carry meaning here: a line starting with #, a function header, a pair of braces, and two statements inside them. Every C program you write for the rest of your life is a longer version of this shape.

Line 1: the include directive

The # at the start is a signal. It says: this line is not for the compiler. It is for the preprocessor, the tool that runs first and tidies up your file (Module 0, lesson 4).

include is its instruction, and it is blunt: copy the whole of that file in here, right now, before compiling starts.

stdio.h is short for standard input output header. It is a plain text file that came with your compiler. Inside it are the declarations of printf, scanf and about forty more functions.

A declaration is a promise, not the code. Think of a menu: it lists which dishes exist and what each one needs. The kitchen, the real machine code of printf, lives in the C standard library and joins at the linker.

The angle brackets < > send the preprocessor to the system's own folders. Double quotes, as in #include "myfile.h", send it to your project folder first.

So a directive is not a statement, and it takes no semicolon. Put one there and you have pasted a stray ; into your program.

Line 3: the function header

This is where your program starts running. When the operating system launches the program, it calls main. Not the first line in the file. Not the first function you wrote. main, always.

Three pieces, left to right:

  • int is the return type. It promises that when main finishes, it hands one whole number back to the operating system.
  • main is the name, and the C standard fixes it. Main, MAIN and main2 are three different names, and none of them is the one the system calls.
  • (void) is the parameter list. Parameters are values a function accepts, and void here means "this one accepts nothing".

You will also meet int main(int argc, char *argv[]), which is how a program reads the words typed after its name. That is Module 15. Until then, (void) says what you mean.

So int main(void) is a promise and a name, and the machine keeps its half by calling it first.

Lines 4 and 7: the braces

Curly braces mark the body of the function: where it begins and where it ends. Everything between them is what main does.

Braces come in pairs, always. A brace with no partner is the most common beginner mistake in C. The message you get for it is strange, because the compiler only notices at the end of the file.

So if an error names the last line of a file you did not touch, count your braces.

Line 5: the statement, argument by argument

This line is a function call. You are asking printf to do its job, and handing it one thing to work with.

  • A value you hand to a function is an argument. Here the argument is the text "Hello, C.\n".
  • Double quotes make a string literal, which is C's word for a fixed piece of text.
  • \n is an escape sequence. It means newline: move to the start of the next line.
  • The semicolon ends the statement. In C a statement ends where you say it ends, not where the line ends.

Look at \n again, because it surprises everyone. You typed two characters, a backslash and an n. The output holds one character, the invisible one that ends a line. The backslash is not printed; it is an instruction to the compiler about the character that follows it.

So printf("A\nB\n") prints two lines from one call, and the string is 4 characters long, not 6.

Line 6: return 0, and what Alice deleted

main promised an int, so it returns one. By convention 0 means "finished, no problems", and any other number means something went wrong.

That number is the program's exit status, and it is not decoration. The operating system keeps it. On Linux or macOS, run a program and then type echo $? to see the number it returned.

$ ./hello
Hello, C.
$ echo $?
0

This is how a script decides what to do next. Every "build failed, stopping here" message in a CI pipeline is a program that returned something other than zero.

Now Alice's experiment. Since the C99 standard, if control reaches the closing brace of main, the effect is the same as return 0;. Her program was correct because the standard filled the line in for her.

Write it anyway. It is one line, it says what you mean, and main is the only function in C that gets this favour. Leave it out of a function that returns a value and you have a real bug.

So a missing return 0; in main changes nothing, and a missing return anywhere else changes everything.

The skeleton, with every part named

#include <header>        directive: for the preprocessor, no semicolon

int main(void)            return type, name, parameter list
{                         body starts
    statement;            one instruction, ended by a semicolon
    return 0;             the exit status handed to the system
}                         body ends
  • #include pastes in a file of declarations before compiling starts.
  • int is what main gives back; (void) is what it takes, which is nothing.
  • The braces hold the body; they are counted in pairs by the compiler.
  • Every statement inside ends with ;, however many lines it is spread over.
Anatomy of a C program, line by line #include <stdio.h> int main(void) { printf("Hello, C.\n"); return 0; } directive read by the preprocessor, no semicolon function header return type, name, parameter list the body braces always come in pairs one statement: a call, an argument, a semicolon return 0 is the exit status the system reads
Figure 1. The same five lines, with the name of each part. Learn the names now; every error message uses them.
Example 1: the smallest program that does something

Type it by hand. Do not paste it. The mistakes you make while typing are the lesson.

#include <stdio.h>

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

Change the message, press Run again, and watch the output follow you. That loop, edit and run, is the whole job.

Run in Compiler
Example 2: three lines from three calls

Statements run in order, top to bottom. Nothing clever happens between them.

#include <stdio.h>

int main(void)
{
    printf("Welcome to Progsity\n");
    printf("Track: C, from zero\n");
    printf("Part Zero: before you write a single line\n");
    return 0;
}
Welcome to Progsity
Track: C, from zero
Part Zero: before you write a single line

Now delete one \n and run it again. Two lines join into one, which proves the break comes from the escape sequence and not from the separate call.

Run in Compiler
Example 3: the one a beginner actually writes

Given four printf calls, most people immediately draw a box. Count the characters inside the bars; they have to match, or the box leans.

#include <stdio.h>

int main(void)
{
    printf("+----------------+\n");
    printf("|   PROGSITY C   |\n");
    printf("|   Part Zero    |\n");
    printf("+----------------+\n");
    return 0;
}
+----------------+
|   PROGSITY C   |
|   Part Zero    |
+----------------+

Every line here is 18 characters wide. A judge that compares your output to the expected output counts spaces, so a single extra space is a wrong answer. You will meet exactly that in the problems below.

Run in Compiler

Where this is used

  • Git. The file git.c in the Git source holds int main(int argc, const char **argv), the same header with a parameter list, and it returns the status your shell reads after git push.
  • SQLite. The sqlite3 command line tool is one C file, shell.c, whose main reads your queries and returns 0 when you type .quit.
  • GNU coreutils. ls.c ends with a return that carries the exit status, which is why ls missing-file; echo $? prints a number other than zero.
  • Nginx. nginx.c has one main, a few hundred lines long, that reads the configuration and starts the worker processes. Same skeleton, more furniture.

Common mistakes

1. Forgetting the include line, and getting away with it.

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

On the Playground's GCC 12 this is a warning: implicit declaration of function 'printf', and the program builds and prints correctly. The compiler had never heard of printf, guessed its shape, and guessed right this once. GCC 14 makes the same line an error. Add the header; a warning you understand is still a warning to fix.

2. Capital P in Printf.

#include <stdio.h>

int main(void)
{
    Printf("Hello, C.\n");
    return 0;
}

GCC 12 says warning: implicit declaration of function 'Printf' and then the linker says undefined reference to 'Printf'. C is case sensitive, so Printf is a different name, and nothing anywhere defines it. The two messages together are the clue: the compiler believed you, and the linker went looking.

3. Ending a directive with a semicolon.

#include <stdio.h>;

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

GCC 12 says warning: extra tokens at end of #include directive, and then builds the program and runs it. The preprocessor read the header name, found a semicolon it had no use for, and threw it away with a complaint. A directive is not a statement and takes no semicolon, even when getting it wrong costs you nothing today.

4. Trusting the line number in a missing-semicolon error.

printf("Hello, C.\n")
return 0;

GCC says error: expected ';' before 'return' and names the return line. There is nothing wrong with that line. The compiler read on until it hit a word that cannot continue a statement, and complained there. Always check the line above the one named.

Brain teaser

Zara writes this and says it prints two lines. Bob says one. Predict the exact output, character by character, then say how many newline characters leave the program.

#include <stdio.h>

int main(void)
{
    printf("Chittagong");
    printf("Sylhet\n");
    return 0;
}

Then answer the harder half: where would you put one \n, and only one, to make the output two lines with nothing trailing after the second?

The line break is inside the string, not inside the call. Count the \n characters in the source. A shell prompt sitting on the same line as your output is a clue, not a bug.

Exercise 1Easy

Alice sells books at a school fair and needs a sign for her stall. The sign is two lines: a fixed banner, then the word Stall and her stall number.

Input. One line with one integer n, the stall number.

Output. Exactly two lines:

=== PROGSITY ===
Stall n

Constraints. 1 <= n <= 999.

Sample. Input 7 gives === PROGSITY === then Stall 7. The banner has exactly three equals signs on each side and one space inside each pair.

#include <stdio.h>

int main(void)
{
    int n;
    scanf("%d", &n);

    /* Your two printf calls go here. */

    return 0;
}

The input line is written for you; scanf is taught properly in Module 3. This one is graded against hidden tests in the module's Problems lesson, as banner-two-lines.

Run in Compiler
Exercise 2Easy

Maria wants her three subject marks printed as a small card, one subject per line, using three separate printf calls.

Input. One line with three integers a b c, the marks for Maths, Physics and Chemistry.

Output. Three lines, in that order:

Maths: a
Physics: b
Chemistry: c

Constraints. 0 <= a, b, c <= 100.

Sample. Input 90 85 77 gives Maths: 90, Physics: 85, Chemistry: 77, each on its own line. One space after each colon.

#include <stdio.h>

int main(void)
{
    int a, b, c;
    scanf("%d %d %d", &a, &b, &c);

    /* Three printf calls, one per subject. */

    return 0;
}

Graded as three-printf-lines. Use three calls, not one; the point is that statements run in order.

Run in Compiler
Exercise 3Medium

Bob's report line has to carry a tab and a pair of quotation marks. Printing a quote inside a string needs an escape, because a bare quote would end the string early.

Input. One line with one integer s, Bob's score.

Output. Exactly two lines. The tab is one tab character, not spaces:

Bob wrote:	"Hello, C."
Score:	s

Constraints. 0 <= s <= 100.

Sample. Input 95 gives Bob wrote:, a tab, then "Hello, C."; the second line is Score:, a tab, then 95.

#include <stdio.h>

int main(void)
{
    int s;
    scanf("%d", &s);

    /* Two printf calls. You need \t and \" here. */

    return 0;
}

Graded as tab-and-quote. The escape table is in lesson 3 of this module, if you want it before you try.

Run in Compiler

Common doubts

  • Can I write int main() instead of int main(void)?

    It compiles, and in C17 the two mean slightly different things: () says nothing about the parameters, (void) says there are none. Write (void) and you will never be asked about it in a code review.

  • Why is \n one character and not two?

    Because the backslash is not printed. It tells the compiler to store the single invisible character that ends a line. Your file holds two characters, the program's output holds one.

  • Does the indentation matter?

    Not to the compiler, which ignores spaces, tabs and blank lines between tokens. It matters a great deal to the next reader, and lesson 5 of this module is about exactly that.

  • What happens if I return 1 instead of 0?

    The program prints the same thing and tells the operating system it failed. A shell script that checks the status would stop there. Use 0 unless you mean "something went wrong".

  • Do I need to install anything to run these?

    No. The Run link on every example opens the Playground with the program already in it. Installing a local compiler is lesson 6 of this module, and it is optional.

Key takeaways

  • A C program is a directive, a function header, a pair of braces, and statements inside them.
  • #include <stdio.h> pastes in the promises for printf; the real code joins at the linker.
  • main is the fixed name the operating system calls, and int is the status it returns.
  • A statement ends at its semicolon, not at the end of the line.
  • \n is one character, written as two, and it is what makes a new line.
  • Reaching the end of main returns 0 by itself since C99, and you still write the line.

Next you will meet the two kinds of wrong. One the compiler refuses, and one it builds happily while giving you the wrong answer.

End of lesson 1

Mark it done, and your progress moves with you.

Next: Syntax and Semantics: Two Different Kinds of Wrong