Table of Contents

CS50 Notes -- mode: org --

1 Data Types

1.1 Further reading:

1.2 CS50 Short

1.2.1 Data types and variables:

Data types are a unique characteristic of C(?). In other higher level languages, you don't usually have to declare the type of a variable - you just declare it and start using it.

In C we need to specify the data type of every variable we create the first time we use that variable.

1.2.2 C built-in data types:

  1. Qualifiers

    Some qualifiers are: unsigned, short, long, const.

  2. int
    1. Characteristics

      It is used for variables that will store integers. Integers always take up 4 bytes of memory (32 bits). The implication is that the range of values they can store is limited to 32 bits of information.

    2. Range

      By convention, it was decided that this range of 32 bits would be split into two, and half of that range allocated to positive ints, and the other to negative ints. The range is -2(31st power) to 2(31st power)-1 (-1 due to make space for the zero). Roughly, that's from -2,000,000,000 to 2,000,000,000.

    3. Qualifier "unsigned"

      Unsigned int is not a separate type of variable, but rather a qualifier.

      Unsigned means that it doubles the range of values an integer can take at the expense of no longer allowing to take on negative integers.

      Range

      Useful for cases where we know that the value will never be negative, and is less than 4,000,000,000 (0 to 2 to the 32nd power -1).

  3. char
    1. Characteristics

      The char data type is used for variables that will store single characters.

      Characters always take 1 byte of memory (8 bits). This means that the range of values store is limited to 8 bits worth of information.

      ASCII provides a mapping of characters to numeric values (positive side of range only)

      Useful link: https://www.asciichart.com/

    2. Range

      -128 to 127

  4. float
    1. Characteristics

      The float (floating point number) data type is used for variables that will store floating-point values, also known as real numbers.

      Floats always take up 4 bytes of memory (32 bits).

    2. Range

      The precision of a float is 32 bits(?). There is a precision problem, and we are limited in how precise we can be with our decimal part. For instance, we cannot have a precise value where the decimal part is 100 or 200 digits long, due to us having only 32 bits to work with.

      This can be remedied by using the double data type.

  5. double
    1. Characteristics

      The double data type is used for variables that will store floating-point values, like the float data type.

    2. Range

      The difference between double and float is that doubles are double precision. They always take up 8 bytes of memory (64 bits). With more bits to work with, we can specify more precise real numbers.

  6. void
    1. Characteristics

      void is not a data type, but it is a type.

      Functions can have a void return type, meaning that they do not return a value. Parameter lists for a function can also be void, meaning they do not take input.

      void can be thought of as a placeholder for "nothing".

1.2.3 CS50 library data types

  1. bool
    1. Characteristics

      The bool data type is used for variables that will store a Boolean value.

    2. Range

      true - false

  2. string
    1. Characteristics

      The string data type is used for variables that will store a series of chars.

1.2.4 Other

  • structures (structs)
  • defined types (typedefs)

1.2.5 How to work with variables

  • First, assign a variable a type
  • Secondly, give it a name.

Examples:

int number;
char letter;

int height, width;
float sqrt2, sqrt3, pi;

In general, it's good practice to only declare variables when they're needed.

Using a variable:

  • After a variable has been declared it is no longer necessary to specify that variable's type - this would probably result in an error while compiling.
int number;	// declaration
number = 17;	// assignment
int number = 17; // initialization

2 Operators

2.1 CS50 Short

2.1.1 Arithmetic Operators

We can add (+), subtract (-), multiply (*) and divide (/) numbers, as expected.

We also have the modulus (%) operator, which gives us the remainder when the number on the left of the operator is divided by the number on the right.

  1. Shorthand

    This works with all five basic arithmetic operators.

    x = x *5; x *= 5;

    Incrementation/decrementation:

    x++; x–;

2.1.2 Boolean Expressions

Boolean expressions are used for comparing values.

All boolean expressions can only evaluate to true or false.

Boolean expressions can be used for decisions in conditionals or loops.

In C, every nonzero value is equivalent to TRUE, and zero is FALSE.

  1. Logical operators

    Logical AND (&&):

        x          y       (x && y) 
       true      true        true   
       true      false       false  
       false     true        false  
       false     false       false  

    Logical OR (||):

        x          y       (x || y) 
       true      true        true   
       true      false       true   
       false     true        true   
       false     false       false  

    Logical NOT (!):

       x     !x  
     true   false
    false   true 

    / /

  2. Relational operators
    • Less than (x < y)
    • Less than or equal to (x <= y)
    • Greater than (x > y)
    • Greater than or equal to (x >= y)

    Equality and Inequality

    • Equality (x == y)
    • Inequality (x != y)

3 Conditional Statements

3.1 CS50 Short

3.1.1 Conditionals

  1. If, Else If, Else statements

    Conditional expressions allow programs to make decisions and take different paths depending on the values of variables or user input.

    C provides a few different ways to implement conditional expressions (branches).

    if (boolean-expression)
    {
    }
    

    If the boolean expression above evalues to true, all lines of code between the curly braces will exexute from top to bottom. However, if it evaluates to false, the lines of code will not execute.

    if (boolean-expression)
    {
    }
    
    else
    {
    }
    

    In the same way, if the boolean expression evalues to false, all lines of code between the else curly braces will execute from top to bottom.

    If else chains - mutually exclusive branches:

    if (boolean-expression1)
    {
    	// first branch
    }
    else if (boolean-expression2)
    {
    	// second branch
    }
    else
    {
    	// third branch
    }
    
    

    Non-mutually exclusive branches:

    if (boolean-expression1)
    {
    	// first branch
    }
    if (boolean-expression2)
    {
    	// second branch
    }
    else
    {
    	// third branch
    }
    
    

    Caution: the else will only bind to the nearest if statement.

  2. switch()

    The switch statement allows specification of distinct cases instead of relying on boolean expressions to make decisions.

    int x = GetInt();
    switch(x)
    {
    case 1:
    	printf("One!\n");
    	break;
    case 2:
    	printf("Two!\n");
    	break;
    case 3:
    	printf("Three!\n");
    	break;
    default:
    	printf("Sorry!\n");
    }
    
    

    However, if we intend to fall through one of more cases, we can:

    int x = GetInt();
    switch(x)
    {
    case 3:
    	printf("Three!\n");
    case 2:
    	printf("Two!\n");
    case 1:
    	printf("One!\n");
    default:
    	printf("Boom!\n");
    }
    
    

    This will take input e.g. 2 and then print:

    Two! One! Boom!

  3. Ternary operator (?:)
    int x;
    if (expr)
    {
    x = 5;
    }		
    else
    {
    x = 6
    }
    
    // Instead, we could do:
    
    int x = (expr) ? 5 : 6;
    
    
    • Those two snippets of code act identically.
    • The ternary operator (?:) is mostly a cute trick, but is useful for writing trivially short conditional branches. A good thing to be familiar with, even if I prefer not to use it.
  4. Use cases
    • if (and if else, if-…-else)

    Use Boolean expressions to make decisions

    • switch

    Use discrete cases to make decisions

    • ?:

    Use to replace a very simple if-else (fancy points)

4 Loops

4.1 CS50 Short

4.1.1 Loops

  1. Infinite loop
    while (true)
    {
    }
    
    

    In this case, the lines of code between the curly braces will execute repeatedly from top to bottom, until and unless we break out of it (as if with a break; statement) or otherwise kill our program.

  2. Evaluate w/ boolean expression
    while (boolean-expr)
    {
    }
    
    

    If the boolean-expr evaluates to true, all lines of code will be executed until boolean-expr evaluates to false.

  3. do while loop
    do
    {
    }
    while (boolean-expr);
    
    

    In this case, the code will always execute once, and then check if the boolean expression is true. The code will repeat while boolean-expr is true, and stop once it evaluates to false.

  4. for loop

    for loops are syntactically unattractive, but for loops are used to repeat the body of a loop a specified number of times.

    for (int i = 0; i < 10; i++)
    {
    }
    
    for (start; expr; increment)
    {
    }
    
    

    The process:

    • The counter variable(s) (i) is set.
    • The boolean expression is checked.

    – If it evaluates to true, the body of the loop executes – If it evaluates to false, the body of the loop does not execute

    • The counter variable is increment and then the Boolean expression is checked again, etc.
  5. Use cases
    • while

    Use when we want a loop to repeat an unknown number of times, and possibly not at all. Example: control flow for a game; we do not know how long the user is going to keep playing, but we may want to perform some actions while the game is running, such as keeping track of positions, etc.

    • do while

    Use when we want a loop to repeat a number of times, but at least once. Example: asking user for input that matches certain criteria.

    • for

    Use when we want a loop to repeat a discrete number of times, though we do not know the number at the time of compilation.

    Loops are interchangeable, but they should be used in the right context/use case.

5 Functions

5.1 CS50 Short

Smaller programs can be written all inside of main(). However, as programs become more complex (running into the thousands and tens of thousands of lines of code), it isn't a very good idea to keep everything inside of main.

C and most programming languages allow us to write functions.

Functions are also known as procedures, methods (particularly in OOP/audit-oriented languages), or subroutines.

5.1.1 What is a function?

  • A black box with a set of 0+ inputs and 1 output.
a
b  ---> [func(a,b,c)] ---> z
c

The function func() takes a, b, and c as inputs, processes them in a certain way, and finally returns a single output - z.

3
6 ---> [add(a,b,c)] ---> 16
7

We call it a black box because we do not necessarily know how the function is implemented.

It is a good practice to name functions descriptively, as well as having good documentation.

5.1.2 Why use functions?

  • Organization

Functions help us break up a complicated problem into more manageable subparts.

  • Simplification

Smaller components tend to be easier to design, implement, and debug.

  • Reusability

Functions can be recycled; you only need to write them once, but can use them as often as you need.

5.1.3 Function declarations

  • Function declarations

The first step to creating a function is to declare it. This gives the compiler a heads-up that a user-written function appears in the code.

  • Function declarations should always go atop the code, before main()
  • There is a standard form that every function declaration follows:

    return-type name(argument-list);

  • The return-type is what kind of variable the function will output.
  • The name is what we choose to call our function.
  • The argument-list is the comma-separated set of inputs to the function, each of which has a type and a name.

Examples:

int add_two_ints(int a, int b); // function declaration

float mult_two_reals(float x, float y) // function definition 
{
	float product = x * y;
	return product;
}

Or:

return x * y;

5.1.4 Function Calls

To call a function, pass an appropriate argument and assign its return value to something of the correct type.

// includes

#include <cs50.h>
#include <stdio.h>

// declare functions

int add_two_ints(int a, int b);

int main(void)
{
	// ask user for input
	printf("Integer: ");
	int x = GetInt();
	printf("Second integer: ");
	int y = GetInt();

	// add the two numbers together via a function call
	int z = add_two_ints(x, y);

	// output the result
	printf("Result: %i\n", z)

}

int add_two_ints(int a, int b)
{
	return a + b;
}

5.1.5 Misc

  • Functions can sometimes take no inputs, for which we have to declare the function as having a void argument list, as in int main(void).
  • Function sometimes do not have an output - in that case we declare the function as having a void return type.

6 Variables & Scope

6.1 CS50 Short

6.1.1 Scope

Scope is a characteristic of a variable that defines from which functions that variable can be accessed.

  1. Local variables
    • Local variables can only be accessed within the functions in which they are created. So, if a variable is created within main(), then it can be accessed from anywhere within main(). However, if we have a custom function and try to use a variable defined in main(), this will not work.
    int main (void)
    {
    	int result = triple(5)
    }
    
    int triple(int x)
    {
    	result x * 3;
    }
    
    
    • Here, the x is local to the function triple(). No other function can refer to that variable, not even main(). result is local to main().
  2. Global variables

    – Global variables can be accessed by any function in the program, because they are declared outside of functions.

    #include <stdio.h>
    
    float global = 0.5050;
    
    int main(void)
    {
    	triple();
    	printf("%f\n", global);
    }
    
    void triple(void)
    {
    	global *= 3;
    }
    
    
    • Here, global is global to all functions in the program, and we can refer to it from any part of the program.

    Caution: a function can change the value of a variable before it is expected to be changed.

  3. Why it matters?
    • For the most part, local variables in C are passed by value in function calls.
    • When a variable is passed by value, the callee received a copy of the passed variable, not the variable itself.
    • This means that the variable in the caller is unchanged unless overwritten.

    Examples:

    • No effect on foo:
    int main(void)
    {
    	int foo = 4;
    	triple(foo);
    }
    
    int triple(int x)
    {
    	return x *= 3;
    }
    
    
    • Overwrites foo:
    int main(void)
    {
    	int foo = 4;
    	foo = triple(foo);
    }
    
    int triple(int x)
    {
    	return x *= 3;
    }
    
    
    • Things can get particularly insidious if the same variable name appears in multiple functions, which is perfectly okay as long as the variables exist in different scopes.

7 Arrays

7.1 CS50 Short

7.1.1 Definition

  • Arrays are a fundamental data structure.
  • Arrays hold values of the same type at contiguous memory locations.

7.1.2 Structure

  • In C, the elements of an array are indexed starting from 0!
  • If an array consists of n elements, the first element is located at index 0, and the last element is located at inded (n-1).
  • C is very lenient, and will not prevent us from going "out of bounds" of our array - caution, possible segmentation fault.

7.1.3 Array declaration

type name[size];

  • Type is what kind of variable each element of the array will be.
  • Name is what we wish to call the array.
  • The size is how many elements we want our array to contain.
  • Declaring an array:
int student_grades[40];

  • Using an array:
// Create an array of 10 booleans

bool truthtable[10];

truthtable[2] = false;

if (truthtable[7] == true)
{
	printf("TRUE!\n");
}

/* 
An array of 10 booleans should end at truthtable[9].
C won't stop us from checking what's on truthtable[10], but we risk running into segmentation faults.
*/

truthtable[10] = true; 

  • Declaring & initializing an array:

When declaring and initializing an array simultaneously, there is a special syntax that may be used to fill up the array with its starting values:

// instantiation syntax (ideal for small arrays)

bool truthtable[3] = { false, true, true };

// instantiation syntax without needing to declare size of array

bool truthtable[] = { false, true, true };

// individual element syntax

bool truthtable[3];
truthtable[0] = false;
truthtable[1] = true;
truthtable[2] = true;

// populating an array using a for loop

for (i = 0; i < 2; i++)
{
	truthtable[i] = i;
}

// multiple dimensions in array

bool battleship[10][10];

// We can choose to conceptualize the above as a grid, though in memory it's still just a 100-element one dimensional array.

// Multi-dimensional arrays are great abstractions to help visualize boards or other complex representations.

7.1.4 Copying elements from one array to another

  • While we can treat individual elements of arrays as variables, we cannot treat entire arrays themselves as variables.
  • For instance, we cannot assign one array to another using the assignment operator like so: array1 = array2. To do this, we must use a loop to copy over the elments one at a time.

#+BEGINSRC C

for (i = 0; i < length; i++) { array1[i] = array2[i]; }

#+ENDSRC C

7.1.5 Variable scope

  • When calling a variable in a C function, most of them are passed by value (meaning, the function receives a copy of the variable).
  1. Passing by reference instead of by value
    • Arrays are not passed by value. Rather, they are passed by reference. The callee receives the actual array, NOT a copy of it. This means that when the callee manipulates the elements of an array, the array itself is changed. This will be explained later.

    What happens here?

    void set_array(int array[4]);
    void set_int(int x);
    
    int main(void)
    {
    	int a = 10;
    	int b[4] = { 0, 1, 2, 3 };
    	set_int(a);
    	set_array(b);
    	printf("%d %d\n", a, b[0]);
    }
    
    void set_array(int array[4])
    {
    	array[0] = 22;
    }
    
    void set_int(int x)
    {
    	x = 22;
    }
    
    
    
    • int a gets 10;
    • int b is an array with an index of 4, already initialized;
    • A function setint() is called and gets a as input, but has no output;
    • setint() received A COPY (passing by value) of int a, but returns nothing (void)
    • setarray takes the b array and passes it by reference to the function. Even though nothing is returned (void output), the array itself is changed.
    • Output is 10, 22.

Author: flavius

Created: 2021-10-02 Sat 18:16

Validate