Statements

C code is composed of statements. A statement is a command that the program executes. In C, each statement must end with a semicolon, unless specified otherwise.

1
int x = 1;

The above is a variable declaration statement, where the integer variable x is declared and assigned a value of 1.

Multiple statements can be written on a single line:

1
int x; x = 1;

Here, two statements are written on one line. Line breaks between statements aren’t required but are often used for readability.

A single statement can also be split across multiple lines, with semicolons indicating the end of the statement:

1
2
3
4
5
int x;
x
=
1
;

In this example, the statement x = 1; is broken into four lines. The compiler ignores line breaks within a statement.

Even a single semicolon is a valid statement, called an “empty statement,” though it has no effect:

1
;

Expressions

In C, most calculations are performed through expressions. An expression represents a computation that produces a value.

1
1 + 2

The above is an expression that calculates the result of 1 + 2.

An expression followed by a semicolon can also become a statement, though it may not serve a practical purpose:

1
2
8;
3 + 4;

The above are two expressions, which become statements when followed by a semicolon.

The key differences between expressions and statements are:

  • A statement can contain an expression, but an expression alone is not a statement.
  • Expressions always return a value, while statements may or may not. For example, a variable declaration statement like int x = 1; does not return a value.

Statement Blocks

In C, multiple statements can be grouped together using curly braces {} to form a block, also known as a compound statement. A block of statements is treated as a single compound statement.

1
2
3
4
{
int x;
x = 1;
}

In this example, the curly braces create a block of statements. There is no need to add a semicolon after the closing brace.

Whitespace

Whitespace in C mainly serves to help the compiler distinguish between different syntax elements. Where spaces aren’t required for the compiler to differentiate syntax, they are optional and mainly used to enhance readability.

1
2
3
int x = 1;
// Equivalent to:
int x=1;

In the above example, spaces around the assignment operator = are optional since the compiler can interpret the code without them.

Multiple spaces between syntax elements are treated as a single space:

1
int    x =     1;

Here, the extra spaces have no effect on the code.

Whitespace is also used for indentation. While indentation doesn’t affect the program’s functionality, it enhances code readability, especially for distinguishing code blocks. Most C coding styles suggest indenting nested code by four spaces. However, for brevity, this text uses two spaces.

1
2
3
4
5
6
7
// Four-space indentation
if (x > 0)
printf("positive\n");

// Two-space indentation
if (x > 0)
printf("positive\n");

A line containing only spaces is considered a blank line, which the compiler completely ignores.

Comments

Comments are annotations in the code that the compiler ignores, meaning they don’t affect the program. C supports two types of comments.

  1. Block comments, enclosed by /*...*/, can span multiple lines:
1
2
3
4
5
/* Comment */

/*
This is a multi-line comment.
*/

Block comments can also be placed inline:

1
int open(char* s /* file name */, int mode);

In this example, /* file name */ explains the function’s parameter without interfering with the code.

It’s important to close block comments with */, or they may unintentionally span beyond their intended scope.

1
2
3
4
printf("a "); /* Comment 1
printf("b ");
printf("c "); /* Comment 2 */
printf("d ");

In the example above, forgetting to close Comment 1 causes the second comment to include unintended lines of code.

  1. Line comments, introduced in C99, start with // and extend to the end of the line:
1
2
3
// This is a comment

int x = 1; // This is also a comment

Comments cannot be placed within double quotes as they will be treated as part of the string.

1
printf("// hello /* world */ ");

Here, the characters // and /* ... */ are treated as part of the string and have no effect as comments.

When compiling, comments are replaced with spaces. So ~~`min/* space */Value` becomes `min Value` instead of `minValue`.~~

printf()

Basics

This guide often uses the printf() function, which outputs text to the screen. The f in printf stands for “formatted,” meaning you can customize the output.

1
printf("Hello World");

The above command prints the text “Hello World” to the screen.

By default, printf() doesn’t automatically add a newline. To move the cursor to the next line, include the newline character \n at the end of the string:

1
printf("Hello World\n");

To print a string across multiple lines, insert newline characters where needed:

1
printf("Hello\nWorld\n");

The above example can also be written as two separate printf() calls, producing the same output.

1
2
printf("Hello\n");
printf("World\n");

Before using printf(), you must include the standard library header stdio.h at the beginning of your code:

1
2
3
4
5
#include <stdio.h>

int main(void) {
printf("Hello World\n");
}

Placeholders

In printf(), you can specify placeholders in the output text. A “placeholder” is a symbol that will later be replaced with a specific value.

1
2
// Output: There are 3 apples
printf("There are %i apples\n", 3);

In this example, "There are %i apples\n" is the output string, and %i is the placeholder, indicating where a value should be inserted. Placeholders start with the percent sign %, and the second character specifies the data type, such as %i, which expects an integer.

The second argument of printf() provides the value to replace the placeholder. In this case, the integer 3 replaces %i. The resulting output will be: There are 3 apples.

Common placeholders include %i for integers and %s for strings:

1
printf("%s will come tonight\n", "Jane");

Here, %s is a string placeholder, so the second argument of printf() must be a string. In this example, "Jane" is substituted, and the output will be: Jane will come tonight.

You can use multiple placeholders in one printf() call:

1
printf("%s says it is %i o'clock\n", "Ben", 21);

This string contains two placeholders: %s for a string and %i for an integer. They correspond to "Ben" and 21, producing the output: Ben says it is 21 o'clock.

The number of placeholders in the format string must match the number of arguments passed to printf(). For instance, if there are n placeholders, printf() needs n + 1 arguments. Failing to provide the correct number of arguments can lead to unpredictable behavior, like displaying random memory values.

printf() supports a wide range of placeholders, each corresponding to a different data type in C. Here’s a list of common placeholders:

  • %a: Hexadecimal floating point, lowercase.
  • %A: Hexadecimal floating point, uppercase.
  • %c: Single character.
  • %d: Decimal integer.
  • %e: Scientific notation, lowercase ‘e’.
  • %E: Scientific notation, uppercase ‘E’.
  • %i: Integer, similar to %d.
  • %f: Floating point (both float and double types).
  • %g: Floating point with up to 6 significant digits. The integer part is automatically converted to scientific notation once it exceeds 6 digits, and the ‘e’ in the exponent part is lowercase.
  • %G: Same as %g, but with an uppercase ‘E’ in scientific notation.
  • %hd: Decimal short int.
  • %ho: Octal short int.
  • %hx: Hexadecimal short int.
  • %hu: Unsigned short int.
  • %ld: Decimal long int.
  • %lo: Octal long int.
  • %lx: Hexadecimal long int.
  • %lu: Unsigned long int.
  • %lld: Decimal long long int.
  • %llo: Octal long long int.
  • %llx: Hexadecimal long long int.
  • %llu: Unsigned long long int.
  • %Le: Scientific notation for long double.
  • %Lf: long double.
  • %n: Outputs the number of characters printed so far. This placeholder does not output anything but stores the count in a variable.
  • %o: Octal integer.
  • %p: Pointer address.
  • %s: String.
  • %u: Unsigned integer.
  • %x: Hexadecimal integer.
  • %zd: size_t type.
  • %%: Outputs a literal percent sign.

Formatting Output

printf() also allows customization of placeholder output.

  1. Specifying Width
    You can define a minimum width for placeholders.
1
printf("%5d\n", 123); // Output: "  123"

In this example, %5d specifies that the output will occupy at least 5 spaces. Since 123 is only three digits long, two spaces are added before it to meet the width requirement.

By default, values are right-aligned, meaning spaces are added in front. To left-align the value, insert a - after %:

1
printf("%-5d\n", 123); // Output: "123  "

For floating-point numbers, this width refers to the entire number, including decimal places:

1
2
// Output: "  123.450000"
printf("%12f\n", 123.45);

Here, %12f ensures that the output will take up at least 12 characters, including padding and decimal digits.

  1. Always Showing Sign
    By default, printf() only displays the minus sign for negative numbers. To force it to show a plus sign for positive numbers, add a + after %:
1
2
printf("%+d\n", 12);  // Output: +12
printf("%+d\n", -12); // Output: -12
  1. Limiting Decimal Places
    To limit the number of decimal places, you can specify the desired precision with %.2f:
1
2
// Output: Number is 0.50
printf("Number is %.2f\n", 0.5);

This ensures that only two decimal places are shown. You can also combine this with width specification:

1
2
// Output: "  0.50"
printf("%6.2f\n", 0.5);

This format reserves 6 spaces for the entire number, with 2 reserved for decimal places.

You can dynamically specify both the width and precision using arguments:

1
printf("%*.*f\n", 6, 2, 0.5); // Equivalent to printf("%6.2f\n", 0.5);
  1. Partial String Output
    By default, %s outputs the entire string. If you only want to display a portion, use %.[m]s to specify the length, where [m] is the number of characters to display:
1
2
// Output: hello
printf("%.5s\n", "hello world");

Here, %.5s tells printf() to output only the first 5 characters, resulting in hello.

Standard Libraries and Header Files

In C, many functionalities, like printf(), are built into the language. You don’t need to write them from scratch. These built-in functions are part of the standard library. Since these functions are standardized, their behavior is consistent across different systems, ensuring code portability.

Functions are organized into header files, such as stdio.h for printf(). To use a function, you need to include its corresponding header file at the beginning of your program with the #include directive:

1
#include <stdio.h>

This tells the compiler to load the required functions. Note that #include does not require a semicolon.