C Primer (Part 1)

Welcome to learning C! This primer contains syntax of basic features of C.
For most students, this primer will be mostly review, but be sure to take a look at the highlighted sections for important/interesting details!


Variables and comments

When defining variables in C, we define them based on their type. Note that the end of each line in C is marked by a semi-colon (;).

Single-line comments start with //, while multi-line comments start with /* and when with */.

Types are not known after the program is compiled, only the amount of bytes the variable takes up in memory.

// This is a single line comment. /* This is a multi-line comment. */ char c = '\t'; // One byte // Typically used to represent ASCII characters // Numeric variables short s = 2; // Two bytes whole numbers int n = 10; // Four bytes, whole numbers long l = 300; // Eight bytes, whole numbers float f = 0.12f; // Four bytes, decimal numbers double d = 1220934.93419; // Eight bytes, more precise decimal numbers

Variables in C are mutable, so we can change their value once defined:

int c = 320; // the variable c is initialized to 320 c = 300; // the value of c is changed to 300

You can also leave a variable undefined, without initializing the variable to an initial value. If you access the value of the variable before it is given a value, this behavior is undefined in C – you can get any random value!

short s; // the variable is undefined char ch = 'p'; short e = s; // this is undefined behavior! s = 4; // the variable's value is changed to 4

Naming Conventions

General guidelines for good naming:

Signed vs Unsigned Numbers

The range of values that can be represented by each numerical type is dependent on the number of bytes allocated and whether or not the variable is signed or unsigned.

// x can go from 0 to 4,294,967,295 unsigned int x; // y and z can go from -2,147,483,648 to 2,147,483,648 int y; signed int z;

Note that the signed nature of y is implicit in the declaration of the variable (i.e. numbers are signed by default in C). These ranges fluctuate dependent on the type and whether or not decimals are supported.


Booleans in C are defined by the bool keyword. You can combine them together using boolean logic, where &&, ||, ! are the and, or and not operations respectively in C.

bool x = true; bool y = false; bool a = x && y; // and - only true if x and y are both true bool b = x || y; // or - true if one of x or y are true bool c = !x; // not - true if x is false, and vice versa

In C, booleans are really just hidden integers. 0 is treated as false, while any non-zero integer is treated as true (typically represented by 1).

You can also use relational operators to produce boolean values:

Operator Meaning
A > B True (1) if A is greater than B, false (0) otherwise
A < B True (1) if A is less than B, false (0) otherwise
A >= B True (1) if A is greater than or equal to B, false (0) otherwise
A <= B True (1) if A is less than or equal to B, false (0) otherwise
A == B True (1) if A is exactly equal to B, false (0) otherwise
A != B True (1) if A is not equal to B, false (0) otherwise

Conditionals: Using if and else

We can use if and else to determine what code is executed based on boolean values.

int y = 2; char ch; if (3 > 5) { // If the above is true, we'll enter this branch, // defined by the {} braces ch = '4' } else if (y) { // You can use another branch for another condition. ch = 'p' } else { // If both of the above conditionals are false, this will run. ch = '\n' }

The code above sets the value of ch to 'p'.

Defining functions and scope

To define a function, we specify a return type (or void if there is none), a function name, and function arguments.

// the function oper takes in a char and a short, and returns an int // in this case, the function always returns the value 4 int oper(char ch, short r) { return 4; }

You can also call a function using its name and the appropriate passed arguments, by their number and type:

int fibonacci(int n) { if (n == 0) { return 0; } else if (n == 1) { return 1; } else { return fibonacci(n - 1) + fibonacci(n - 2); } }

C passes arguments ‘by value’ to a function when it is called. For any variable passed to the function as an argument, a copy is made of its value in a different place in memory, which is then given to the function. Thus any changes to the argument in the function is not reflected in the variable passed in.

void changeVar(int n) { n = 3; } int main() { n = 10; changeVar(n); // n is still 10 }

The main function is the first function in your program that is executed, at program startup. There are two widely accepted signatures for main:

int main() { ... } int main(int argc, char* argv[]) { ... }

These arguments are how command-line arguments, given when the program is run in terminal (see Compiling and Running your Code), are passed into the program. These arguments are given as an array to argv, with the first element being the name of C executable. (argv is an array of strings - see part 2 of this primer for more details). argc is the number of elements in argv - 1 more than the number of command-line arguments.

For example, if the C program test.c is compiled to the executable test, then we can run the program as ./test 3 I love CS 300!. The command line arguments are spaces separated - 3, I, love, CS, and 300!. Thus argc is 6 and argv is ["./test", "3", "I", "love", "CS", "300!"].


Each variable is accessible only in some portion of code, called its scope. You can typically think of a variable’s scope as starting where it is declared and ending at the next closing curly bracket }. Global variables are those defined outside any functions, and can be accessed and modified inside all functions. Local variables are those defined inside functions, and are only accessible within those said functions.

int a = 0; // start of a's scope (global variable) int main() { int b = 3; // start of b's scope (local variable) a = 10; // a can be accessed anywhere b = 2; } // end of b's scope void f(int x) { // start of x's scope short c = 2; // start of c's scope b--; // here is outside of b's scope and // this will cause a compilation error a = 3; } // end of c's and x's scope // end of a's scope (end of file)

Scopes are also defined by if/else statements, and for and while loops. If two variables are in scope at the same time, the one with the inner-most scope supercedes the other.

int a = 0; int main() { int b = 4; int c, d; for(int x = 1; x < b; x++) { // start of x's scope a += x; } // end of x's scope { int b = 10; // start of inner b's scope c = b; // c is 10 (refers to inner b) } // end of inner b's scope int d = b; // d is 4, refers to outer b }


Input and output operations in C are defined by C Standard Input and Output Library. This can be imported by including #include <stdio.h> at the top of your C file (refer to part 2 for more information on headers). The library includes the function printf:

#include <stdio.h> int main(){ printf("Hello World!\n"); // printf tells your computer to print something to the console, // while the \n says to leave a line break afterwards }

To print variables to the console, include a format specifier in the string to be printed, at the place where you want the variable to be. Then pass the variable as an additional argument to printf. All variables passed in addition to printf will replace the format specifiers in the string their given order.

The format specifier determines how to format the variable as a string, which includes:

#include <stdio.h> int main() { int numOfTAs = 18; short numOfHTAs = 4; char ch = 'A'; // Declaring and initializing a pointer (more on this in Part 2!) int* pointer = &numOfHTAs; printf("There are %d TAs and %d HTAs, in CS300\n", numOfTAs, numOfHTAs); printf("My favorite letter is %c, whose ASCII code is %d.\n", ch, ch); printf("The pointer to a variable I wrote above is %p.\n", pointer); }

Example output:

There are 18 TAs and 4 HTAs, in CS300
My favorite letter is A, whose ASCII code is 65.
The pointer to a variable I wrote above is 0x7ffd1e2a375a. 


You can define for and while loops in C, just as you can in many other languages. for loops are often used for iteration, and define an initial condition, a condition to continue iteration, and an operation to complete each iteration. while loops simply include a condition to continue iteration.

#include <stdio.h> int main() { int myIterator = 1; /* while loop: * 1. If myIterator is less than or equal to 5, continue to * the next step. Otherwise the loop ends. * 2. Lines 13-14 are executed, then repeat step 1. */ while (myIterator <= 5) { printf("Did you know that myIterator is now: %d!\n", myIterator); myIterator += 2; } /* for loop * 1. outer is initialized to 2. * 2. If outer is less than 5, continue to the next step. * Otherwise, the loop ends. * 3. Lines 25-31 are executed. * 4. outer is increased by 1 (outer++). Repeat from step 2. */ for (int outer = 2; outer < 5; outer++) { printf("Look at how high I can count!\n"); for (int inner = 1; inner <= outer; inner++) { printf("%d ", inner); } printf("\n"); } }


Did you know that myIterator is now: 1!
Did you know that myIterator is now: 3!
Did you know that myIterator is now: 5!
Look at how high I can count!
1 2
Look at how high I can count!
1 2 3
Look at how high I can count!
1 2 3 4

Compiling and Running your Code

Programs written in the C lanuage are converted into executable files by programs called compilers. In this class, we use the GCC compiler (which stands for GNU Compiler Collection). To compile a C file, all you need to do is pass the file’s path as an argument into the gcc command. For example, for the file loops.c, we would enter the following in the bash terminal:

$ gcc loops.c

Barring any errors in compiling, this should produce an executable binary file called a.out. This is just the default name for a file that has been compiled. It is an abbreviated form of name “assembler output”, as the last stage of the compilation process.

To run an executable binary file, type a ./ followed by the name of the file, like so:

$ ./a.out

To give the compiler a target file name for the executable, you have to provide the -o flag followed by the target name:

$ gcc loops.c -o loops

This will create another executable binary named loops, which can be executed by:

$ ./loops

Acknowledgments and Extra Materials: USNA Intro to C Programming, Declaration of Variables in C, Decision and Branching Concepts - Booleans, C++ Reference - Scope