[CS50] TIL CS50X Week 1 - C part 1

Week 1 - C part 1

This course is intended for students who have never coded before. The post may be elaborate due to the reason.

🧩 What I Should Learn?

  • Create our first program in C
  • Functions
  • How to use variables, conditionals and loops
  • Linux and Command Line Interface

🎯 What I learned today

Week 0 review

What we learned previously?

We learned about Scratch, a visual programming language containing functions, conditionals, Boolean expressions, loops, etc.

The essential programming concept presented in Scratch will merge with our savvy along with the learning path as we learn how to program in a programming language.

When we write the code, humans write source code, the code that all the human programmers will ultimately write. It doesn’t matter if it’s a Scratch, C, or Python, but it is human-readable code.

While the computer or a machine only understands what we can call machine code, which is a binary number. This machine code is a pattern of zeros and ones that produce a desired effect.

We must convert the source code into a machine code to communicate with the computer. Hence, we need a very specific piece of software called a compiler.

If we fit into the same paradigm as last time, the input will be a source code, and we need to output the machine code somehow.

The middle box is a special program that converts source code to machine code. We call this type of program a compiler.

In this lesson, we will learn the compiler that will allow us to convert the source code written in the programming language C to the machine code.

In addition to that learning block, we will learn about how to write good code. The good code can be evaluated upon three axes.

Three Good Code Evaluation
  • Correctness - “Does the code run as intended?”
  • Design - “How well is the code designed?”
  • Style - “How aesthetically pleasing and consistent is the code?”

Obviously, code correctness is the first and foremost goal. We can’t always write the correct code; the bug causes an error or blue screen, but that is not what we intend to do.

More subjective goals are design and style. We briefly saw from the Scratch what is a well-designed code.

The better-designed and styled code is often faster and more maintainable by the writer and even colleagues.

Hello VS Code

VS Code

The compiler that is utilized for this course will be Visual Studio Code or VS Code

We can write the code anywhere we want, even with a pen and paper. Notepad, text editor, or Microsoft Word will support writing the code, but none of those are designed to format the code nor to compile and run the code.

VS Code has all the pre-loaded software required for this course. Hence, the course and the instructions herein were designed with VS Code.

Notice that there is a file explorer on the left, and on the right side, two areas are separated.

The one on the top is called text editor, where we write the code or edit the program. Finally, the one on the bottom is called a Command Line Interface, known as CLI, command line or terminal window where we can send commands to the computer.

First Program in C

Run the command code hello.c

Notice that we deliberately use the lowercase of the entire filename and include the .c extension. You could use capital H or maybe capital C for the extension but follow the best practices and keep consistency with others.

After creating the hello.c file, go to the text editor and type as above. We will go over the details in order, but let’s compile and run this program.

Type make hello in the terminal window; think of this step as double-clicking a file on your Mac or PC.

This is the process of compling, in which the make program automates the compilation for us. By using this command, the make compiler automatically detects a file on the hard drive called hello.c and converts its source code in C to machine code or the program in zeros and ones.

If we execute this command without an error message, we can proceed. Check the typos of your code if any error occurred.

Notice that the terminal window has a dollar sign; it represents the current line and is ready for the command.

To run this program, type ./hello in the terminal window. This might be weird for people who have never coded before, but it is how we run the program called hello in the current folder or directory in this environment.

Take attention to the left; our file explorer has two files we created, hello.c with the code command and another file called hello.

The compiler can read the file hello.c, where we store our code. The file hello is an executable file that users cannot read nor open by the standard compiler.

If we look closer, without doing certain things, the text is already color coded.

Most programming environments, including VS Code, do it for you; we call it syntax highlighting.

It is a feature of typical text editors nowadays that analyzes the code that the user types, and when it notices different keystroke types, such as functions, loops or conditionals, it highlights those things in different colors.

Functions

Say block

In Scratch, we utilize the say block to display text on the screen.

In a similar manner, in C, we have a function called printf to do exactly this behavior.

So, if we convert the left say block to the C code at the right printf, we need a white oval or an argument.

The argument passed to printf is "hello, world\n", and then the semicolon at the end.

We will eventually get used to these nuisances like the parentheses, quotes, semicolons and the like.

Reading an error message

The common mistake we, or even experienced programmers, occasionally make is omitting a semicolon.

We can meet numerous errors when we recompile the code. We accidentally forgot the semicolon, but the errors disappear if we put it back.

The red circle indicates where the error occurred, so reference those as an important clue when finding or analyzing errors.

The recompiling is important because the machine code in hello is not changed even though we changed the source code.

Another experiment we can do is removing \n since we don’t have a clue what it is and why it is there.

Try removing those and recompiling or making the program; it will end up giving us a weird terminal text shape.

Notice that the dollar sign is printed right after the hello world. We can still type something with the dollar sign, but it looks stupid and not user-friendly.

Moving the cursor to the following line for the user when we are done running the program is convention. The backslash n (\n) is the special symbol known as the escape sequence that C knows.

Libraries

After jiggling the codes, we might be curious about the first line of code. If we delete the first line, what error message do we get?

From the error message, we can figure that we declare the printf function without a library.

The statement in the first line, #include <stdio.h>, is a special command that tells the compiler that we want to use the capabilities of library called standard input and output.

Think of it as importing a library, or in Scratch, we import an extension like text-to-speech. That was the same as adding a library.

Library is the third-party written code, or someone else wrote it in this case. Same in C, we don’t automatically get the printf function. We have to include a header file that declares the printf function to exist.

If you are interested in what functions in the library, check out here. Also, CS50 has its own library called cs50.h; you can check it on the same page above.

Variables

Ask and answer block

Recall that in Scratch, we had the ability to ask the user name as an input and say hello with that name appended to it like this: "hello, (your name).

We can use a get_string function from the cs50 library that collects the user input and the quotes. At the end of the string, leave the space for the user so that the cursor moves one space after the sentence.

Notice in C that we have to be a little more specific than Scratch. In C, if we want to get a function’s return value, we have to use an equal sign and the variable’s name on the left.

The equal sign doesn’t mean equality, but the assignment, which means copying the value on the right over to the value on the left.

Yet, there is one more thing, which is why we call C pedantic language: we need to tell C what type of value we are storing.

When we move to the VS Code, we add the cs50.h library on the top, and then the answer is passed to the printf function.

But what is the %s sign?

Format codes

One step back to the Scratch, we had the best solution for the same function above. The join block was the elegant solution for using the say block and the input.

The picture above is how we translate the say and the join blocks. It is not perfectly matched, but this is how we can implement the same function.

The percent sign or a placeholder is known as a format code in C, especially for printf. This means the %s is a placeholder for a string, the s stands for string, and we ask the computer to plug in some value here.

The function printf is smart enough to take the answer and go back to the hello %s\n if we have %s and one additional argument after the comma inside of the function.

To sum up, the get_string function is used to get a string from the user. Then, the variable answer is passed to the printf function. The %s sign tells the printf function to prepare itself to receive a string.

Same as the answer, we can define the name of the variable and the type of the variable. There are many data types, such as int, bool, char, and many others.

Conditionals

If block

Another building block we utilized within Scratch was that of conditionals, or the if-else block.

Suppose we want to compare two values, and in Scratch, we have a block for that ability.

Notice that the right side keyword is almost identical to the left; that’s because MIT borrowed those keywords.

What if there is a three-way fork in the road? In Scratch, it is unwieldy graphically. The code might look like this:

Notice the block is kind of a mess. If we nitpick or critique the design of our code, we can figure out that the third question is unnecessary.

By reducing redundant conditional, we produce a minor tweak from the original.

Let’s now go to the VS Code and write the code in C.

Type code compare.c in your terminal window, and it will create a new file named compare.c. Once we close and run the command again, it will open the existing file with the same name.

And if we write the code without a typo, our program should run like this:

Notice that we created variables x and y and set variables to an int or integer. These values are populated using the get_int() from the CS50 library.

Our program can only check if x is less than y, so we need to improve our program by coding as follows:

Now, all potential outcomes are accounted for.

Another example program

If we are building a program that asks if you agree or not, we can build the program like this:

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

int main(void)
{
  char c = get_char("Do you agree? ");

  if (c == 'y')
  {
    printf("Agreed.\n");
  }
  else if (c == 'n')
  {
    printf("Not agreed.\n");
  }
}

Compile this program and run it. The program works but in a little obnoxious way. We must manually type the lowercase and can’t use the capital Y or N.

Another point is that we used double quotes for the string and single quotes for the character.

This is a necessity in C; when we are dealing with strings, such as text, a name, a sentence or anything more than one character, we typically use a "". When dealing with deliberately single characters, we must use ''. Because of this, we let the computer know the content inside of the single quote is indeed a char, not a string.

Back to the code, how can we improve our program to take both lower and upper characters?

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

int main(void)
{
  char c = get_char("Do you agree? ");

  if (c == 'y')
  {
    printf("Agreed.\n");
  }
  else if (c == 'Y')
  {
    printf("Agreed.\n");
  }
  else if (c == 'n')
  {
    printf("Not agreed.\n");
  }
  else if (c == 'N')
  {
    printf("Not agreed.\n");
  }
}

This could be a correct solution, but perhaps not the best design. Another term of art here, we can say the code smell is a little off. That’s because we repeat the same thing but want the code to be different.

The code above has a third smaller size than the original and less redundancy, which has a better design. Also, we have to mention that we have a new operator, ||, which effectively means or.

Loops

Meow program

Recall that we made a meow block in Scratch that sounds like a cat meowing several times.

There is no sound block in C so that we will rely on the print. The program above in C will be as follows:

#include <stdio.h>

int main(void)
{
  printf("meow\n");
  printf("meow\n");
  printf("meow\n");
}

As we learned through the course, this is not the best design. To implement the loop in C, we need to know the syntax of the loop.

If we set the counter to count a number, it might look like this:

In the same manner, if we want to increment the counter by one, we can use the following code:

counter = counter + 1;

// In a more simple way, we usually do this instead.
counter += 1;

// More simple way:
counter++;

The first code almost seems like a paradox, but it’s the common syntax in programming. A single equal sign is an assignment from right to left. Also, we don’t need to mention the keyword int when we update an existing variable.

Converting the repeat block from Scratch will look like this:

Notice that we create an int called i and assign it the value 0. Then, we create a while loop that will run until the i becomes zero. Whenever the loop runs, we subtract i by one.

But that is not what human counts, so we can modify the code like this:

int i = 1;
while (i <= 3)
{
  printf("meow\n");
  i--;
}

And this will count i from one to three. However, programmers usually start counting from 0, so the go-to syntax, once we get comfortable counting from zero, will be like this:

int i = 0;
while (i < 3)
{
  printf("meow\n");
  i++;
}

For-loop

The most canonical way to solve the loop might be a for-loop, although it takes a little more time to get used to. But this for-loop consolidates into one line all of the same logic.

The for is just a preposition that implies, here comes a loop.

Inside the parentheses, there are three parts.

The first yellow-colored part is initialized to 0, just like before.

After the initializing part, the loop checks the Boolean expression part. And if i is less than three, it prints out meow.

Then i is going to get incremented. The i starts at 0, and goes to 1. At that point, the Boolean expression is rechecked. The first step happens only once, and we don’t keep changing i back to 0.

When the i reaches 3, the Boolean expression asks, Is 3 < 3? and indeed, three is not less than three, so the loop stops.

One additional building block, when we recall, Scratch had a forever block when we played background music or checked a collision to a wall as such.

The infinite loop is not a good thing in general, but we can implement it by the following code:

int main(void)
{
  while (true)
  {
    printf("meow\n");
  }
}

Notice that true will always be the case, and if we remind of the transistor, the 0 is false and 1 is true. Therefore, we can replace true with 1, and by doing so, the code will always run.

We will lose control of the terminal window by running this code. To break from the infinite loop, hit cmd + c or ctrl + c on the keyboard.

We can encounter the following error if we don’t include the <cs50.h> library.

True and false are themselves unique words that we have to include. If we want to use particular Boolean values like this, we must include another library called standard bool, #include <stdbool.h>, or the CS50 library alternatively.

Linux and Command Line Interface

Linux

Linux is an operating system accessible via the command line in the terminal window in VS Code. Essentially, it is just an alternative to macOS or Windows that provides both a GUI and a command-line environment.

Windows and macOS have terminal windows or the equivalent thereof. But Linux is known for, along with other operating systems, its command line environment.

Terminal in VS Code

The Command Line Interface, or CLI, literally refers to the terminal window. If we go back to the VS Code and close the tab to focus on the terminal window, the terminal window is really just the command line interface to our own server in the cloud.

There are many commands or command-line arguments that are worth knowing, and here are some examples.

  • cd stands for change directory, which means changing our current directory(folder)
  • cp for copying files and directories
  • ls stands for list and for listing files in a directory
  • mkdir, for making a directory
  • mv stands for move and can be used to move and rename files and directories
  • rm, for removing or deleting files
  • rmdir, used for removing(deleting) directories

Indeed, these commands are all succinct and abbreviated because we or developers earlier didn’t want to type out long commands.

The most commonly used is ls, which will list all the files or directories in the current directory.

As we can see in the picture, we have green files with an asterisk, which means the file is executable. Besides the green files, we have .c files where we write the code and the blue pset1 directory.

Another useful command is mv, where we can move a file from one file to another. Suppose I accidentally created a file called Greeting, typed the code, and realized I didn’t put the .c extension.

Instead of copying all the code and making another file called greeting.c and pasting all the code there, we can simply run this mv Greeting greeting.c to move all the written code.

The above picture suffices to explain the process that we’ve discussed above. Create a file without the extension, write some code, and move all the code that was written to a new file called greeting.c.

We can use the mkdir pset1 to create a directory, the same as the picture above.

Notice that we ran ls but can’t see the mario.c file. We can see visually on the top left pset1 folder contains the mario.c, but we can’t run or compile the file because we are in a different directory.

For instance, if we browse Finder on Mac or File Explorer on Windows, we can double-click to get into that folder. The exact process here is that we have to change into the folder or directory, and cd is the command there.

Notice that we have a pset1/ before the dollar sign, indicating that we are in the directory called pset1. We can start running and compile mario.c because we are in the right directory.

Ultimately, this command line interface will be a more powerful, capable and efficient mechanism for writing code, running commands, solving problems, and analyzing data more, even though a somewhat stiff learning curve exists.

📌 Takeaway

  • When humans write the source code, the computer or machine can’t read the source code. Thus, conversion is needed.
  • A compiler is a special program that converts source code into machine code.
  • The better-designed and styled code is often faster and maintainable.
  • VS Code is our compiler, and make file.c will generate or compile the machine code from the source code.
  • When the command line gets make file.c command, the compiler generates the executable file with the same name. Which are not easily accessible and readable by humans.
  • Recompiling is needed each time we change the source code.
  • In C, we must import a library to use a function. Also, we need the escape sequence to switch to the following line after executing the program.
  • To use a variable in C, we need to use format codes.
  • C requires the use of quotation marks separately. When dealing with characters, use ''(single quote) and ""(double quotes) for strings.
  • Command mv moves all the code from a new file to an existing file. Or think it could change the file’s name and delete the old one.

💻 Solution

  • None

Continue to part two

🔖 Review

  • When we evaluate if the code is well written, checking three criteria would be the best standard: the code correctness, design, and style.
  • C is pedantic language, in which we need to define every variable type and include the library even for the printf function.
  • True and False can be represented by 0 and 1
  • There are two patterns of loop, a while loop and a for loop. Also, developers count the number from 0.

Categories:

Updated:

Comments