We started the course with Scratch, and then learned C.
Recall that we write our source code in C, but needed to compile it to machine code, in binary, before our computers could run it.
clang
is the compiler we learned to use, and make
is a utility that helps us run clang
without having to indicate all the options manually.#include <cs50.h>
, and use clang
instead of make
, we also have to add a flag: clang hello.c -lcs50
. The l
flag links the cs50
file, which was installed into the CS50 Sandbox.“Compiling” source code into machine code is actually made up of smaller steps:
Preprocessing involves looking at lines that start with a #
, like #include
, before everything else. For example, #include <cs50.h>
will tell clang
to look for that header file first, since it contains content that we want to include in our program. Then, clang
will essentially replace the contents of those header files into our program:
...
string get_string(string prompt);
int printf(const char *format, ...);
...
int main(void)
{
string name = get_string("Name: ");
printf("hello, %s\\n", name);
}
Compiling takes our source code, in C, and converts it to assembly code, which looks like this:
...
main: # @main
.cfi_startproc
# BB#0:
pushq %rbp
.Ltmp0:
.cfi_def_cfa_offset 16
.Ltmp1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
.Ltmp2:
.cfi_def_cfa_register %rbp
subq $16, %rsp
xorl %eax, %eax
movl %eax, %edi
movabsq $.L.str, %rsi
movb $0, %al
callq get_string
movabsq $.L.str.1, %rdi
movq %rax, -8(%rbp)
movq -8(%rbp), %rsi
movb $0, %al
callq printf
...
The next step is to take the assembly code and translate it to instructions in binary by assembling it.
Now, the final step is linking, where the contents of linked libraries, like cs50.c
, are actually included in our program as binary.
Let’s say we wrote this program, buggy0
:
int main(void)
{
printf("hello, world\\n")
}
make
this program, that we didn’t include a missing header file.help50 make buggy0
, which will tell us, at the end, that we should #include <stdio.h>
, which contains printf
.Let’s look at another program:
#include <stdio.h>
int main(void)
{
for (int i = 0; i <= 10; i++)
{
printf("#\\n");
}
}
Hmm, we intended to only see 10 #
s, but there are 11. If we didn’t know what the problem is (since our program is working as we wrote it), we could add another print line to help us:
#include <stdio.h>
int main(void)
{
for (int i = 0; i <= 10; i++)
{
printf("i is %i\\n", i);
printf("#\\n");
}
}
Now, we see that i
started at 0 and continued until it was 10, but we should have it stop once it’s at 10.
If we wrote our program without any whitespace, like the below, it would still be correct:
#include <stdio.h>
int main(void)
{
for (int i = 0; i < 10; i++)
{
printf("i is %i\\n", i);
printf("#\\n");
}
}
style50 buggy2.c
, and see suggestions for what we should change.So to recap, we have three tools to help us improve our code:
help50
printf
style50
Inside our computers, we have chips called RAM, random-access memory, that stores data for short-term use. We might save a file to our hard drive (or SSD) for long-term storage, but when we open it and start making changes, it gets copied to RAM. Though RAM is much smaller, and temporary (until the power is turned off), it is much faster.
We can think of bytes, stored in RAM, as though they were in a grid:
In C, when we create a variable of type char
, which will be sized one byte, it will physically be stored in one of those boxes in RAM. An integer, with 4 bytes, will take up four of those boxes.