01-functional-c
01-functional-c
An Introduction to C
January 11 – 16, 2024
An Introduction to C
Readings: CP:AMA 2.2, 2.3, 2.7, 4.1, 5.1, 9.1
• the ordering of topics is different in the text
• some portions of the above sections have not been covered yet
2
A Brief History of C
C was developed by Dennis Ritchie in 1969–73 to make the Unix operating
system more portable.
It was named “C” because it was a successor to “B”, which was a smaller
version of the language BCPL.
3
A Brief History of C
There are a few different versions of the C standard. In this course,
the C99 standard is used.
4
A Brief History of C
The C11 standard (2011) added some new features to the language, but those
features are not needed in this course.
The C17 standard (2017) only fixes a few bugs from C11.
The C23 standard (2023) was just released in April of this year.
5
The First C Program
Program Entry Points
Function Definitions
Program Entry Points
Typically, a program is launched (or executed) by an Operating
System (OS) through either a shell (e.g., bash) or another program
(e.g., DrRacket).
7
Program Entry Points
In C, the entry point is a special
function named main.
Every C program must have one (and only one) main function.
8
A Simple Program
int main(void) {
return EXIT_SUCCESS; // success!
}
The return value communicates to the OS the error code (or exit
code, error number, or just errno).
int main(void) {
// ...
return EXIT_SUCCESS; // return-statement is optional
}
In this course, your main function should never return a non-zero value, as it
causes Marmoset tests to fail.
10
Function Definitions: Return Type
; Racket function: // C function:
; my-add: Int Int -> Int
(define (my-add x y) int my_add(int x, int y) {
(+ x y)) return x + y;
}
The return type specifies the data type that is returned to the
caller (here: int).
11
Function Definitions: Identifier
; Racket function: // C function:
; my-add: Int Int -> Int
(define (my-add x y) int my_add(int x, int y) {
(+ x y)) return x + y;
}
The identifier of the function is the name by which the function is
called. It (generally) must be unique.
12
Function Definitions: Parameter List
; Racket function: // C function:
; my-add: Int Int -> Int
(define (my-add x y) int my_add(int x, int y) {
(+ x y)) return x + y;
}
A function can receive data from the caller via its parameter list.
Each parameter consists of the data type (here: int) followed by
the identifier (here: x and y).
13
Function Definitions: No Return Value
; Racket function: // C function:
; foo: any -> nothing
; Such a function cannot void do_nothing(int n) {
; exist in Racket! return; // optional
}
Use the void keyword as return type to indicate that a function
returns no value.
14
Function Definitions: No Parameters
; Racket "function": // C function:
; Functions without parameter
; are constants in Racket! int the_answer(void) {
(define (the-answer) 42) return 42;
}
Use the void keyword in the parameter list to indicate a function
has no parameters.
my_num(); // => 42
15
Function Definitions: No Parameters
C allows void to be omitted in a parameter-less function definition:
int the_answer() {
return 42;
}
Always use void to clearly communicate (and enforce) that there are no
parameters:
int the_answer(void) {
return 42;
}
Function Definitions: Function Body
; Racket function: // C function:
; my-add: Int Int -> Int
(define (my-add x y) int my_add(int x, int y) {
(+ x y)) return x + y;
}
Braces indicate the beginning ({) and end (}) of a function body.
17
Function Definitions: Returning Data
; Racket function: // C function:
; my-add: Int Int -> Int
(define (my-add x y) int my_add(int x, int y) {
(+ x y)) return x + y;
}
The return keyword, followed by an expression, is used to return
data back to the caller function.
18
Function Definitions: Return Type
; Racket function: // C function:
; my-add: Int Int -> Int
(define (my-add x y) int my_add(int x, int y) {
(+ x y)) return x + y;
}
The return type specifies the data type that is returned to the
caller (here: int).
The return type and the type yielded by the expression must
match.
19
Quiz time!
Which of the following functions is syntactically incorrect C code?
[Select all that apply!]
A. void add1(int n) {
return n + 1;
}
B. int mult(int m, n) {
return m + n;
}
C. bool is_even?(int a) {
return a % 2 == 0;
}
D. int double(int n) {
return 2 * n;
}
E. int triple(int n) {
3 * n;
} 20
Quiz time!
Answers will be discussed in class!
21
Data Types
Typing
Primitives: Integer (int)
Primitives: Boolean (bool)
Dynamic Typing
Racket uses dynamic typing: types are determined when the
program is running.
The types of param and the produced (“returned”) value are both
dynamic.
We communicated the types in the contract as a comment.
23
Static Typing
; Racket function: // C function:
; my-add: Int Int -> Int
(define (my-add x y) int my_add(int x, int y) {
(+ x y)) return x + y;
}
C uses a static type system: all types must be known when the
program compiles, and the type of an identifier cannot change.
24
Data
Data is stored in memory as a sequence of bits (1 and 0). Eight bits
form one byte.
25
Integer Type
With the int type, 32 bit (4 bytes) of data are interpreted as an
integer (ℤ). An int can have any value between
-2,147,483,648 (INT_MIN) and 2,147,483,647 (INT_MAX).
26
Boolean Type
With the bool type, 32 bit (4 bytes) of data are interpreted as a
binary false (0) or true (any other value).
27
Default Types
int my_add(int x, int y) { // properly typed
return x + y;
}
C assumes that missing type are int and may display a warning such as:
29
Number Types
When working with integer values in C, do not add a leading zero (0) to the
value. For example, do not write 017 if you want to represent the number 17.
A leading zero may seem harmless, but it is not:
trace_int(17);
trace_int(017);
trace_int(0x17);
> 17 => 17
> 017 => 15
> 0x17 => 23
In C, integer values that start with a zero are evaluated in octal (base 8), so
010 is equivalent to decimal 8, 011 is equivalent to decimal 9, and so on.
Integer values that start with 0x are evaluated in hexadecimal (base 16), so
0x10 is equivalent to decimal 16, and decimals 10, 11, ..., 15 are equivalent
to hexadecimal 0xA, 0xB, ... 0xF.
30
Operators
Arithmetic Operators
Boolean Operators
Operator Precedence
Arithmetic Operators
C expressions use traditional infix algebraic notation:
3 + 2; // => 5
5 – 9; // => -4
1 + 3 * 2; // => 7
(1 + 3) * 2; // => 8
Racket uses prefix or Polish notation, e.g., (+ 3 3). Languages that use prefix
(or postfix) notation do not require parenthesis to specify the order of
operations.
32
The Division Operator /
When working with integers, the C division operator (/) truncates (rounds
toward zero) any intermediate values: 9/4 = 2.25 ✘✘ = 2
(4 * 5) / 2; // = 20 / 2 = 10
4 * (5 / 2); // = 4 * 2 = 8
-5 / 2; // = -2
33
The Remainder (Modulo) Operator %
The C modulo operator (%) produces the remainder after integer
division.
9 % 2; // => 1
9 % 3; // => 0
9 % 5; // => 4
-9 % 5; // => -4
A. 2 + 3 + 7 * 2
B. 2 * 19 / 2
C. 99 % 20
D. 19 / 2 + 19 / 2
E. 59 / 3
35
Quiz time!
Answers will be discussed in class!
36
Boolean Algebra
In Boolean algebra, the values of the variables are the truth values
true and false (usually denoted 1 and 0 respectively).
P Q not P P and Q P or Q
False False True False False
False True True False True
True False False False True
True True False True True
P Q !P P && Q P || Q
0 0 1 0 0
0 not 0 1 0 1
not 0 0 0 0 1
not 0 not 0 0 1 1
37
Boolean Operators
Operators that produce a Boolean value (e.g., == and <) will always
produce 0 or 1, which in turn can be interpreted as false or
true.
38
Boolean Operators: Equality and Non-equality
The equality operator in C is == (double equal sign).
40
Boolean Operators: Comparators
The comparison operators <, <=, > and >= behave exactly as you
would expect:
It is always a good idea to add parentheses to make your expressions clear. For
example, > has higher precedence than ==, so the expression 1 == 3 > 0 is
equivalent to 1 == (3 > 0), but it could easily be misunderstood.
41
Operators: Order of Precedence
Precedence Operator Description Associativity
() Function call
[] Array subscripting
1 Left-to-right
. Structure and union member access
(type) Cast
2 Right-to-left
* Indirection (dereference)
& Address-of
sizeof Size-of
12 || Logical OR
43
Operators: Order of Precedence
Precedence Operator Description Associativity
= Assignment
44
Quiz time!
which of the following C expressions evaluates to false?
[Select all that apply!]
A. ((1 + 1) > 1) - 2
B. 3 + 2 >= 2 + 3
C. 5 / 4 && 0
D. 42 != 24
E. 24 % 6
45
Quiz time!
Answers will be discussed in class!
46
Conditionals
if
if – else
if – else if
if – else if – else
Conditionals
Racket’s cond consumes a sequence of “question and answer”-
pairs, where a question is a Boolean expression (or predicate).
Racket functions that have the following cond behaviour can be
re-written in C using if, else if, and else:
49
Conditionals: if, else if, else
50
Conditionals: if, else if, else
There can be more than one return in a function, but only one value is
returned. The function “exits” when the first return is reached.
51
Conditionals: if, else if, else
The function below causes a compile-time error:
// in_between(n, lo, hi) return true if lo <= n <= hi,
// and false otherwise.
bool in_between(int n,
int lo,
int hi) {
if (n < lo) {
return false;
} else if (n > hi) {
return false;
}
}
If a function has a return type other than void, you must ensure that every
path through the function ends with a return-statement.
52
Conditionals: if, else if, else
// Potential approaches for re-writing the function:
bool in_between(int n, int lo, int hi) {
if (n < lo || n > hi) {
return false;
} else {
return true;
}
}
53
Conditionals: if vs. cond
Given the examples we have seen so far, it might appear that cond and if are
the same.
Fundamentally, however, they are quite different:
• The cond statement produces a value and can be used inside of an expression:
• The if statement does not produce a value: it only changes the control flow
through a function and cannot be used to produce a value.
54
Conditionals: if vs. :?
Unlike C’s if statement, the C ternary conditional operator (?:) does produce
a value.
The following expression produces a, if q is non-zero (true), and b otherwise.
q ? a : b
For example:
(n >= 0) ? n : -n // abs(n)
(a > b) ? a : b // max(a, b)
You may use the ?: operator in this course but use it sparingly.
Overusing the ?: operator can make your code hard to follow.
55
Quiz time!
When writing an if-statement when should we use braces and
why should we use them?
[Select the most appropriate response!]
56
Quiz time!
Answers will be discussed in class!
57
Tracing
Tracing Functions
Values that can be traced include all primitives:
trace_int(136) // => 136: tracing integers (int)
trace_bool(true) // => true: tracing Boolean values (bool)
trace_char('a') // => a: tracing characters (char)
trace_ptr(...) // => 0x...: tracing pointers (*)
59
Tracing Expressions
We have provided tracing tools
to help you “see” what your
code is doing. Here, we use
trace_int inside of main to
trace and output an expression.
You can leave the tracing in your code. It is ignored in our tests and does not
affect your results (no need to comment it out).
60
Tracing Expressions
Tracing outputs:
• File name
61
Tracing Expressions
Tracing outputs:
• File name
• Function name
62
Tracing Expressions
Tracing outputs:
• File name
• Function name
• Line number
63
Tracing Expressions
Tracing outputs:
• File name
• Function name
• Line number
• Expression
64
Tracing Expressions
Tracing outputs:
• File name
• Function name
• Line number
• Expression
• Result / return value
65
Recursion
Simple Recursion
Accumulative Recursion
Recursion in C
A function that calls itself is called recursive.
1, 𝑖𝑓 𝑛 𝑖𝑠 1
𝑓𝑎𝑐𝑡 𝑛 = ቊ
𝑛 ∗ 𝑓𝑎𝑐𝑡 𝑛 − 1 , 𝑜𝑡ℎ𝑒𝑟𝑤𝑖𝑠𝑒
1, 𝑖𝑓 𝑛 𝑖𝑠 1
𝑓𝑎𝑐𝑡 𝑛 = ቊ
𝑛 ∗ 𝑓𝑎𝑐𝑡 𝑛 − 1 , 𝑜𝑡ℎ𝑒𝑟𝑤𝑖𝑠𝑒
68
Recursion in C: Simple Recursion
Below is a typical example for simple recursion.
69
Recursive Chain: Simple Recursion
// fact(n) calculates the factorial of n.
int fact(int n) {
if (n == 1) { // base condition
return 1;
} else { // recursive condition
return n * fact(n - 1);
}
}
71
Recursive Chain: Accumulative Recursion
int fact_wrk(int n, int acc) { // worker function
if (n == 1) { // base condition
return acc;
} else { // recursive condition
return fact_wrk(n - 1, n * acc);
}
}
int fact(int n) {
return fact_wrk(n, 1);
}
72
Documentation & Style
You should follow the course style. The course staff and markers may
not understand your code if it is poorly formatted.
Select Style Guidelines
int my_add(int x, int y)·{
return x + y;
}
A block start ( {) appears at the end of a line, not in the next line!
74
Select Style Guidelines
int my_add(int x, int y)·{
return x + y;
}
75
Select Style Guidelines
int my_add(int x, int y) {
··return x + y;
}
76
Select Style Guidelines
int my_add(int x,·int y) {
return x·+·y;
}
77
Identifiers
Every function, variable, and structure requires an identifier (or
name).
C identifiers must start with a letter [a-zA-Z] and can only contain
letters [a-zA-Z], underscores [_], and numbers [0-9].
/* This is a multi-
line comment. */
80
Function Documentation – Purpose Statement
Provide a purpose for every function that shows an example of it
being called, followed by a brief description of what the function
does (not how it does it).
No contract types are necessary since they are part of the
definition.
81
Function Documentation – Requires Statement
Since C is strongly typed, parameter values are already restricted
to the range of values that their types allow.
On occasion, however, further restrictions are necessary. In these
cases, we document these restrictions using a requires-
statement in the function documentation.
82
Quiz time!
Which of the following are valid comments in C?
[Select all that apply!]
A. # this is a comment
B. //this is a comment
C. ;; this is a comment
D. /* this is a comment */
83
Quiz time!
Answers will be discussed in class!
84
Assertions
The assertion-function assert(exp) displays a message and
terminates the program if the expression exp yields 0 (false).
If exp is not 0 (i.e., true), the program just continues to the next
line of code.
int i = 42;
assert(i == 42); // assertion passes,
// moving to next line
assert(i % 2 == 0 && i >= 136); // assertion fails,
// program terminates
86
Function Documentation: Asserting Requirements
At the start of each function, you should assert all requirements
using assertions.
// my_function(x, y, z) ...
// requires: x is positive
// y < z
int my_function(int x, int y, int z) {
assert((x > 0) && (y < z)); // bad
88
Testing
Assertion-based Testing
Assertions
The assert-function can be used in place of Racket’s check-
expect.
90
Assertion-based Testing
#include "cs136.h" // more on this later
int main(void) {
assert(my_add(3, 4) == 7); // x and y are positive
assert(my_add(0, 136) == 136); // x is zero
assert(my_add(136, 0) == 136); // y is zero
assert(my_add(0, 0) == 0); // x and y are zero
assert(my_add(-3, 4) == 1); // x is negative
assert(my_add(3, -4) == -1); // y is negative
assert(my_add(-3, -4) == -7); // x and y are negative
}
We discuss additional testing methods later. For now, test your code with
asserts in your main function as above.
91
Quiz time!
Consider the following code:
// in_between(x, lo, hi) determines if lo <= x <= hi
// requires: lo <= hi
bool in_between(int x, int lo, int hi) {
return lo <= x <= hi;
}
[Select the most appropriate response!]
92
Quiz time!
Answers will be discussed in class!
93
End of the Session