W1 - Shorts 📚🔍 - Intro to C Language and CS50 Library

Read more here
CS50 IDE: https://cs50.dev/ (Online VSCode)
CS50 Library Documentation: https://manual.cs50.io/#cs50.h

#CS50x #C #Computer_Science #VisualStudioCode

🔙 Previous Part | Next Part 🔜

↩️ Go Back

Table of Contents:

🔙 Previous Part | Next Part 🔜

↩️ Go Back


A) Introduction to Data Types and Variables in C 🎦

A.0) Overview of Data Types (Modern Languages vs C)

In modern programming languages like PHP and JavaScript, you don’t need to specify [[data types]] when declaring variables; the language infers the type based on the value.

Example - JavaScript:

let number1 = 2, number2 = 23, number3 = 99;

console.log(number1); // 2
console.log(number2); // 23
console.log(number3); // 99

However, C, being an older language, requires that you explicitly specify the data type of a variable the first time you declare it.

Example - C:

int number = 17;
char letter = 'H';

Read more:

A.1) Common Data Types in C (int, char, float, etc)

  1. int (Integer): 32 bits

    • Used for variables that store whole numbers (e.g., 1, -2, 1000).
    • Takes up 4 bytes of memory, which is 32 bits.
    • The range of values for int is approximately -2 billion to +2 billion (-2^31 to 2^31 - 1).
    • Half of the range is for negative numbers, and half is for positive numbers, with one spot reserved for 0.
  2. unsigned int: 32 bits

    • A modifier (qualifier) for int that allows only positive values.
    • Doubles the positive range (approximately 0 to 4 billion, or 2^32 - 1) by eliminating negative numbers.
  3. char (Character): 8 bits

    • Used for storing single characters (e.g., 'A', 'b', '3').
    • Takes up 1 byte (8 bits) of memory.
    • The range of values is -128 to +127 (-2^7 to 2^7 - 1).
    • Uses ASCII encoding to map numbers to characters (e.g., 65 for 'A', 97 for 'a', and 48 for '0').
  4. float (Floating Point): 32 bits

    • Used for numbers with decimal points (e.g., 3.14, -0.001).
    • Takes up 4 bytes (32 bits) of memory.
    • Precision is limited due to the finite number of bits; decimal precision may be limited to about 7 significant digits.
  5. double (Double Precision Floating Point): 64 bits

    • Similar to float but with double the precision, taking up 8 bytes (64 bits).
    • Offers higher precision and can represent more digits after the decimal, useful for tasks that require greater accuracy (e.g., pi with more than 15 digits).

A.1.1) Special Type: void

A.1.2) CS50-Specific Data Types (bool and string)

A.1.3) In the next Lectures: "Structures" and "Defined Types"


A.2) Working with Variables in C (declaration, assignment and initialization)

A.2.1) Example of Variable Use and Best Practices

Example - using printf()

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

int main(void)
{
    // Declare and initialize variables
    int number = 42;
    char letter = 'A';
    bool isCS50Fun = true;

    // Print variables
    printf("Number: %d\n", number);
    printf("Letter: %c\n", letter);
    printf("CS50 is fun: %s\n", isCS50Fun ? "true" : "false");
}

A.3) Key Takeaways: Data Types

This overview should give you a clear idea of data types, their usage, and how to work with variables in C programming.


B) Operators in C 🎦

B.1) Assignment Operator ( = )

The assignment operator (=) allows you to assign a value to a variable.

For instance:

int x = 10; // Assigns the value 10 to the variable x

In this example, = assigns 10 to x.

B.2) Arithmetic Operators (+, -, *, / )

C supports basic arithmetic operations:

Example:

int y = 10;
int x = y + 1; // x is now 11
x = x * 5;     // x is now 55

Note: Assignment in C works such that the right-hand side of the equation is evaluated first and then assigned to the left-hand variable. For instance, x = x * 5 means x is multiplied by 5, and the result (55 in this case) is stored in x, overwriting its previous value.

B.2.1) Modulus Operator explained (%)

The modulus operator (%) calculates the remainder when one integer is divided by another.

int m = 13 % 4; // m is now 1, because 13 divided by 4 gives a remainder of 1

Use Case: Modulus is useful for:

B.3) Shorthand Assignment Operators (combine assignment with arithmetic)

C provides shorthand operators for common arithmetic tasks:

These make the code more concise and readable.

Increment and Decrement Operators:

Example:

int x = 10;
x++; // x is now 11
x--; // x is back to 10

B.4) Boolean Expressions and Logical Operators (true, false)

Boolean expressions evaluate to either true or false.
These are used to compare values or make decisions in code.

True and False in C:

Logical Operators:

  1. AND (&&): True only if both operands are true.

    if (x && y) { /* Code runs only if both x and y are true */ }
    
  2. OR (||): True if at least one operand is true.

    if (x || y) { /* Code runs if either x or y or both are true */ }
    
  3. NOT (!): Inverts the truth value.

    if (!x) { /* Code runs if x is false */ }
    

Example Truth Tables:

B.5) Relational Operators (greater than, less than, equal, etc)

Relational operators are used to compare two values:

Example:

int x = 5, y = 10;
if (x < y) { /* This block runs because 5 is less than 10 */ }
if (x == y) { /* This block does not run because 5 is not equal to 10 */ }

Important Note:
A common beginner mistake is using = (assignment) instead of == (equality check) when testing for equality.

if (x = y) { /* This assigns y to x and always evaluates to true if y is non-zero */ }

Always use == for comparison:

if (x == y) { /* Correct: checks if x is equal to y */ }

B.6) Conclusion: Operators in C

Understanding these operators is essential for:

While the syntax might initially seem complex, with practice, these concepts become second nature and help make code more efficient and expressive.


C) Conditional Statements in C 🎦

Conditional statements in C allow programs to make decisions and take different paths based on certain conditions. They enable programs to execute specific blocks of code when conditions are met or bypass them otherwise. Let's break down the main types of conditional statements in C:

C1. if Statement

The if statement is one of the simplest forms of conditionals. It checks a condition, and if the condition is true, it executes the code block within the curly braces.

Syntax:

if (condition) {
    // Code to execute if the condition is true
}

Behavior: If the condition is true, the code within the braces runs. If false, it is skipped.

Example:

if (x < 10) {
    printf("x is less than 10.\n");
}

C2. if-else Statement

The if-else statement allows a program to take one path if a condition is true and another if it is false.

Syntax:

if (condition) {
    // Code to execute if the condition is true
} else {
    // Code to execute if the condition is false
}

Behavior: Only one block of code (either if or else) runs, depending on the condition.

Example:

if (x > 0) {
    printf("x is positive.\n");
} else {
    printf("x is not positive.\n");
}

C3. if-else if-else Chain

The if-else if-else chain allows multiple conditions to be checked in sequence. Only one block of code runs—the first block with a true condition.

Syntax:

if (condition1) {
    // Code for condition1
} else if (condition2) {
    // Code for condition2
} else {
    // Code if none of the above conditions are true
}

Behavior: Only one block runs, even if multiple conditions are true. The first true condition’s block is executed.

Example:

if (x > 10) {
    printf("x is greater than 10.\n");
} else if (x == 10) {
    printf("x is exactly 10.\n");
} else {
    printf("x is less than 10.\n");
}

Key Point: Each block in an if-else if-else chain is mutually exclusive—only one block can execute.

C4. Non-Mutually Exclusive if Statements

In some cases, it’s possible to have separate if statements that aren't part of an if-else if-else chain, meaning multiple blocks of code could potentially execute if their conditions are true.

Example:

if (x > 5) {
    printf("x is greater than 5.\n");
}
if (x < 15) {
    printf("x is less than 15.\n");
}

Behavior: Both conditions are checked independently. If both are true, both blocks will run.

C5. switch Statement

The switch statement is an alternative to if-else chains when dealing with discrete values. It allows you to specify distinct cases for a variable or expression.

Syntax:

switch (variable) {
    case value1:
        // Code for value1
        break;
    case value2:
        // Code for value2
        break;
    // Add more cases as needed
    default:
        // Code if none of the cases match
}

Behavior: The matching case runs, and then break stops the execution from continuing to subsequent cases.

Example:

int x = GetInt();
switch (x) {
    case 1:
        printf("You entered one.\n");
        break;
    case 2:
        printf("You entered two.\n");
        break;
    default:
        printf("You entered a number other than one or two.\n");
}

Important: Always use break to prevent fall-through behavior, where the program continues to execute subsequent cases. However, you can intentionally omit break to let cases fall through, which can be useful in specific situations (e.g., cascading output).

Fall-Through Example (without break):

switch (x) {     
	case 4:
		printf("Four.\n");
	case 3:
		printf("Three.\n");
	case 2:
		printf("Two.\n");
	case 1:
		printf("One.\n");
		printf("Blast off!\n");
		break;
}

Behavior: If x is 4, it will print "Four," "Three," "Two," "One," and "Blast off!" due to fall-through.

C6. Ternary Operator (?:)

The ternary operator is a compact way to write if-else statements with simple expressions.
It is structured as condition ? expression1 : expression2.

Syntax:

variable = (condition) ? value_if_true : value_if_false;

Example:

int x = 10;
int y = (x > 5) ? 20 : 30; // y will be 20 if x > 5, otherwise y will be 30.

Explanation:

Use Case: This operator is great for simple conditional assignments and can make code look concise.

C7. Summary: Conditional Statements in C

These conditional statements provide powerful tools for controlling the flow of a program, allowing different code paths based on dynamic conditions and user input.


D) Loops in C 🎦

Loops are essential in programming because they allow you to execute a block of code multiple times without duplicating the code. This makes your programs more efficient and easier to read.

In C, there are three main types of loops, each suited for different use cases:

  1. while Loop
  2. do while Loop
  3. for Loop

D.0) Use Cases (how to pick a Loop)

D1. while Loop

A while loop runs as long as a specified condition is true. If the condition is false when the loop is first encountered, the code inside will not run at all.

Syntax:

while (condition) {
    // Code to run while the condition is true
}

Example:

int x = 0;
while (x < 5) {
    printf("%d\n", x);
    x++;
}

D.1.1) Note: Infinite Loops (while (true))

An infinite loop runs indefinitely. This can be intentional (e.g., running a server) or unintentional (a common bug).

Note: using a while (true) loop,

Example:

while (true) {
    // This will run forever
}

Stopping an Infinite Loop:

Tip: Press Ctrl + C to force-stop a running program from the command line.

D2. do while Loop

A do while loop is similar to a while loop, but it guarantees that the code inside runs at least once before the condition is checked.

Syntax:

do {
    // Code to run at least once
} while (condition);

Example:

int num;
do {
    printf("Enter a positive number: ");
    scanf("%d", &num);
} while (num <= 0);

D3. for Loop

A for loop is used when you know in advance how many times you need to repeat a block of code.

It consists of three main parts:

Syntax:

for (initialization; condition; increment) {
    // Code to run a specific number of times
}

Example:

for (int i = 0; i < 10; i++) {
    printf("Iteration %d\n", i);
}

D4. Loop Control Statements (brake and continue)

Example of continue:

for (int i = 0; i < 5; i++) {
    if (i == 2) {
        continue; // Skip printing when i is 2
    }
    printf("%d\n", i);
}

D5. General Advice: using Loops in C

Loops are powerful and essential for efficient programming. Using them correctly will make your code more flexible and reduce redundancy.


E) Basic Linux Command Line Operations 🎦

The Linux command line is a powerful tool that allows programmers to interact directly with the operating system.

Although many Linux distributions offer Graphical User Interfaces (GUIs) similar to those found in Windows and macOS,

the command line provides an efficient way to execute commands using only a keyboard.

This is especially useful in programming environments like the CS50 IDE, which runs on Ubuntu, a popular Linux distribution.

E.1) Basic Command Line Commands

1. ls ("List" contents)

2. cd ("Change Directory")

 cd pset1  # Moves into the 'pset1' directory
 cd ..     # Moves back to the parent directory
 cd        # Moves to the home directory (equivalent to cd../..)

3. pwd (Show "Present Working Directory")

4. mkdir ("Make Directory")

5. cp ("Copy" and Copy Directory -r)

6. rm ("Remove" and Remove Directory -r - with force option -f)

7. mv ("Move"/Rename)

	mv file.txt ../ # Moves 'file.txt' to the parent directory

E.2) Practical Tips for Command Line Usage

E.3) Summary: using the Command Line

These basic commands are fundamental for navigating and managing files in any Unix-based system, including Linux and macOS. Mastery of these will enable you to work efficiently in a coding environment, such as the CS50 IDE, and allow you to perform common tasks directly from the terminal.

  1. chmod: Changes the permissions of a file or directory.
  2. ln: Creates hard or symbolic links to files or directories.
  3. touch: Creates an empty file or updates the timestamp of an existing file.
  4. rmdir: Removes empty directories.
  5. man: Displays the manual or help information for other commands.
  6. diff: Compares the contents of two files line by line.
  7. sudo: Executes commands with superuser (root) privileges.
  8. clear: Clears the terminal screen.
  9. telnet: Connects to remote computers using the Telnet protocol, mainly for network troubleshooting and testing.

Read more here


F) Magic Numbers and Symbolic Constants in C 🎦

F.1) What Are Magic Numbers?

Magic numbers refer to the use of unexplained numerical constants directly within code. These numbers often seem arbitrary and provide no immediate context or explanation for their values.

For example, in the Mario problem, you may have seen a hard-coded limit such as:

if (height > 23)

Here, 23 is a "magic number" because its purpose isn't clear to someone reading the code without additional context.

F.1.1) Why Avoid Magic Numbers?

Including magic numbers in your code is generally considered a poor practice because:

F.2) Example of a Magic Number (playing with a deck of cards)

Consider the following example:

for (int i = 0; i < 52; i++) {
    // Deal a card
}

In this code, the number 52 is a magic number.

While a seasoned programmer might recognize this as the number of cards in a standard deck,

it's not immediately clear to everyone, and if the code were part of a larger suite involving card manipulation, it could lead to confusion.

F.2.1) Addressing Magic Numbers with Symbolic Constants

One way to improve clarity is to use a named variable:

int deck_size = 52;
for (int i = 0; i < deck_size; i++) {
    // Deal a card
}

This makes the code more readable, but there is a potential issue: if deck_size is accidentally modified in another part of the program, it could cause unintended behavior.

Method 2: Using define for Symbolic Constants

A more robust solution in C is to use a preprocessor directive called #define to create symbolic constants:

#define DECK_SIZE 52
for (int i = 0; i < DECK_SIZE; i++) {
    // Deal a card
}

Syntax Reminder:

#define NAME REPLACEMENT

Note: Do not place a semicolon at the end of the #define directive.

F.3) Examples of Symbolic Constants (use it with floats, strings, etc)

  1. Defining a mathematical constant:
    #define PI 3.14159265
    

Example: This makes your code more readable:

float circumference = 2 * PI * radius;
  1. Defining a string:
    #define COURSE "CS50"
    printf("Welcome to %s!\n", COURSE);
    

F.3.1) Note: Conventions for Symbolic Constants (use All-Uppercase Names)

It is common practice to use all-uppercase names for symbolic constants:

F.4) Advantages of Using Symbolic Constants

Conclusion: avoid Magic Numbers

Magic numbers should generally be avoided in code due to their potential to confuse and complicate code maintenance. Replacing them with symbolic constants using #define makes your code clearer, safer, and easier to modify.


🔙 Previous Part | Next Part 🔜

↩️ Go Back


Z) 🗃️ Glossary

File Definition
Uncreated files Origin Note
data types W1 - Shorts 📚🔍 - Intro to C Language and CS50 Library