Post

Introduction to C

Writeup on how to start thinking in programs and C syntax

Introduction to C

Hey everyone, if you are on this page, you are either:

  • taking CS1010
  • learning to code in C
  • my friends trolling my comments section

Either way, your purpose is clear and hopefully I don’t have to convince you why we are learning C!

In this series, I hope to discuss C standard in both a high and low level; teaching you how C code works in your head and also in the computer. I will also be sharing some exam tips and how to better visualise and understand code in general.

If you do not want to read the long in-depth explanations, you are encouraged to refer to my summaries!

Hopefully by the end of this article series, you will be able to understand C (to some extent) and score well for your exams!

Content Overview:

Introduction to C

Let’s take a look at what C programs usually look like:

1
2
3
4
5
1. #include <stdio.h>
2. 
3. int main(void){ 
4.     printf(“Hello World!\n”);
5. }

If you are a complete beginner, or have only coded with python before, I am sure this code may look slightly foreign to you! Don’t worry, I will go through the code line by line with you!

Line 1: #include <stdio.h>

  • We are including the C “standard input/output” library here!
  • stdio.h is a header file which contains most of your important functions that you usually use while coding
  • Some of these functions are:
    • printf (including format specifiers like %f, %s, etc)
    • sscanf
    • getchar
    • fopen

Line 3: int main(void)

  • This is fundamentally a function declaration (we will delve deeper into functions later on)
  • We are essentially declaring the main function to the compiler
  • Why have a main function?
    • It is good for you to organise your own thoughts
    • More importantly, the computer (specifically the OS and the linker) needs a fixed starting address, which is the main function
    • When you run your compiled code, the OS sets up the memory, loads your program, and then jumps directly to the first instruction inside main

💡 CS1010 Tip: It is considered “Best Practice” in C to use void because it makes your code more type-safe. It prevents accidental data from being passed where it isn’t wanted.

Line 4: printf(“Hello World!\n”);

  • We are calling the printf function which ouputs anything you write in the brackets!
    • Make sure that you wrap your text in “”
    • We will cover how to add variables in your output later
    • \n means new line. In C, the output does not automatically move to the next line after outputting the given text/values, so you will have to do it for them!
  • This line of code will output Hello World!

💡 CS1010 Tip: Remember to end your statements with ;

What if you want to print something that is not determined yet? How do we print 2 * 3? Sure we can do this simple calculation in our heads right now, but what if the numbers get very complicated?

The solution is to use the computer to do the calculation for you! Save your brain power for more complicated tasks later on!

Format Specifiers

In this program, we are trying to calculate the tax of a good worth $888. If the tax is 9%, then the total tax value should be 888 * 9/100! Simple math!

How do we output the value though? Would the following example work?

printf("Tax (cents): 888 * 9 / 100");

The answer is no! As said previously, printf will output everything wrapped in “”. This means that it would not consider the math operation you wrote down and just directly print it! The output will therefore be:

Tax (cents): 888 * 9 / 100

How you may ask, should we output the calculated value? The answer is format specifiers.

1
2
3
4
5
1. #include <stdio.h>
2.
3. int main(void){
4.     printf("Tax (cents): %d\n", 888 * 9 / 100);
5. }

Line 4: printf("Tax (cents): %d\n", 888 * 9 / 100);

  • We have a format specifier %d and we have also left the calculations outside of the “text”
    • Format specifiers are a form of placeholders that tell the compiler what type of data to expect and in what position
    • In this example, the %d specifier represents an integer data type, and therefore expects an integer argument
    • The argument is placed outside of the “text” in respective positions according to the positioning of the format specifiers.
  • This code will now output: Tax (cents): 79

Format Specifiers are a great way to output any unknown variable or calculations that you may have in your code. It is also very helpful in print debugging. In CS1010, it is expected knowledge after week 1 so definitely try to familiarise yourself with it!

Here are some of the format specifiers that we frequently use:

SpecifierData TypeDescriptionExample Output
%d or %iintSigned decimal integer42, -15
%uunsigned intUnsigned decimal integer42
%zusize_tUnsigned integer representing memory size40, 8
%ffloat or doubleDecimal floating-point3.141590
%ccharSingle character'A'
%schar[] or char*String of characters"Hello"
%pvoid *Pointer (memory address)0x7ffee1b2c
%x or %Xunsigned intUnsigned hexadecimal2a, 2A

Variables

Lets refer back to the last example:

1
2
3
4
5
1. #include <stdio.h>
2.
3. int main(void){
4.     printf("Tax (cents): %d\n", 888 * 9 / 100);
5. }

The code looks very messy! If we have to input the calculation into the printf function every single time, there will be a lot of wasted energy typing. In the practical exam, we should be coding efficiently and effectively!

So lets try to make our code cleaner! We can do that by using variables.

Variables are named data storage containers for your data. They are able to hold any data types as long as it is declared beforehand.

image

C Variable Declaration

<type> <variable name>;

Example:

  • int x;
  • float y;
  • double z;

C Variable Initialization

<variable name> = <expression>;

Example:

  • x = 5;
  • y = 5.0;

You can declare a variable and initialize it at the same time!

  • int x = 3;

You can also declare multiple variables at once, but be careful when initializing them!

  • int x, y, z;
  • int x, y, z = 5 only sets z = 5!

If you want to declare and initialize all of them in one line, you can do this: int x = 5, y = 6, z = 7;

Variable Naming Restrictions

Obviously there are some restrictions to how you name your variables. This is so that the compiler is able to accurately identify what is a variable. So what is the naming convention?

Allowed characters: letters, digits, and underscore (eg. no spaces)

  • OK: test, test123, test_123, etc
  • NOT OK: test 123, t@est, etc

Start of a variable: letter or underscore

  • C discourages names starting with underscores as those identifiers are “reserved to the implementation” per the C standard (meaning used by the C library, or rarely by the compiler itself)
  • NOT OK: 123test, @test, etc

Not a reserved word or keyword (eg. int/double)

  • NOT OK: double double, int int, etc

Now lets see this in action! Here is an example:

1
2
3
4
5
6
7
8
1. #include <stdio.h>
2.
3. int main(void){
4.     int product_cost = 888;
5.     int gst_rate = 9;
6.     int tax = product_cost * gst_rate / 100;   
7.     printf("Tax (cents): %d\n", tax); 
8. }

Line 4: int product_cost = 888;

  • We are declaring an integer variable named product_cost and assigning the value 888 to it
  • product_cost will now hold/ represent 888

Line 5: int gst_rate = 9;

  • We are declaring an integer variable named gst_rate and assigning the value 9 to it
  • gst_rate will now hold/ represent 9

Line 6: int tax = product_cost * gst_rate / 100;

  • We are declaring an integer variable named tax and assigning the value product_cost * gst_rate / 100 to it
    • Your computer will convert the variables to the value it is representing and calculate this for you!
    • product_cost * gst_rate / 100 === (is equivalent to) 888 * 9 / 100
  • tax will now hold/ represent 888 * 9 / 100 or 79

💡 CS1010 Tip: Try to understand each statement with the specific choice of language that I have used to describe it

Line 7: printf("Tax (cents): %d\n", tax);

  • Now our printf statement is a lot cleaner to read!

Variable Types

What if we want to be more accurate in our gst calculation? Ideally, we would want to include some decimal points right? But integers cannot hold decimal points, so we will have to use a different data type for decimal numbers.

1
2
3
4
5
6
7
8
1. #include <stdio.h>
2.
3. int main(void){
4.     double product_cost = 888;
5.     double gst_rate = 9;
6.     double tax = product_cost * gst_rate / 100;   
7.     printf("Tax (cents): %f\n", tax); 
8. }

Line 4: double product_cost = 888;

  • We are declaring a double variable named product_cost and assigning the value 888 to it
  • Even though we assigned it to be 888, it will actually hold/represent 888.0

Line 6: double tax = product_cost * gst_rate / 100;

  • The tax variable now holds the calculation including decimal points!
  • tax = 79.920000

Bits and Bytes

Now that we have some practice with using variables and data types, we need to consider the pros and cons of each data type. Have you ever thought about how your computer memory works? How much space a data type takes up? What happens when the size of your data is larger than the data type you are using?

💡 CS1010 Tip: This part is very often tested in exams and is quite tricky! Your computer’s memory is made up of a very large number of tiny storage units. The smallest unit is called a bit (short for binary digit).

A bit can only store one of two values:

0 or 1

You can think of a bit like a switch - it is either off (0) or on (1).

Modules like CS2100 (or its CG equivalent) will explore this further at the hardware level. For now, we only need a high-level understanding.

How Can Only 0 and 1 Represent Everything?

If a bit can only hold two values, how can we represent numbers like 6? Or 42? Or even text? The truth is computers don’t use just one bit — they use groups of bits.

When we write numbers using only 0 and 1, we are writing them in binary, also known as base2.

In base10 (decimal), each digit represents a power of 10:

345 = 3×100 + 4×10 + 5×1

This representation is what you are normally familiar with (I hope).

In base-2 (binary), each digit represents a power of 2:

The rightmost bit is 2⁰
Next is 2¹
Then 2²
Then 2³

0 0 0 0
2³ 2² 2¹ 2⁰

Each bit acts like a switch:

If the bit is 1 → we “take” that power of 2.

If the bit is 0 → we “don’t take” it.

We use 0b to represent base2 / binary representation. Similarly, we use 0d to represent base10 / decimal and 0x to represent base16 / hexadecimals.

Think about it: How would you convert binary to hexadecimals and vice versa?

💡 CS1010 Tip: 1 byte = 8 bits

Self Exercise

Represent 67 in binary

Click to reveal answer The answer is 0b01000011.

Represent 129 in binary

Click to reveal answer The answer is 0b10000001.

Represent 255 in binary

Click to reveal answer The answer is 0b111111111.

Every one of our data types have a set amount of bits and bytes assigned to them! It is important to know the limits of your data types!

Here is a breakdown of the standard C data types, assuming a typical 64-bit environment:

Variable TypeSize (Bytes / Bits)Range
char1 byte / 8 bits-128 to 127
( -(27) to 27 - 1 )
intMin 2 bytes. Generally 4 bytes / 32 bits-2,147,483,648 to 2,147,483,647
( -(231) to 231 - 1 )
unsigned intMin 2 bytes. Generally 4 bytes / 32 bits0 to 4,294,967,295
( 0 to 232 - 1 )
longMin 4 bytes. Generally 8 bytes / 64 bits-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
( -(263) to 263 - 1 )
unsigned longMin 4 bytes. Generally 8 bytes / 64 bits0 to 18,446,744,073,709,551,615
( 0 to 264 - 1 )
long long8 bytes / 64 bits-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
( -(263) to 263 - 1 )

💡 CS1010 Tip: Always use double for anything needing decimal points instead of float! Modern computers have plenty of memory to handle the 8 bytes, and the extra precision prevents weird rounding errors in your calculations.

Integer Overflow

We have now represented each data type with the amount of bits that they hold. This also shows the range of values that they can hold. For example integers can only hold ( -(231) to 231 - 1 ).

So what happens when we add 1 to an integer at maximum value (231 - 1)? The integer will overflow and the value will now be reseted to represent the minimum value an integer can hold (-231).

Similarly, when we subtract 1 from an integer at minimum value (-231), the integer will now represent (231 - 1).

Self Exercise

An 8-bit unsigned integer can store values from 0 to 255.
What happens if we compute: 255 + 1 ?

Click to reveal answer The result is 0. Unsigned integers wrap around when they exceed their maximum value. 255 + 1 = 256, but since the maximum is 255, it wraps back to 0.

What is the result of: 250 + 10 ?
(Assume 8-bit unsigned integer)

Click to reveal answer 250 + 10 = 260. Since the maximum is 255, it wraps around. 260 − 256 = 4 So the result is 4.

An 8-bit signed integer can store values from -128 to 127.
What happens if we compute: 127 + 1 ?

Click to reveal answer The result becomes -128. When the maximum value (127) is exceeded, it wraps around to the minimum value (-128).

What is the result of: -128 - 1 ?
(Assume 8-bit signed integer)

Click to reveal answer The result becomes 127. When we go below the minimum value (-128), it wraps around to the maximum value (127).

What is the result of: 100 + 40 ?
(Assume 8-bit signed integer)

Click to reveal answer 100 + 40 = 140. But the maximum signed value is 127. 140 − 256 = -116 So the result is -116.

💡 CS1010 Tip: You can think of overflowing as the value looping around to the other side of the range.

Arithmetic Operators and Assignment Operators

Summary

Format Specifiers

Summary of Format Specifiers:

SpecifierData TypeDescriptionExample Output
%d or %iintSigned decimal integer42, -15
%uunsigned intUnsigned decimal integer42
%zusize_tUnsigned integer representing memory size40, 8
%ffloat or doubleDecimal floating-point3.141590
%ccharSingle character'A'
%schar[] or char*String of characters"Hello"
%pvoid *Pointer (memory address)0x7ffee1b2c
%x or %Xunsigned intUnsigned hexadecimal2a, 2A

Variable Types

Summary of Variable types:

Variable TypeSize (Bytes / Bits)Range
char1 byte / 8 bits-128 to 127
( -(27) to 27 - 1 )
intMin 2 bytes. Generally 4 bytes / 32 bits-2,147,483,648 to 2,147,483,647
( -(231) to 231 - 1 )
unsigned intMin 2 bytes. Generally 4 bytes / 32 bits0 to 4,294,967,295
( 0 to 232 - 1 )
longMin 4 bytes. Generally 8 bytes / 64 bits-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
( -(263) to 263 - 1 )
unsigned longMin 4 bytes. Generally 8 bytes / 64 bits0 to 18,446,744,073,709,551,615
( 0 to 264 - 1 )
long long8 bytes / 64 bits-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
( -(263) to 263 - 1 )

Tips

💡 It is considered “Best Practice” in C to use void because it makes your code more type-safe. It prevents accidental data from being passed where it isn’t wanted.

int main(void)

💡 Remember to end your statements with ;

💡 1 byte = 8 bits

💡 Always use double for anything needing decimal points instead of float! Modern computers have plenty of memory to handle the 8 bytes, and the extra precision prevents weird rounding errors in your calculations.

💡 You can think of overflowing as the value looping around to the other side of the range.

Exam Questions

This post is licensed under CC BY 4.0 by the author.

Trending Tags