CS 136 - 1 - Functional C

1.1 - History and Expressions

A Brief History

C got its name by the creator, Dennis Ritchie in 1969-73 based on the other programming language at-the-time B. C has "low-level" access to memory (talked about later), thousands of programs are written in it, and portions of every single operating system use C in some capacity.

Remember, C reads from RIGHT TO LEFT.

C Versions

We use the C99 standard (from 1999), but there is also C11 (2011), C18 (2018), and C23 (2024).

Comments

Use // for single line
Use /* comments go here */ for multi-line.

C's multi-line comments cannot be nested. For example,
/* comment /* goes here, */ okay?*/
throws an error.

Expressions

THEY USE INFIX, NO MORE STUPID RACKET. You can type 3+3 which is evaluated to 6.

Operators

There is over 40 operators and the order is complicated (see CP:AMA Appendix A).

Attention

C does not have an exponential operator (e.g. xn)

The / operator

When working with integers, the C division operator (/) truncates the decimals (runs towards zero).
For example, 5/22.

The % operator

The C modulo operator (%) produces the remainder after integer division.

For example, 5 % 21.

Attention

It is often best to avoid the % operator when working with negative integers

1.2 - Identifiers and Functions

C identifiers

Every function, variable, and structure requires an identifier ("name"). They must start with a letter, and can only contain letters, underscores or numbers.

Attention

In this course, you must use underscore_style (snake case) if there are compound words.
For example, hst_rate or tree_height

Anatomy of a function

Example

{2070449D-CC95-4653-9694-B9533997478B}.png

To call this function, we type my_add(2,3);.

We would then say the values 2 and 3 are sent to become a and b within my_add, and in this case 2 and 3 are called arguments.

Static Type System

C uses a static type system: all types must be known before the program is run and the type of an identifier cannot change. For example, if we set int a, it (1) must be set like that before the program is run, and (2) cannot be changed later on.

Something like my_add("hello", 3) would not even run in C.

Info

In C, integer values that start with a zero are evaluated in octal (base 8) and integer values that start with 0x are evaluated in hexadecimal.

Functions without parameters

Info

The word "void" means "nothing". If you don't use "void", C will still understand it. However, this is bad form and always use "void" to clearly communicate that there are no parameters.

For example, calling my_num() 422
Pasted image 20260107181355.png

No nested functions

You can't define a function inside another function.

Function documentation

You are required to provide a purpose for every function that shows an example of it being called, followed by a brief description of what the function does. The purpose below are on lines 1 and 2. Contract types are unnecessary.
Pasted image 20260107181810.png

Whitespace

C mostly ignores whitespace. However, you want to include it for ease of reading and the CS 136 Style Guide.

1.3 - The main Function and Tracing Code

Files in a C program

When programming in C, we encounter 4 types of files:

Entry point

C is a compiled language and therefore C programs can be run directly by the hardware via the OS. However, we have to compile the human-readable C code to machine-readable (binary) code. If no errors are found, compilers will produce a single executable file. During this process, compiled files (extension .o/.ll) can be optionally created. The executable file has machine-readable code.

Attention

In C, the entry point (instead of being the top of the file) is the main function. Every C program must have one (and only one) main function.

main

main has no parameters and an int return type. It has one parameter which is "void". The return value communicates to the OS the "error code". A successful program returns zero.

Attention

Our main functions should never return a non-zero value.

This is a simple "Hello World!" in C.
Pasted image 20260107183626.png

The printf function

Producing output is considered a side-effect.
The printf function is part of the C library. To call it, we #include directive - in this case, stdio.h.

Attention

In C, top-level expressions (code outside of a function) is not allowed.

Tracing Expressions

Leave tracing in the code. Marmoset ignores it and it is actually very helpful.
{C999EF11-4A21-47B5-8358-0859B6554BC4}.png

Tracing Tools Documentation

Info

/****************************************************************************
TRACING TOOLS
****************************************************************************/

// These tracing tools can be used to help debug your code.
// They will not interfere with Marmoset tests or any I/O testing.

// trace_msg(msg) Displays msg in the console

// trace_X(exp) displays a message in the console of the form:
// exp => final value

// X can be one of: int, long, bool, char, double, string, ptr, symbol

// example usage:
// trace_msg("Hello, World!");
// trace_int(1 + 1);

// trace_array_Y(arr, len) displays a message in the console of the form:
// arr => [arr[0], arr[1], ...., arr[len-1]]

// Y can be one of: int, bool, char, double, ptr, symbol

// example usage:
// int a[6] = {4, 8, 15, 16, 23, 42};
// trace_array_int(a, 6);

// trace_printf(str, arg1, arg2, ...) displays a message in the console
// using the printf format specifier syntax
// note: adds a newline automatically
// WARNING: unlike printf, it does NOT detect errors or mismatches between the
// number or type of format specifiers so bad combinations such as
// trace_printf("%s") or trace_printf("%s", 42) may crash

// example usage:
// trace_printf("the value of x is %d and y is %d", x, y);
// trace_off() Turns off all tracing messages
// [by default they are turned on]
void trace_off(void);

// trace_sync() "Synchronizes" tracing and printf output by
// forcing all of the tracing messages to go to the same
// stream as printf (stdout)
// NOTE: this may cause your Marmoset and I/O tests to fail
void trace_sync(void);

// trace_version() displays the current version of the cs136 tools library
void trace_version(void);

Program Documentation

Document a program at the top of the file. There is no need to add documentation for main itself.

Wrong Order

If you call a function in main, it must above the main function, or you can just declare the function which tells the compiler that you will define it later.
{1070FC13-07BC-44C0-A52B-351E8741180A}.png

1.4 - Testing Code

Boolean Expressions

In C, Boolean expressions do not produce true or false. They produce either:

Comparison operators

The equality operator in C is == (yes, that's two equal symbols.)

(3==3) 1 (true)
(3==2) 0 (false)

The not equal operator is !=.

(3 != 3) 0 (false)
(3 != 2) 1 (true)

The operators <,<=,> and >= behave exactly as expected.

(2<3) 1 (true)
(63>=3) 1 (true)

Logical operators

The Logical operators are: ! (not), && (and), || (or):

!(3==3) 0 (false)
!(3==3) && (2<3) 0 (false)
(3==3) && (2<3) 1 (true)
!(3==3) || (2<3) 1 (true)

Attention

When the value of an expression is known, C will short-circuit.

All non-zero values are true

Operators that produce a Boolean value will always produce either 0 or 1. So, any non-zero value is "true".

The value NULL is also considered false.

For example,
!(5) ==0

bool type

Assertions

the assert function is similar to check-expect's in Racket. We will be required to use it to formally test our code.

The way assert works is if it catches an error, it will give an output. If all the "tests" pass, then it says nothing.

For example, this will give an error
{A3CD7A17-B455-4726-93F6-79C6B6E7B03D}.png

This is the corrected file:
{02153B44-2065-492A-95F3-E0C9187899A9}.png

Function requirements

Attention

Assert any feasible function requirements!

For example,
{E0F98411-0E10-4CB2-8E39-833ADB2C90F3}.png
The reason assert(y) also works is because if y is 0, then when 0 is passed into assert, it is false and throws an error.

Infeasible Requirements

Sometimes, it is extremely inifficient to assert, so it is good style to communicate that a requirement is not asserted as follows:
{357F42EE-6877-4A2B-AABE-18237BBF1CAD}.png

Multiple Requirements

If there are multiple requirements, it is better to have small asserts to determine the exact requirement that is not met.

For example,
{87B87206-8F94-4661-AB4F-729287D81501}.png

How to avoid code bugs

Keep it simple stupid. Keep it clean. Keep it as manageable as possible that you can manage yourself. By doing so, you ensure your code is easier to understand, is less prone to errors and is easier to debug.

Some tips:

Developing good tests

Determining the source of code bugs

Follow these steps:

  1. Fix one bug at a time. This is typically faster than trying to fix more than one thing at once.
  2. Try to find a simple test case that triggers the bug.
  3. Come up with an educated guess about what could be wrong.
  4. Come up with a way to test the hypothesis.
Tip: Keep a log of bugs

In a file, keep logs of bugs you encounter. You can then refer back to them and also it will help to remember the problem and the fix to avoid creating the same problem in the future.

1.5 - Statements and control flow

The return statement is a special kind of statement known as a control flow statement. This means that the return "controls the flow" of the program by ending the function and returning to caller.

Types of control flow

We explore four types of control flow:

Compound Statements (Blocks)

Blocks ({}) can contain multiple statements.
Statements are executed in sequence (one after the other) until the end of the function or a return statement. Return statements end the function.
{A37F70C7-D079-430A-B08D-99F120C64E5E}.png

Function calls

As an example:
{241986F8-CA69-433A-8105-0025B88AB2C5}.png
Produces:
{6EA5D3F7-D26B-42D7-AAD8-90A32FDC05C2}.png

Conditionals (if)

The syntax of if is
if (expression) statement

This is how all if statements should be styled:
{1CB5C077-AD6B-420B-9757-B52A9708C333}.png

Conditionals (else)

Can be combined with if if there are two conditions.

For example,
{DE0F1139-9D67-4144-B70F-839556E68D28}.png

Example of recursion in C

{73D1861E-86BA-4F11-82B8-057A701F6BED}.png
{4CB47112-34FF-4DC9-85B7-EA7B6A142E87}.png
{8B17E083-435F-4711-8D2D-3E63FE96AE2E}.png

Accumulative version of Fibonacci Sequence
{AEE1F7CB-C42A-4239-A801-32530664ADB9}.png

Accumulative version of factorial (!):
{31C7E6E4-7B4F-455F-90F4-FD3D10E59888}.png

Conditionals (else if)

When there are more than two possible results, use else if. Use this style:
{DC5F1DC8-C965-4CAB-8ACE-EBA571520E5F}.png

Conditionals and return values

Other C conditional operators

The C switch control flow statement has a similar structure to else if and cond, but very different behaviour. Do not use it in this course.

The C goto control flow statement is one of the most disparaged language features in the history of computer science because it can make "spaghetti code" that is hard to understand.

Tracing