0% found this document useful (0 votes)
8 views

01-functional-c

Uploaded by

Arnav Shukla
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views

01-functional-c

Uploaded by

Arnav Shukla
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 94

CS136

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.

C was specifically designed to give programmers “low-level” access to memory


(discussed starting in Section 04.

Thousands of popular programs and portions of all popular operating systems


are written in C.

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).

To do so, the OS needs to know where to start running the


program. This is known as the entry point. In C, the entry point is a
special function named main.

By calling main, the OS transfers control flow to the program.

After the program has completed its execution, control flow


returns to the OS.

7
Program Entry Points
In C, the entry point is a special
function named main.

In many interpreted languages


(including Racket), the entry
point is simply the top of the
file you are “executing”.

Every C program must have one (and only one) main function.
8
A Simple Program
int main(void) {
return EXIT_SUCCESS; // success!
}

main has no parameters and int as return type.

The return value communicates to the OS the error code (or exit
code, error number, or just errno).

After successful execution, a program must return 0 (i.e., no error code).


9
A Simple Program
Regarding returning values, main behaves different from any
other function. If no value is returned explicitly, the default value
of EXIT_SUCCESS is returned implicitly.

int main(void) {
// ...
return EXIT_SUCCESS; // return-statement is optional
}

In this course, main does not require a purpose statement, but in


general it is good style to document the purpose of a program.

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).

Parameters are separated by comma (,), and the number of


parameters in the parameter list is not limited.

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.

In a “void-function”, the return-statement is optional. Without


return-statement, control flow implicitly returns to the caller at
the end of the function body.

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.

To call a parameter-less function, put nothing between the


parentheses (i.e., do not pass void):

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;
}

This is because () is used in an older C syntax to indicate an “unknown” or


“arbitrary” number of parameters (beyond the scope of this course).

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.

;; my-func: (anyof Int Str) -> (anyof Bool Sym)


(define (my-func param)
(cond
[(integer? param) true]
[(string? param) "CS 135"]))

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.

The type of the returned value of my_add is an int. The


parameters x and y are also int.

24
Data
Data is stored in memory as a sequence of bits (1 and 0). Eight bits
form one byte.

There are four built-in primitive data types (primitives) in C:


integers (int), characters (char), Boolean values (bool), and
floating point numbers (float).

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).

(The example above uses 8 bit (1 byte) for illustration purposes.)

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).

trace_bool(0) // => false: 0 is interpreted as false


trace_bool(101) // => true: all other int as true
trace_bool(true) // => true: the expressions "true" and
// "false" are pre-defined for convenience

27
Default Types
int my_add(int x, int y) { // properly typed
return x + y;
}

If you omit types in a function definition

my_add(x, y) { // missing types


return x + y;
}

C assumes that missing type are int and may display a warning such as:

> type specifier missing, defaults to 'int'

This is bad style: you should specify every type.


Default Types
Because C uses static typing, there are no functions equivalent to the Racket
type-checking functions (e.g., integer? and string?).

In Racket, a contract violation may cause a “type” runtime error.

(my-add "hello" 3) ; Racket runtime error

In C, it is impossible to violate the contract type. “Type”-based runtime errors


do not exist.

my_add("hello", 3) // does not compile in C

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

Normal arithmetic rules apply.


If necessary, use parentheses to specify the order of operations.

(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

Use parentheses to specify the order of operations.

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

The value of (a % b) is equal to (a - (a / b) * b).

In this course, avoid using % with negative integers.


34
Quiz time!
Which of the following C expressions evaluates to a different value
than the others?
[Select the most appropriate answer!]

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.

true || false; // => 1 (true): or operator


true && false; // => 0 (false): and operator
!true; // => 0 (false): not operator

trace_bool(2); // => true: 2 is interpreted as true


trace_bool(!5); // => false: 5 is interpreted as true

38
Boolean Operators: Equality and Non-equality
The equality operator in C is == (double equal sign).

(3 == 3); // => 1 (true)


(2 == 3); // => 0 (false)

The not equal operator is !=.

(2 != 3); // => 1 (true)


(3 != 3); // => 0 (false)
!(3 == 3); // => not (1) => 0 (false)

Always use a double == for equality (equal operator), not a single =


That is the assignment operator!
39
Boolean Operators: Equality and Non-equality
The accidental use of a single = instead of a double == for equality is one
of the most common programming mistakes in C.
This can be a serious bug.
It is such a serious concern that it warrants an extra slide as a reminder.

40
Boolean Operators: Comparators
The comparison operators <, <=, > and >= behave exactly as you
would expect:

(2 < 3); // => 1 (true)


(2 >= 3); // => 0 (false)
(2 <= 3 && 6 <= 6); // => (1 && 1) => 1 (true)

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

++ -- Postfix (suffix) increment and decrement

() Function call

[] Array subscripting
1 Left-to-right
. Structure and union member access

-> Structure and union member access through pointer

(type) {list} Compound literal

++ -- Prefix increment and decrement

+ - Unary plus and Unary minus

! ~ Logical NOT and Bitwise NOT

(type) Cast
2 Right-to-left
* Indirection (dereference)

& Address-of

sizeof Size-of

_Alignof Alignment requirement 42


Operators: Order of Precedence
Precedence Operator Description Associativity

3 * / % Multiplication, division, and remainder

4 + - Addition and subtraction

5 << >> Bitwise shift left and bitwise shift right

6 < <= => > Relational comparators

7 == != Relational equality and inequality


Left-to-right
8 & Bitwise AND

9 ^ Bitwise XOR (exclusive or)

10 | Bitwise OR (inclusive or)

11 && Logical AND

12 || Logical OR

13 ?: Ternary conditional Right-to-left

43
Operators: Order of Precedence
Precedence Operator Description Associativity

= Assignment

+= -= Assignment by sum and difference

14 *= /= %= Assignment by product, quotient, and remainder Right-to-left


<<= >>= Assignment by bitwise left shift and right shift

&= ^= |= Assignment by bitwise AND, XOR, and OR

15 & Comma Left-to-right

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:

# cond in Racket // if, else if, else in C


(define (my-func ...) int my_func(...) {
(cond if (q_1) {
[(q-1) a-1] return a_1;
[(q-2) a-2] } else if (q_2) {
[(q-3) a-3] return a_2;
[else a-else])) } else if (q_3) {
return a_3;
} else {
return a_else;
}
}
48
Conditionals: if, else if, else
if-statements in C allow us to have functions with conditional
behaviour:

49
Conditionals: if, else if, else

Here, the two if-statements are independent from each other:

50
Conditionals: if, else if, else

// 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;
} else {
return true;
}
}

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;
}
}

> Error: non-void function does not return a value in all


control paths

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;
}
}

bool in_between(int n, int lo, int hi) {


if (n < lo || n > hi) {
return false;
}
return true;
}

bool in_between(int n, int lo, int hi) {


return (lo <= n && n <= hi);
}

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:

(+ y (cond [(< x 0) -x]


[else x]))

• 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.

We revisit if in Section 04 after we understand how “statements” differ from


expressions. For now, only use if as demonstrated.

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!]

A. Always: they are a required part of the syntax


B. Only when nesting if statements
C. Always: they are good style
D. Only when writing multiple if-statements in a row
E. Never: they are confusing

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 (*)

Strings (more on them later):


trace_msg("CS136") // => CS136: tracing a message
trace_string(...) // => "...": tracing strings (char *)

And the array-variations for primitives (more on them later):


trace_array_int(...), trace_array_bool(...),
trace_array_char(...), and trace_array_ptr(...)

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 , 𝑜𝑡ℎ𝑒𝑟𝑤𝑖𝑠𝑒

// fact(n) calculates the factorial of n.


int fact(int n) {
if (n == 1) {
return 1;
} else {
return n * fact(n - 1);
}
}
Recursion in C
A function that calls itself is called recursive.

1, 𝑖𝑓 𝑛 𝑖𝑠 1
𝑓𝑎𝑐𝑡 𝑛 = ቊ
𝑛 ∗ 𝑓𝑎𝑐𝑡 𝑛 − 1 , 𝑜𝑡ℎ𝑒𝑟𝑤𝑖𝑠𝑒

# fact(n) calculates the // fact(n) calculates the


# factorial of n. // factorial of n.
# requires: n >= 1 // requires: n >= 1
(define (fact n) int fact(int n) {
(cond if (n == 1) {
[(= n 1) 1] return 1;
[else } else {
(* n (fact (sub1 n))) return n * fact(n - 1);
] }
}

68
Recursion in C: Simple Recursion
Below is a typical example for simple recursion.

// fact(n) calculates the factorial of n.


int fact(int n) {
if (n == 1) { // base condition:
return 1; // return base value
} else { // recursive condition:
return n * fact(n - 1); // return current value
} // combined with value
} // returned by recursive call,
// with recursive argument one
// step closer to base
// condition

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);
}
}

By calling itself, the function builds up a recursive chain:


Recursion in C: Accumulative Recursion
Below is a typical example for accumulative recursion.

int fact_worker(int n, int acc) { // worker function


if (n == 1) { // base condition:
return acc; // return accumulator
} else { // recursive condition:
return fact_worker(n - 1, // recursive call, with
n * acc); // recursive argument
// one step closer to
} // base condition and
} // accumulator combined
// with current value

// fact(n) calculates the factorial of n.


int fact(int n) {
return fact_worker(n, 1); // accumulator initialized with
} // base value

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);
}

By calling itself, the function builds up a recursive chain:

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;
}

A block end (}) is


• aligned with the line that started it, and
• appears on the following line

75
Select Style Guidelines
int my_add(int x, int y) {
··return x + y;
}

Indent a block by 2 spaces (recommended; or 3 or 4). Most


importantly, be consistent!

76
Select Style Guidelines
int my_add(int x,·int y) {
return x·+·y;
}

Add a space around arithmetic operators and after commas.

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].

In this course, use underscore_style (or snake case) for


identifiers with compound words.

For example: hst_rate, trace_int, or quick_sort.

Use underscore_style in your code!


78
Identifiers
C identifiers can start with a leading underscore (e.g., _name) but they may
interfere with reserved keywords. Avoid them in this course as they may
interfere with Marmoset tests as well.

In C, underscore_style is the most popular style.


In other languages (e.g., Java), camelCaseStyle is a popular alternative, for
instance:

const float densityOfOsmium = 22.59f;


int calcAverageAge(Person[] listOfPersons) {...}

In practice, it is important to use the recommended style for the language


and/or follow the project (or corporate) style guide.
Comments
In C, any text on a line after // is a comment

Any text between /* and */ is also a comment.

// This is a single-line comment.

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

/* */ can extend over multiple lines and is frequently used to


“comment out” code.

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.

; (my-div x y) produces // my_div(x, y) returns the


; the fraction of x / y // fraction of x over y.
; my-div: Int Int -> Num // requires: y is not 0
; requires: y is not 0 int my_div(int x, int y) {
(define (my-div x y) return x / y;
(/ x y)) }

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.

; (my-div x y) produces // my_div(x, y) returns the


; the fraction of x / y // fraction of x over y.
; my-div: Int Int -> Num // requires: y is not 0
; requires: y is not 0 int my_div(int x, int y) {
(define (my-div x y) return x / y;
(/ x y)) }

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

Technically, assert is not a function but a macro.


85
Asserting Requirements
Like in Racket, a requires-statement is part of the documentation
and is therefore not enforced.
Unlike Racket, however, it is possible to enforce requirements
using assertions.

; (my-div x y) produces // my_div(x, y) returns the


; the fraction of x / y // fraction of x over y.
; my-div: Int Int -> Num // requires: y is not 0
; requires: y is not 0 int my_div(int x, int y) {
(define (my-div x y) assert(y != 0);
(/ x y)) return x / y;
}

86
Function Documentation: Asserting Requirements
At the start of each function, you should assert all requirements
using assertions.

// fact(n) calculates the


// factorial of n.
// requires: n must be between 1 and 12 (both inclusive)
int fact(int n) {
assert(1 <= n && n <= 12);
if (n == 1) {
return 1;
} else {
return n * fact(n - 1);
}
}

Whenever feasible, assert any function requirement.


87
Asserting Requirements
If you have a function with more than one requirement:

// 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

assert(x > 0); // good


assert(y < z);
//...
}

It is better to have several small assert statements. That way it is easier to


determine which assertion failed (i.e., which requirement was not met).

88
Testing
Assertion-based Testing
Assertions
The assert-function can be used in place of Racket’s check-
expect.

# Checking values in Racket // Checking values in C


(check-expect assert(my_sqr(7) == 49);
(my-sqr 7) 49)

90
Assertion-based Testing
#include "cs136.h" // more on this later

int my_add(int x, int y) {


return x + y;
}

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!]

A. This code has no problems


B. There is a problem with the documentation
C. There is a syntax error
D. The code always returns the wrong result
E. The code sometimes returns the wrong result

92
Quiz time!
Answers will be discussed in class!

93
End of the Session

• Use the following operators in your code: +, -, Any further


*, /, %, &&, ||, ! , ==, !=, <, >, <=, >=
• Understand the concept operator precedence
questions?
• Be able to use conditionals in your code
• Be able to implement recursive functions
• Be able to document functions
• Memorize basic style regarding whitespace and
identifier naming
• Write a simple C program from scratch

You might also like