programmers
Source code is what programmers write, Harvard CS-50 course's First lecture

Thank you.
All right, this is CS50, and this is week one, our second week together.
And you'll recall that last week, week zero, we focused on Scratch,
ultimately, this graphical programming language by which you can drag and drop
puzzle pieces that interlock together, only if it makes logical sense
to do so.
And many of you had actually probably played with that in like middle school
or even prior at some point.
But for our purposes, the goals of Scratch
were to give us sort of a mental model for some fundamental constructs
that we're going to see again and again today in C in a few weeks in Python
and even thereafter.
And those include things like functions and return variables and arguments
and variables and loops and conditionals and more.
And so even if today feels like a bit of a fire hose, such as that pictured
here, I appreciate that a lot of today's ideas are exactly the same
as last week's ideas.
It's just that the syntax is going to change.
It's going to look a little different.
It's going to look a little scarier.
It's going to be harder to sort of memorize, except with practice,
will come that muscle memory.
But the ideas ultimately are going to be the same.
And indeed, this is, if unfamiliar, MIT down the road
has a tradition of hacks whereby students once a year do something fairly crazy.
And at this point, they happen to connect an actual working,
drinking fountain to an actual fire hydrant.
And the sign there, very pixelated, says, getting an education from MIT
is like trying to drink from a fire hose.
And that's indeed how computer science, how programming, how CS50
will sometimes feel, but realize that what's going to be ultimately most
important is not where you feel you are day after day,
but where three months from now you feel that you are relative to last week
alone, so-called week zero.
So let's look back at what week zero looked like.
It looked a little something like this, the simplest of programs,
via which we get that cat to say, hello, world.
Today, that same code is going to start to look a little like this, which
was a glimpse we gave you last week.
But this time, I've deliberately color coded it
to try to send the message that, whereas in Scratch,
we had this yellowish puzzle piece that sort of kicked things off,
that didn't really do anything itself, but it got the program started,
whereas the real work was done in purple here.
Same is going to be true today, whereby I'm
going to wave my hands for a little bit of time
at this yellowish code on the screen, but what's really going to have
the most effect is this same purple line here and the white text within.
And we'll break down what all of these lines mean over the next couple of weeks,
but sometimes we'll wave our hand at details
if we feel it's a little unnecessary at this point in the story.
And in fact, let me get rid of the color coding for now,
and we'll see that this is the kind of code in a language called C
we're going to start playing with and using today and for the next several weeks.
And indeed, it's representative of what we're going to generally call source code.
So source code is what programmers write.
It's what you write.
It's what you wrote, albeit by dragging and dropping puzzle pieces.
This week onward, you're going to start using your keyboard all the more,
and you're going to write source code.
So this is code that we humans can understand with some training and with some practice.
But of course, per last week, what language do computers ultimately
understand?
Only.
So binary, zeros and ones.
And so you and I, yes, can write code starting today in a form that looks
a little something like this, which admittedly might look a little arcane
and cryptic, but it's certainly better than a whole bunch of zeros and ones.
But we're going to write in source code, but the machines that we write code for
ultimately only understand these here zeros and ones, which may very well
say hello world, but we're going to call this moving forward machine code.
So machine code is what the computers understand.
Only the zeros and ones.
Source code is what you and I understand and actually write.
So it stands to reason that we're going to have to somehow translate one
to the other from source code to machine code.
And I alluded to this ever so briefly last week,
but we're going to use the same mental model whereby the source code we write
might be the input to some problem.
The output we want there from is going to be the machine code.
So what we're going to equip you with today inside of this proverbial black box
is a special piece of software that takes source code as input, produces
machine code as output, and that type of program is called a compiler.
And there's bunches of differences of compilers in the world.
We're going to have you use one of the most popular ones,
but it's simply a piece of software that someone else wrote that converts
one language to another.
Source code, for instance, in a language called C, to machine code,
the zeros and ones that are max PCs, phones, and other devices actually understand.
So where are we going to do this and how are we going to do this?
So I promised last week that we'd introduce you to this year tool,
which I used briefly at the very start of class to whip up that chat bot.
We're going to use it though not for Python this week, but indeed for a different language
C. And indeed, this tool, Visual Studio Code, or VS Code for short,
is super popular in industry.
This is what real programmers, so to speak, are using all of the time nowadays.
There's absolutely alternatives.
If some of you have programmed before, you might have used or experienced
different tools.
But this is a very common tool that you'll see even after CS50.
And in fact, it's something that ultimately you can install for free
on your own Macs and PCs so that by the end of the course,
you're completely independent of CS50 and any CS50 related tools.
But what we have done for the very start of the class
is essentially provided you with a cloud-based version of this tool.
So all you need is a web browser on any Mac or PC or the like so that everything
is pre-installed for you, pre-configured for you.
You don't have to deal with the stupid technical support
headaches at the start of the term because it should just work.
But by the end of the term, once you're a little more comfortable with technology
and with code in particular, you can absolutely off-board yourself
from this tool, install it, download it on your own Mac and PC,
and have pretty much the exact same environment completely under your control.
So starting today, you're going to see an interface that looks quite like this
quite often.
And we used this same interface last week ever so briefly.
Moving forward, here's we're going to write code.
At top right is where one or more code tabs are going to appear,
similar to any tabbed environment that you might use.
Here, for instance, is just a screenshot of the first file
we'll create today called hello.c.
The reason it's called hello.c is because it's
in a language called C, as we soon shall see, no pun intended.
Meanwhile, the code here happens to be color coded, not quite in the same way
as you saw before because I manually made it look more like scratch blocks.
But among the features that VS Code and other programming environments
provide is something called syntax highlighting, whereby you don't worry
about or even think about these colors.
But as you write out code in a recognized language,
tools like VS Code will just color code different parts of your code for you
just to make different features jump out.
And we'll see what those features are over the course of today.
But you'll also spend a good amount of time, as I briefly did last week,
down here in the bottom right of your screen,
the so-called terminal window, which is going to be where you run commands
for compiling code and writing code.
And in fact, as we'll see today, you're going to start using your mouse
and clicking a little bit less.
You're going to start using your keyboard and typing a bit more.
And ultimately, even though at first that might feel like a step backwards
to sort of not use something that's so user friendly,
the reality is most every programmer tends to find themselves ultimately
much more productive, much more powerful using the keyboard more often,
more quickly than, say, a traditional mouse or trackpad would allow.
Meanwhile, we'll see some somewhat familiar features here at left.
Like this is where you'll see the files and folders that will create over time.
At far left here is going to be an activity bar, which is essentially
a modern form of a menu via which you can open and close things and access
other features.
For my purposes, I'll generally hide this part here.
I'll generally hide this part here so that when we're together,
we're focusing almost entirely on code and commands.
But I'm just typing some quick keyboard shortcuts
to simplify my own user interface in that way.
So with all that said, just some terminology.
So this whole collective environment that I'm describing here
is generally what's known as a graphical user interface.
Why?
Well, it's an interface for users that's graphical in nature,
with icons and buttons and the like.
Shorthand notation for this is GUI, GUI for short.
But within this graphical user interface, as promised,
is going to be that terminal window at bottom right where I promised we
would be typing most of our commands.
And just to give you a bit more jargon in computing,
that's generally known as a command line interface, or CLI for short,
whereby you're typing commands into that interface instead.
And the world of computing software is essentially
divided into GUIs and CLIs.
And sometimes a piece of software might have one of each as well.
But without further ado, why don't we go ahead and focus entirely
first on this here program, which I dare say is the simplest program
you can write in a language like C, and see how we can actually compile
and run it together.
So I'm going to go over to VS Code here, where I've hidden my file explorer
with all the icons.
And I've hidden my activity bar so that only do I have room for tabs of code
and the command prompt at the bottom.
I'm calling this a command prompt because it's at this dollar sign
where I'm going to run some of my commands.
And it's a dollar sign by convention.
It has nothing to do with currency.
It's just a computing convention.
Some systems will use a carrot symbol.
Some systems will use a greater than symbol, rather, or something else.
But it just means type your commands here.
The first such command I'm going to type is this.
Code hello.c, with a single space in between.
I've not used any spaces in the name of the file.
I've not capitalized any aspect of the file just because this is convention,
unlike your Mac or PC, where you might be in the habit of naming files
with spaces and capitalization.
Generally, you'll make your life simpler by just using lower case
and no spaces at all.
As soon as I hit Enter, what you'll see is that a brand new tab appears
called hello.c, with a cursor blinking on line 1.
And this is essentially VS code, waiting for me now
to type the first line of my code.
Notice, though, that the command is complete,
whereby I have another cursor here, which if I click in the terminal window
and give foreground to it, my cursor might blink there instead.
That just means I can type another command when I am ready.
So let's go ahead and whip up this code.
And I've done this many times, so I can type it fairly quickly.
But in this tab, I'm going to do include standard IO.h, so to speak,
int main void, then inside of so-called curly braces,
indenting therein by four spaces.
I'm going to say print f, quote unquote, hello, comma, world, backslash,
and close quote, semicolon.
And voila, I've written my first program in C. In a class like this,
no need to write down each and every line of code that I write.
In fact, on the course's website will be copies of everything
that we've done, as well as excerpts therefrom in the course's notes.
But you're welcome but not expected to follow along in real time
with what I am typing here.
So that's it.
Like I've written my very first program in C.
If I had done this on an actual Mac or PC without a command line interface,
I might have a new icon on my desktop, so to speak, called hello.
And ideally, I could double click on that or tap on it and run the program.
But because I'm in this specific programming environment
that has a mix of a GUI and a CLI, I actually
need to click down in my terminal window,
and I need to now compile this program first.
Because at this point in time, it exists only as source code.
So to do this, I'm going to compile my code by very aptly saying make space
hello.
And I'm pronouncing the space, but literally I hit the space bar.
Make space hello, as it sort of implies semantically,
will make a program called hello.
Notice I have not said hello.c again because the compiler, let's call it
make for now, even though that's a bit of a white lie,
is going to infer that if I want to make a program called hello,
it's going to automatically look for a file called hello.c in this case.
So a bit of magic.
Enter.
And remarkably, anytime you don't see any output at a command like this,
that's probably a good thing.
Generally speaking, when you see output, when compiling your code,
you have done something wrong, or in this case, I might have done something wrong.
But no output is good, because what I can now do, and this is a bit cryptic,
I can run this program not by double clicking or tapping anywhere,
but by doing dot slash hello with no spaces.
And this is a bit weird, but what the dot slash means
is that after having just made a program called hello,
that program is going to end up in my current folder.
It's somewhere in the cloud, yes, more on that in a bit,
but the program called hello is just somewhere in my current folder.
When I say dot slash, that's like saying go into the current folder
and run the program therein called hello specifically.
Now, as I'll often do, I'll cross my fingers,
hope that I didn't mess this up in any way,
and I should see in a second, hello world indeed printed onto the screen.
And so just to recap those in commands, one,
I ran code hello dot c, which is a VS Code specific thing.
Code, short for VS Code, just creates a new file called hello dot c,
and then I'm on my way with my own keyboard.
Make hello compiles that source code into machine code,
thereby creating a new file called hello.
And to run that program, hello, I type this strange command dot slash hello.
But this is a paradigm, no matter what you call your programs,
we're going to see again and again and again.
So even if you've not done something quite like this,
it will very quickly get familiar.
Yes, questions?
When you say make hello, how does the computer know
what part of the code is described to hello?
Good question.
When I say make hello, how does the computer
know what part of the code is described to this program hello,
it literally is going to take the entire contents of hello dot c
and turn them somehow into a program.
And does it have to be like the name hello or the name?
Does it have to be named hello?
No.
I could have called it goodbye or anything more my first program dot c,
anything at all.
So long as I change these words here accordingly.
But it has to be like, it needs to be like from the same thing.
Yes.
If I have like green dot c, it's going to make green or whatever.
Exactly.
If you change the name there, you need to change your commands accordingly.
Other questions on these here?
Steps.
No?
All right.
So let's tease apart what it is we just did and why this code works
in the way that it does.
Well, to recap, in Scratch, we had a program like this.
When the green flag was clicked, we wanted to say hello world onto the screen.
The code that corresponds to that is roughly here.
And indeed, notice that the yellowish or orangish code lines up
with the when green flag clicked.
The purple code here lines up with the say block.
And the white code inside of here roughly corresponds
to what was in the white oval that we kept using again and again last week.
So let's do more of a one-to-one correspondence.
And these slides are deliberately designed
to give you again that sort of mental model of taking same ideas from last week
and just changing the syntax this week onward.
So when we have a function like this thing here,
and recall that a function is just an action or verb,
it sort of accomplishes a small piece of work,
in code, in C specifically, you're going to type, of course, not a purple puzzle
piece, but you're going to say the word print.
Well, more technically, print F, where the F as we'll soon see
means format the printed output.
Because this is more powerful than just printing some raw text alone.
Then you can have parentheses, open and close, left and right.
And notice that it's no accident that MIT chose an oval for their input
to functions, because it roughly looks like the start of a parenthesis
and parenthesis on left and right.
Meanwhile, what goes inside of the parentheses in the corresponding
C code?
Well, at the end of the day, minimally, hello, comma, world,
because that's literally what we want to print to the screen.
But in C, unlike in Scratch, there's a bit of overhead,
a bit of additional syntax that you just got to deal with to make clear
to the computer what you want to print.
In particular, you're going to have to surround everything
you want to print with double quotes to make
clear that hello is not some special function or variable or something
else.
It's hello world is the English phrase that you want to print.
So double quote here, double quote there means here's the beginning
and the end of what I want to print.
You're also curiously going to put a backslash n in most cases
at the end of the word or words you want to print.
We'll take that away in a moment and see what it does.
And then lastly, and perhaps most annoyingly in programming circles,
you have to finish your thought with a semicolon.
Much like in English, you would finish most sentences with a period instead.
And the thing about programming is with C in particular,
if you mess up almost any of these details I just rattled off,
something's going to go wrong.
And so you're in good company.
The very first program you try to write or try to compile,
odds are it might not work correctly because you'll
develop over time the muscle memory for spotting all of these seemingly
minor and actually minor details, but that do matter to the computer.
All right, so if you're familiar, of course,
with the notation in mathematics of functions,
like a function in code is really the same idea as a function in math,
whereby the function f takes some input, for instance, x,
and generally produces some output.
So if you're coming more from that background,
realize that what we're really doing here is roughly the same.
But in code, recall that we can have different types of output.
So if this is our grand mental model and say
we've got a function as inside of this black box that takes arguments,
that is to say, as its inputs, it can sometimes have side effects.
And recall that side effects are often visual things that happen as a result.
They just play on the screen.
Maybe it comes out of the speaker.
It's something generally ephemeral that just happens.
But it's not necessarily useful in the same way
as another type of function that we'll return to in just a bit.
But last week, recall that we got the cat with a speech bubble
to manifest on the screen and say, hello, world in that speech bubble.
When the input was hello, world.
And the corresponding function was instead say.
So let's see if we can't now tease apart what the code we wrote is actually
doing for us bit by bit.
So let me go back to VS Code here.
And let me propose to break this in a little way.
Let me delete the backslash n if only because at first glance,
who knows or cares what that's doing.
Let's just get rid of it if we don't understand it.
I could now go back down to my terminal window.
And I could do dot slash hello enter again.
But there's seemingly no change, which is good.
Doesn't seem like I broke it.
But I've kind of misled you here.
Why?
Why did nothing seem to change?
I didn't recompile it.
So recall that the compiler converts source code to machine code.
But I already did that a couple of minutes ago.
If I've changed the source code, it stands
to reason that I need to recompile the code to actually see the effects of that.
So let me do that again.
Make hello enter.
Nothing seems to have gone wrong.
But let me now do dot slash hello enter.
And it's subtle now.
And in fact, let me go ahead and zoom in.
It's really just an aesthetic bug.
And so far as functionally, the program is still technically printing
hello world.
But what's seemingly wrong?
Or put another way, what did the backslash n apparently do?
Yeah.
Yeah, so it's somehow giving me a new line.
And that's essentially what the backslash n denotes
is give me a new line there.
And why was I doing that?
Well, really just for the aesthetics.
If this dollar sign represents my prompt where I type commands,
if anything, it just looks kind of stupid
that I finished a program over here, and then the prompt is on the same line.
It just looks wrong, even though you could sort of argue that was my intent,
even though in this case it wasn't.
So what would the alternative be?
Well, what you're seeing here is what's actually generally known
as an escape sequence, which are sort of special sequences of symbols,
like backslash and n, in this case, that do a little something unusual.
And here's just a non-exhaustive list of some you'll encounter in the real world.
And including in CS50, backslash n moves you to a new line.
Backslash r is a so-called carriage return.
If you've ever seen or used an old school typewriter,
this refers to the process of bringing the typing head back to the left n.
So it sort of moves the cursor horizontally, as opposed to vertically.
This one's interesting.
Backslash double quote.
Why does there exist this pattern?
Backslash double quote.
Yeah.
If you just write double quote, it closes the step you're trying to say.
Exactly.
So recall that phrase we tried to type, print out, like hello, comma, world.
If for some reason you don't want to say hello, world,
but you wanted to say some sort of snarkily, like hello, world, or something
like that, well, you can't put a quote, a quote, a quote, a quote,
and expect the computer to know which quote corresponds to what.
It's just arguably ambiguous.
So if inside of double quotes, you actually
want to print actual double quotes, this is a escape sequence that
tells the computer, this is not some quote delineating where my thought begins
and ends.
This is literally a double quote.
And we'll see other situations in which a single quote or apostrophe
is the same.
We'll see crazy situations in which you want to print a backslash.
But backslash already has some special meaning.
So there's solutions to all of these problems.
But let's not get too far into the weeds here.
But let me go back to the code and propose what the alternative otherwise
might have been.
If I didn't know about backslash n, my instinct to move the cursor
to the next line might have been literally to just hit Enter or do
something like this, like move the double quote,
move the parenthesis, move the semicolon on to the next line.
But this should start to rub you the wrong way.
And indeed, this violates a principle of most programming languages
and that most programming languages are line-based.
You sort of start and finish your thought, ideally, on the same line.
And this runs a foul of that.
And two, even if you're seeing code for the first time,
assume that this just looks stupid as well,
to sort of move part of your thought to the next line.
It just looks a little sloppy, and it is.
So C and many other languages, Python among them,
solve this by giving you these so-called escape sequences.
So if you want a new line there, you do backslash n,
and you will get your new line there.
Now, that's a bit of an overstatement what I said,
and that sometimes lines of code will be so long that they do wrap
onto multiple lines.
But generally, that's a convention that we're going to try to avoid.
All right, what else could go wrong?
Well, let's do this.
Let me go ahead and clear my terminal window, which I can do by hitting Control L,
or I can literally type clear.
And I'm going to frequently do this just to keep the screen clear,
even though it has no functional impact.
It's just an aesthetic.
Let me do something else accidentally.
Suppose I forgot to finish my thought, and I omitted the semicolon.
But otherwise, the code is perfect.
Let me do make hello now.
Enter.
Now we're going to see some output that's a little more arcane.
Let me go ahead and scroll back up here to make
clear that what's just happened is I ran make hello,
but I didn't get back to another prompt.
I don't see immediately a dollar sign because there's an error message here
that is almost as long as the code I tried to write.
Not to worry, let's see.
Here is the name of the file in which the problem exists.
Stands to reason that it's in hello.c.
Here is the line number in which the problem seems to exist, line 5.
And that's helpful because it lines up with this.
And then if you care to count, this is the 29th character.
So if I count from left to right, around character 29, something is wrong.
Something is missing.
So it's a pretty decent error message.
In fact, it even says expected semicolon after expression.
There's a little green carrot symbol pointing me at the mistake.
So this is, again, this is another value of the compiler.
Not only does it know how to convert source code to machine code,
it's also pretty good at finding mistakes in your code
and trying to draw your attention to them.
So how do I fix this?
Well, assuming you've understood the error message at this point,
well, you just go back in, add the semicolon.
Let me go back down to my terminal window.
I'm going to clear it just to clean up the mess.
Let me rerun make hello, and now we are back in business.
And indeed, if I do dot slash hello, I've got hello world back on the screen.
Well, let's make one other mistake.
Suppose that I forgot, as you sometimes will,
to include this line at the top, which will make more sense next week.
But for now, let's just omit it and dive right into the code.
You would think this is enough, just printing out hello world.
Well, here, let me go back down to my terminal window.
Let me do make hello again now, and I'm going
to get a whole different error message instead.
So now, problem is still with hello dot c.
That makes sense.
Line 3, OK, so somewhere in there, printf
is suddenly the problem, even though the semicolon is back,
and the backslash and is back.
So let's keep reading.
Error, call to undeclared library function printf with type int,
and then this is a whole mouthful.
So here is an example of an error message
that unless you're sort of conditioned to know what this means,
and you've seen it before, it's quite more cryptic and unclear
like what the solution to the problem is, especially when the rest of your code
is truly correct.
I've just forgotten something stupid.
But how can I sort of think about this problem?
Well, it turns out that another feature of c
is that it comes with a bunch of header files, a bunch of files whose
names don't end in dot c, but end in dot h.
And these so-called header files, which end in dot h,
are contain code that other people wrote that you can use in your own programs.
So for instance, in this particular case,
a header file is giving us access to what's more generally in computing
called a library.
A library is code someone else wrote that you can use.
And I actually used a library last week.
When I did that import line and mentioned OpenAI, the company,
I was actually using a library from that company
that I had automatically downloaded and installed
into my programming environment in advance of class,
because I don't know how to implement a chat bot without standing
on their shoulders and using a lot of the code they themselves wrote.
Same idea here.
Even though printf is a feature of c, if you want to use it,
you have to include that library by telling your program
to include the header file that defines that function.
And you only know this by being taught it or looking it up in a book or a reference.
But in this case, I wanted to use a header file called standard i o dot h,
std i o dot h.
It is not studio dot h.
This is a very common bug online.
If you find yourself typing studio dot h, typo, it's standard i o dot h.
And in that file, then, is defined the printf function.
So if I go back to my code here, the solution to this problem
truly is to just undo the deletion I made a moment ago,
because what line one is now doing for me is it's telling the compiler,
oh, by the way, I didn't write all the code that I'm about to use.
Please include the definition of printf from this other file called standard i
o dot h.
And again, you'd only know this by looking it up in a reference,
attending a lecture, or something like that.
It's not obvious otherwise.
But these are the kinds of things you very quickly look up.
So where do you look them up?
Well, it turns out the ecosystem of C has hundreds of books you can buy or download.
Many, many, many websites among them is one of CS50's own.
And in fact, the conventional way to look stuff up for the programming language
called C is to look at the official manual pages, or man pages for short,
for the C language.
Unfortunately, many of them were written decades ago when they were certainly
written by fairly advanced programmers and not for a broad audience.
And so what we have done is imported all of that freely available documentation,
hosted it at our own URL here, manual.cs50.io.
And we've essentially simplified it for those less comfortable, those of you
who might be less familiar with, less comfortable with technology,
and really for most people who aren't used to reading manual pages,
it's just useful to have it written in teaching assistant-like language instead.
So for instance, if you go to a URL like this,
you'll see CS50's documentation for this official library, standardio.h,
that comes with C itself.
If you go to URL like this, you can look up the documentation for printf
itself specifically.
So for instance, let me go ahead and just give you a teaser for this.
If I were to do the same on my own computer,
I might see the CS50 manual pages here.
And you'll see header file by header file, a bunch of frequently used
functions in CS50.
We've also filtered the list down from a massive list to much shorter list
so that you can sort of see what's most likely useful to you.
If you go to a specific page like standardio.h,
you'll see, for instance, here just over a half dozen functions
that we won't touch on today beyond printf,
but that we'll see in the class over time that does useful stuff.
For instance, printf prints to the screen,
and we'll see other functions for opening files, closing files,
and the like, because all of that's related to standardio, input and output.
If I go to a specific man page for this header file,
you'll see the standard formatting for these pages.
So here's the name of the function, printf, and it prints to the screen.
You'll see a synopsis, and this indeed indicates we're in less comfortable mode.
If you want to see the original, more arcane documentation, just uncheck that,
and you'll see the original official documentation.
But you'll see a mention of what header file this function is defined in
so that you know what file to use in your own code.
You'll see a so-called prototype, which is just the first line of code
from that function, more on that in just a little bit.
You'll see an English description.
You'll see example code, long story short, this is the authoritative answer.
And even though you have access in this class to the virtual rubber duck,
it's the s50.ai, and other forms of it that you'll soon see,
you should also have the tendency and the instinct moving forward
to check the official documentation.
And all of today's AIs are trained on things like the official documentation.
So that's the source material that any of these AIs, the duck among them,
are actually relying on.
But what we're also going to see is that besides these official functions,
there's some that CS50 itself has invented.
We use these really as training wheels for just the first few weeks of the course.
And then we take these training wheels off.
But the reality is in a language like C, certain stuff is just really hard
or annoying to do.
Certainly if you're learning how to program for the very first time,
or at least you are new to C. We'll eventually show you how to do it
that way.
But even if you just want to get input from the user,
like a string of text or a number of some sort,
it's generally not that easy to do in C, at least in these early days.
So for instance, at this URL here, you can see documentation for CS50's own library
and CS50's own header file, CS50.h.
And you'll see such functions in the documentation as these.
Get string, get int, get char, and a bunch of others as well.
And we'll touch on those this week.
But it will ultimately be a way of just getting useful work done quickly
by standing on our shoulders and actually using functions we wrote
to then solve problems of interest to you.
So let's focus, for instance, on one of these first.
Get string.
A string in programming speak means text, zero or more characters of text,
like H-E-L-L-O comma space W-O-R-L-D.
That is a string of text in computer speak.
And it's obviously not a number, like 50.
It's actual text that you would type on the keyboard.
We'll see then what other things we want to get.
But with this function, we can start to replicate another program
that we implemented pretty quickly last week in Scratch.
So recall that in Scratch, this one was a little more interactive.
I used another blue puzzle piece, Ask, to actually get input from the user.
And recall that unlike the print def function today and the say block
last week, this time we still have the same input output model.
But if we pass an arguments to a function that we're about to see,
you can get back not just a side effect sometimes, but a return value,
like a useful reusable value like the person's name, as we'll soon see.
All right, so let's actually do this.
If in Scratch, the equivalent was asking the user, what's your name,
asking them that, and then waiting for an answer
that we can store in a variable, let me propose that in C side by side,
it's going to look a little something like this instead.
At left, we have the Scratch block, the Ask function.
Here is the argument there, too.
And then wait just means it's going to wait till the user finishes typing.
If I want to translate this to C now, today moving forward,
well, it looks a little something like this.
The closest analog in C, thanks to CS50's library,
is going to be a function called getString.
So there's no C function called ask.
And we deliberately name this function getString
just to make super clear what it is you are getting.
It's got a string of text in this case.
And we've got the parentheses ready to go,
indicative of this white oval for user input.
If I want to prompt the user with that same phrase, what's your name?
Well, I can just put it inside of those parentheses.
But what next do I need to add around my user input?
You need to add the quotation marks.
Yeah, I need the quotation marks just to make clear
that these aren't special individual words.
This is a whole phrase that I want to be displayed to the user.
So I'm going to indeed put double quotes around everything.
And this is just an aesthetic.
I don't, in this case, want to bother moving the cursor to the next line.
Like, I want the user to see the question.
And I want the cursor to just stay there, blinking, waiting for their prompt.
But I don't want the cursor to be right next to the question mark.
So I'm deliberately just leaving a single white space there just
to kind of scooch it over a bit so it looks a little prettier, at least
to my eye.
Now, we're not done yet because we need to do something with this value.
The getString function, as we'll soon see,
is going to prompt the user for me to type something in like my name.
But where do I want to put that?
Well, MIT has the answer put in a variable called answer.
And you can't rename that in Scratch.
It's just defined as answer.
But in C, what I'm going to need to do is something like this.
If you want to keep return values around from a function,
you literally use an equal sign.
And then to the left of it, you put the name of the variable
into which you want to put that return value.
So in mathematics, we would use x, y, and z as our variables.
Again, in code, as in Scratch, you can name your variables anything you want.
By convention, they should usually be lowercase.
They should not have spaces therein.
Some are alert file names.
But this is a pretty good analog now of what's going on collectively here.
But C is a little more precise.
You can't just give the variable a name.
You need to tell C, or really the compiler, what type of value
you want to put in this variable.
So if it's a string of text, you put string.
If it's a number, you're going to put something else.
But for now, it's a string per the function's name.
It's going to give me a string.
Now, we're so close to finishing this comparison.
There's one detail missing.
What's still missing from the code here?
Yeah.
Yeah, so we have to finish the thought, lastly, with a semicolon.
So if you're getting to the point already,
this is one of the reasons why we start with Scratch.
You get the intuition pretty quickly.
And even though nothing on the right-hand side is particularly hard,
there's just all these stupid little details
that you have to ingrain in yourself over time.
In this case, for C, but for many programming languages,
we're going to see the similar paradigm.
But among the goals of the course, too, are to show you how ultimately
languages have been evolving.
And so one of the things we'll see in Python in a few weeks' time,
that some of this syntax actually goes away.
Because over time, humans have gotten annoyed at older languages
like this, like, why the heck do I have to keep putting a semicolon when
it's clear that I'm at the end of the line?
So we'll see among languages like Python,
we can get rid of some of these same features.
But for now, it's just a matter of remembering what goes where.
All right, so let's go ahead now and take that same idea of converting Scratch
to see and actually do something with this code.
Let me go back to VS Code here.
I'm going to keep my file name the same.
But what you'll see on CS50's website is that we'll add version numbers
to each of the examples that I'm typing out,
so you can actually see the progression of these programs,
even though we're not changing the name.
And what I'm going to go ahead and do here, for instance, in hello.c
this time is the following.
I'm going to go ahead and first get rid of the single hello world.
I'm going to go up here and include this time, CS50.h, so not one,
but two header files.
And then inside of my curly braces, inside this so-called main function,
as we'll soon call it, I'm going to go ahead and do this.
Exactly the same line of code as on the screen before.
I'm going to get a string prompting the user for what's your name,
question mark, space, close quote, semicolon.
And as an aside, this will we'll soon see, print on the screen.
What's your name?
So that implies that the getString function is actually
using printf itself to print out that message.
I do not need to use printf to display that message on the screen,
because I read the documentation for CS50's getString function,
and I just know that it is using printf for me to achieve that particular goal.
Now let me do something intuitive, but not quite correct.
If I want to print out that answer so that the expression is going to be
not hello world, but hello David or hello Kelly,
let me go ahead and say hello comma answer, backslash n
to move the cursor down as before, semicolon.
So this is not quite right, and even if you've never programmed before,
you can perhaps see where this is erroneously going.
Let me remake the program, because I've changed the source code
and I need new machine code.
Nothing seems to be wrong, logic rather syntactically.
But if I do now dot slash hello and hit Enter,
you'll see I'm being prompt.
What's your name?
So I'm going to go ahead and type in David and then hit Enter.
But when I do, if you know where this is going,
what am I going to see instead?
Hello answer.
And the computer's just doing literally what I told it to do.
I said quote unquote, print out hello answer.
But obviously, that's not the goal that I have in mind.
So how do I actually work around that?
Well, what I really need to do is achieve
the equivalent of this thing here, which we did by stacking blocks
in Scratch or nesting them, if you will, one inside of the other.
So I want to join the expression hello comma space and that answer.
And it turns out in C, you can't do it quite like this.
Like there isn't an analog of the join function, at least that we'll see today.
So we have to do this a little bit differently.
We can do it, though, by maybe telling the computer,
well, go ahead and print out hello comma space.
And then maybe we can give it like a placeholder
to plug in the name once we know the name.
Because when I'm writing my code, I have no idea
who's going to play this game, me or Kelly or someone else.
So what if we use special syntax to indicate
where I want the person's name actually to go?
Let me propose that we now do this.
Instead of printing out hello comma answer quote unquote,
let's go ahead and start printing out something.
And I got my parentheses ready to go and I did my semicolon in advance this time.
I want to somehow now say hello comma placeholder.
And you would only know this by someone having told you or a reference online.
Percent S is the placeholder for a string
that you don't know when you're writing the code.
But when someone else is running the code,
it will be filled in and substituted for their input.
So hello comma percent S is the closest we can get to this.
I still need, though, some other syntax.
I still do need those quotes on the left and the right.
Just to be aesthetically pleasing, I'm going to put a backslash n there
at the end to move the cursor.
But now I've left room in my parentheses for one more thing.
And you could perhaps guess where I'm going with this.
Again, even if you've never programmed before,
this is telling printf print out H-E-L-L-O comma space something,
what should I probably pass in to these parentheses as a second input
so that printf knows what that something is?
Yeah?
The variable name.
So the variable in which I have the user's name and indeed,
the convention is to put a comma after the quotes
and then the name of the variable that has the value you want to be
substituted for that placeholder.
Now notice there's a collision of syntax and grammar here.
The comma inside of the quotes is just an English thing.
Hello comma so and so.
The comma outside of the quotes is meaningful to C
because it delineates which is the first input or argument to left
and which now is the second.
And we haven't seen this before in C. Up until now,
we've only been passing one input, but you can pass in two or three or four.
Completely depends on what the function is designed to expect.
So let me put this all together now.
Let me go back to VS code.
Previously, we were literally printing out answer,
but I can change answer to percent s.
I can move my cursor outside of those quotes comma answer
because that's the name I gave to that variable.
I can go back down to my terminal window and clear it just to reduce clutter.
Let me do make hello one more time.
Seems to work dot slash hello enter david.
And now, hello comma David is printed.
OK, questions on any and all of that?
I was wondering with the header file, where is it pulling from?
Good question.
Where is it pulling these header files from?
So what you are seeing here is a graphical user interface that's
somewhere hosted in the cloud at cs50.dev, the URL I mentioned last week.
And we're going to tease this apart in just a moment.
That software is running on a computer, and that computer's
got a hard drive or a solid state drive, like folders of storage.
Those files, cs50.h and standard io.h and many more,
are pre-installed on the server to which I have connected.
And they're stored in a standard place so that the compiler in particular
knows where to look for them.
And those are all things we did in advance for you.
Yeah?
Why does the backslash n not create a new line?
Why does the backslash n not create a new line?
So it is, backslash n is essentially being printed here,
which has the effect of pushing the dollar sign to the next line.
Otherwise, the dollar sign would stay on that second to last line.
Other questions?
Why is there no backslash n on this string?
Good.
Why is there no backslash n over here?
Good question.
My choice is the programmer.
I just wanted to see the sentence.
What's your name, question mark?
And I wanted the user, me, to type my name immediately after it,
like this.
But I didn't have to do it that way.
I just wanted to show you the difference.
Got you.
And then also, like, just generate what we're doing,
like the word, actually, always write the first four lines?
Should you always write the first four?
Oh, these?
Yes.
For today, trust me, do this, do this, do this, do this.
And next week, we'll understand even more what those lines do.
However, slight caveat, only use CS50.h if you're using one of our functions.
Clearly, you don't need CS50.h if you're just
printing something out, as in the first example.
Other questions?
My second comma is dividing the first input and the second input.
I understand that the second input is what I type as the user.
The first input does not really feel like input for me,
because that's the question that you ask.
Can you, like, expand a little bit when you both say input?
Correct.
So to summarize the question, on the right here,
this input is effectively provided by the user.
This first input, though, is provided by me.
That's the way it is.
So these are both inputs, because they're being provided as inputs
to the function.
The origins of those inputs, though, are entirely
up to what I'm trying to achieve.
The first one I know in advance.
Like, I'm the programmer.
I know I wanted to say hello comma, someone.
The second input I don't know in advance,
so I'm using a variable to store the value that I'm
going to get when the getString function is used later on.
But they're both inputs, even though they're used in different ways.
Good question.
Any others?
OK, so if we now have that done, well, let's just take a step back
into the first question that was just asked about where are these files.
Let's take a look back at actually what it is we're actually using here.
So it turns out, even though most of you are using Mac OS or Windows,
there's other operating systems out there in the world.
Phones have iOS.
iPads have iPad OS.
Android devices have Android, which is its own operating system.
The operating systems in the world are the pieces of software
that really just do the most fundamental operations on a device,
like booting it up, shutting it down, sending something to a printer,
displaying something on the screen, managing windows and icons
and all of that sort of commodity stuff that
is used by other people's software as well.
A very popular operating system in the programming world
and in the world of servers in the cloud and on the internet at large
is called Linux.
And it's a descendant of something called Unix, which
has been around for quite some time.
It's what many programmers, most programmers,
use depending on their environments.
Insofar as Linux is very highly performance,
like you can support thousands of millions of users on servers running
an operating system like this.
It tends not to, but it can have a graphical user interface, which just
means it can operate more quickly because it doesn't need all of these graphics
that are really just for humans' benefits, not necessarily for web
browsers and other devices.
And Linux, insofar as it's usually used or often used
as a command line interface, comes with a whole bunch of commands
that you'll start to use and see over time.
Now, I've used a bunch of commands already.
I've used code, which is a VS Code thing.
I have used make, which is, for today's purposes, our compiler,
but that's a little white lie that we'll distill next week.
And then I've used dot slash hello, which is a command I essentially
invented as soon as I created a program called hello.
But there's a bunch of other ones as well.
For instance, if I want to list the files in my current folder,
I can type ls and hit Enter for short.
If I want to create a new folder, otherwise known as a directory,
I can use mkdir to make a directory.
If I want to remove a directory, I can use rm directory.
If I want to remove a file, I can use rm.
If I want to rename a file, I can use mv for move.
If I want to copy a file, cp, if I want to change directories,
change into a folder, I can use cd.
Now, these two just take a little bit of time in practice to memorize them.
And they're all very terse insofar as the whole point of a command line
is to let people navigate things quickly.
So for instance, even though this will be a bit of a whirlwind,
let me go back into VS Code.
And let me propose that we play around with just a few of these commands
so that you've seen me doing it.
But generally speaking, in CS50's problem sets,
we will tell you step by step what commands to type
so that you can achieve the same results.
And then later in the term, we'll stop bothering,
reminding you pedantically how to do this and that,
because it should come more naturally, eventually.
But for instance, let me go ahead and do this.
Let me go ahead and reopen my file explorer at left.
Yours will look a little different.
You'll have a different number is your unique ID,
but generally you'll see whatever files and or folders you've created already.
The first thing I created today was called hello.c.
And then by using make, I created a second file I claimed called hello.
So the reason dot slash hello works is because there is in fact
a program called hello in my current folder, ergo the dot, that was created
when I compiled my source code into machine code.
Now, suppose for the sake of discussion that this is going to get messy quickly,
because the more programs we create in class and for problem sets,
you're just going to have a hot mess of files inside of this one main folder.
Well, let's create subfolders like you might be inclined to do on your Mac
or PC or Google Drive or whatnot.
Well, we can do this in a bunch of ways.
I could right click or control click on my file explorer.
And I'll see a somewhat familiar contextual menu.
And I can literally choose new folder or I can rename things
or I can move things around by dragging and dropping them.
But for today, let's focus more on the CLI, the command line interface,
and again, commands like this.
So let me go back into VS Code and let me propose
that we do a few things just because as a tour.
First, let me delete the machine code.
I'm done with this example.
I don't really want to keep these bits around unnecessarily.
I'm going to delete hello, not hello.c, but hello, the compiled program.
When I type that, I'll be cautioned.
Remove the regular file, whatever that means, called hello.
Here, I'm being prompted for a yes-no response.
Why suffices?
So I'm going to hit Y, Enter, and watch what happens at top left
as soon as I use my terminal window and this command to remove that file.
It disappears.
I could have right clicked on it or control clicked on it,
but this command line interface achieves the same thing.
Now, suppose that for problem set one and future problem sets,
I want to keep every program I write in its own folder just
to keep myself organized, especially as the term progresses.
Well, let me create a new folder called hello itself.
So I don't want to create a program called hello.
I want to create a folder called hello.
Well, one way I can do this, per this here, cheat sheet,
is to make a directory, which just means folder.
So mkdir, hello, Enter, and you'll see at top left, now I indeed have a folder.
And it even has an obvious folder icon next to it.
Now, I could cut some corners.
I could click and drag on hello.c and just drop it into hello.
But again, let's stick with the command line interface.
Let me go ahead now and move mv for short, hello.c into hello.
So this is the first command where I'm passing in not one word after the command,
like code hello.c or make hello.
Now, I'm typing two words after the command,
because the way the move command is designed
is to expect the origin as the first word
and the destination as the second, so to speak.
Whereby, if I want to rename hello.c, sorry,
if I want to move hello.c into the hello folder, I should type like this.
Now, you can, just so you know, include a trailing slash, a forward slash
at the end of the destination just to make clear that you want to put this
into a folder and not just rename hello.c to hello.
But because the hello folder already exists,
Linux knows what it's doing, and it's just
going to assume that when you do that, watch what happens at top left.
Hello.c seems to have disappeared, but if I click this little triangle,
ah, there it is.
It's now inside of that folder.
But now I've created kind of a predicament for myself.
Let me clear my terminal window, and now let me type ls.
And when I type ls for list, you'll see only a folder called hello,
and it's color coded just to call it out to your eyes,
and there's a trailing slash just to make obvious that it's a folder.
That's all done automatically for you by Linux, the operating system.
But wait a minute, where did my hello program go?
Like, where is hello.c?
Well, it's in that folder, so I need to change into that folder or directory.
And here, per the cheat sheet, we have cd for change directory.
So I can do cd space hello, with or without the slash, and hit Enter.
And now you'll see this, and it's admittedly a little cryptic,
but my prompt has now changed to still be a dollar sign,
but before it is just a constant reminder of what folder I am in.
We adopted this as a convention.
Many systems do the same thing, though the formatting
might be a little different.
This is just to help you remember where the heck you are without having
to type some other command to ask the operating system what folder you are in.
So now that I'm here, if I type ls and hit Enter, what should I see?
Just hello.c, because that's the only thing in that there folder.
So now let's do maybe one other thing.
Let's do make hello inside of this folder.
That is OK, and notice at top left what just happened.
Now I've got both files back.
All right, suppose I want to get rid of one.
Well, I can do rm hello again.
I can type y for yes to confirm the deletion,
and now I'm back to where I just was.
Now suppose I want to do get other things.
Suppose that I'm not really proud of this version of hello.c.
Let me keep it, but rename it.
Well, I can say how about mv hello.c to old.c.
I just want to rename the file.
So mv can be used not only to physically move a file from one place
to another.
If you use it on two file names, it will just rename the file for you.
So there's no rename command that you need to use instead.
But you know what?
No, I regret that.
This program was fine.
Let's rename it back.
So let's move old.c back to hello.c and watch at top left.
It just renames the file again.
Let me go ahead and make a backup, though.
So let me copy with cp hello.c into a file called backup.c.
Just in case I screw this up, I want to have a spare around.
Now you see at top left, I've got both files.
If I now type ls, you'll see both files.
So what's happening in the GUI is the exact same thing
is happening in the CLI.
But you know what?
This was just for demonstration's sake.
I don't need any of this.
So let me remove the backup.
Say yes for why.
Let me go ahead and move hello.c out of this folder, which I could just
kind of drag and drop it.
But how do I move hello.c to the parent folder, so to speak?
I want to move it out of this folder?
Well, you would only know this by having been told dot dot is special notation.
That means the so-called parent folder.
So go back up in the hierarchy.
And now, if it's not obvious, a single dot, which we have seen before,
means this folder.
Two dots means one step up.
There's no triple dots or quadruple dots.
You have to use different syntax, but more on that another time.
So watch what happens.
When I do move hello.c up into the parent directory,
notice at top left that the indentation changed
because it's no longer inside of that same folder.
And heck, now I'm going to go ahead and do this.
I could go back to my main folder by doing cd dot dot
to back out of this folder.
But when in doubt, or if you ever get yourself
into a confusing mess, just type cd enter alone,
and you'll be magically whisked away to your default folder, a home directory,
so to speak, even though that too is a bit of a white lie.
So that will lead you always where you're starting when logging in to cs50.dev,
aka VS Code.
And now I can see the folder, which happens to be empty, and the file.
So let me go and do one last command.
Or mdir, hello, to really undo all of the work,
such that we're now back to where the story began.
But the point here is just to demonstrate with these basic fundamental commands,
you can do everything that you've taken for granted on Macs and PCs for years
with a mouse instead.
Questions on any of these here?
Linux commands.
Yeah?
So if you have like five different files in a folder,
how can you like choose what you're going to open?
Really good question.
If you have five different files in a folder,
how can you choose which one to open?
Well, you can certainly do code, space, and the name of the file you want to open.
Or we're going to see other tricks.
Like you can use an asterisk or a star for a so-called wild card,
and say open everything in this folder.
And you can even use more precise patterns than that.
So over time, once we have more files at my disposal,
I'll be able to do tricks like that as well, too.
Yeah?
I don't know if I can talk this, but I can just make some of the all-in thing,
and then I can drop back from the loading.
Uh-huh.
So when you like read a file, I will pull it back to low,
because that's how low it's written and all of this code is written, too.
So how did you make that low?
Sure.
So one of the things I did in my VS Code a moment ago
was once I was inside of the hello folder, into which I had put hello.c,
just for the sake of discussion, I then recompiled it by running make hello.
And this example is a little confusing, deliberately insofar as I've got a file
called hello.c inside of a folder called hello.
But because I compiled hello.c, I then created a program called hello as well.
But that program hello was inside of a folder called hello,
which is only to say that you can totally do this.
You can't have a file and a folder in the same place
named the same thing, because they would collide.
Like, you can't do that on a Mac or a PC as well.
You have to have unique names.
But you can certainly put something inside of another folder without collision.
Good question.
All right.
So let's introduce a few more building blocks and a few more things we can do.
So besides these Linux commands, which we'll now start taking for granted,
we have a bunch of other features of programming languages that we saw in scratch.
Let's now translate them to C. So conditionals,
we're sort of the proverbial fork in the road,
enabling you to do this or this or some other thing based on the answer
to a question, a so-called Boolean expression.
Here, for instance, in scratch is how we might express if a variable x is less
than a variable y, we'll go ahead and say x is less than y.
And out of context, I didn't include it in the slide,
presumably we've created x and y and somehow given them values, whatever they are.
But this is just now the conditional part of the program.
In C, the way you would do the same thing is you would say if and then a space,
then parentheses, which have nothing to do with functions.
If is not a function, it is a feature of C that implements conditionals,
just like this orange block is a feature of scratch.
Inside of the parentheses, you put your same Boolean expression.
So here, too, out of context, if up here I have defined variables x and y,
well, I can certainly use them in this conditional.
And I can use this less than operator, just like in math class,
to ask this question.
And the answer, even though it's a less than sign,
is indeed, if you think about it, going to be true or false.
Yes or no, it's a Boolean expression.
It either is less than or it is not.
All right, inside of the curly braces, which are necessary here,
I'm just going to literally put our old friend printf,
and there's nothing interesting here, except the new phrase, x is less than y,
with the backslash n, the semicolon, and the parentheses.
This, though, is deliberate.
Just like in scratch, the say is sort of indented and sort of hugged by the if
orange puzzle piece.
Similarly, do these curly braces, are they meant to sort of imply the same?
It's sort of embracing these lines of code.
As an aside, in C, they're not always necessary.
If you have a single line of code, you can technically omit them.
However, what you'll see in C, as well as in CS50 in particular,
we will generally preach a certain style, like any company in the real world
would do, so that programmers who are collaborating on code,
all write code that looks the same, so that it doesn't devolve into a mess
because everyone has their own convention.
So this is a convention to which you should indeed adhere,
and then I've indented four spaces to make clear logically
that this line of code only executes if the answer to this question is true.
Or yes.
Meanwhile, in Scratch, if we had an if-else condition,
so a two-way fork in the road, if x is less than y, say so,
else say x is not less than y.
How can I do that in C?
Well, if x less than y, something else.
And what goes in between those curly braces?
Well, just two different printfs, x is less than y, or x is not less than y.
The only new thing here is we've added else and another pair of curly braces,
just like we've got two orange shapes hugging those two purple puzzle pieces
there.
How about something a little more involved?
And this looks like it's escalating quickly,
but it's just because the Scratch puzzle pieces are so big.
If x is less than y, then say x is less than y.
Else if x is greater than y, then say x is greater than y.
Else if x equals y, then say x is equal to y.
How can we do this in C?
Almost the same idea.
If x less than y, else if x greater than y, else if x equals y.
Well, before we reveal what's in the curly braces, this is not a typo.
Why have I presumably done this, even if you've never used C before?
Yeah?
Because it equals the variable sign.
Exactly.
The single equal sign, which we've used already,
when storing a value from getString into a variable,
like answer, is technically the assignment operator.
So humans decades ago decided that when faced with the situation where
they wanted to copy from the right to the left a return value into a variable,
it made sort of visual sense to use an equal sign,
because you want those two things ultimately to be equal,
even though you kind of read the code from right to left in that case.
I can only imagine at some point the same people were in the room,
and they were coming up with the syntax for conditionals, and they're like,
we've already used equals for assignment.
What do we now use for equality?
And the solution in C, as well as in many other languages,
is literally this.
They use two.
So this is the equality operator, whereas a single one
is the assignment operator.
And it's just because.
Now Scratch is designed for kids, no sense in confusing little kids
with equal equal sign.
So Scratch uses a single equal sign, whereas C and most languages
use double equal sign.
So a minor divergence there.
What goes in the curly braces?
Nothing all that interesting, just a bunch more print devs.
But here's an opportunity to distinguish not only the equivalence
of this Scratch code with C code, but a misdesign opportunity
that we sort of tripped over, if briefly, last week.
This is arguably not well-designed, even though it is correct.
Why?
Yeah?
You don't need to ask this third Boolean expression.
Yeah, we don't need to ask this third Boolean expression.
Is x equal equal to y, so to speak?
Well, logically, if we're using sort of normal person numbers,
it's either less than or greater than or by default equal to.
So you're just wasting the computer's time
and in turn the user's time by asking this third question.
So slightly better here would be, get rid of the else if,
just have a default case, an else block, so to speak, that looks like this.
If it stands to reason that there's only three possibilities,
you only really need to interrogate two of them out of the three.
So a minor optimization, but you could imagine doing that again and again
in your code, you don't want to be wasting the computer or the user's
time if you can improve things like that.
All right, so now that we have these equivalences between Scratch code
and C code for these conditionals, well, what other things can we throw into the mix?
Well, C has a whole bunch of operators.
And just so that you've seen a list in one place,
you've got not only assignment, and less than, and greater than,
and equality, but a few others here as well.
Now, even though in Microsoft Word and Google Docs,
you can kind of do a greater than or equal to sign, one over the other,
or less than or equal to.
In C, in most languages, you actually just hit the keyboard twice.
You do the less than and an equal sign, or you do a greater than and the equal sign.
And that's how you achieve the notion of greater than or equal to,
or less than or equal to.
This one we've seen, and you want to guess what exclamation point equals means,
otherwise pronounced bang equals?
Yeah, not equal.
So generally, in programming, you'll see an exclamation point implying
the negation of something else, the opposite.
So you don't want it to be equal to, you want it to be not equal to.
Now, you might think, shouldn't it be not equal equal?
Yes, but they're trying to save keystrokes.
So this is the negation of that, even though it doesn't quite look like it
should be, just two characters instead of three.
And dot, dot, dot, there's many other operators that
won't counter in the wild over time.
But there's also worth noting, in C, more than just strings.
Like strings recall were strings of text,
and there's other types of data that you might get from a user or store.
We've seen string, but we'll actually see a whole bunch of others.
So in C, we're going to see bools themselves,
a variable that can be true or false, and that's it.
So very much interrelated with Boolean expressions,
a variable itself can be true or false.
We're going to see chars or characters, so not strings of text,
like multiple letters and words and the like,
but just individual characters.
C, unlike some languages, does distinguish between single characters
and multiple characters.
Double, or rather, let's jump to float.
A float is otherwise known as a floating point value, which is just
a number that has a decimal point in it, a real number, if you will.
But a float generally uses, nowadays, 32 bits total to represent those numbers.
The catch with that is that how many total values can you represent with 32
bits, roughly per last week?
It was one of the few numbers I proposed you remember.
It's like roughly 4 billion.
But how many real numbers are there in the world, according to math class?
An infinite number.
So we seem to have a mismatch between what we can represent in code
and how many actual numbers there are in the world.
OK, so not to worry, if you need more precision,
like more significant digits, well, you can upgrade your variable,
so to speak, from a float to a double, which uses 64 bits, which
is way more precise, twice as many bits,
but it doesn't fundamentally solve the problem,
because, really, it's still finite and not infinite.
And we'll end today with a look at what the real world implications of that are.
But besides floating point values, there's just simple integers, 0, 1, 2,
and the negatives thereof.
But those conventionally use 32 bits, which
means the highest a computer can count using an int would be 4 billion.
But if you want to do negative numbers, it's going to be roughly 2 billion,
so you can go all the way to negative 2 billion.
So that's not that large nowadays.
Along uses 64 bits, which is a much bigger range of values,
but there, too, still finite.
And there's a bunch of others as well.
So these are just the types of data that we can store and manipulate
in our programs.
But a couple of those, one in particular, specifically come from CS50.h.
So among the things you get by including CS50.h in your code
is access to not only get string, but these other functions as well.
We'll start to use these in a little bit, whereby you can get integers or chars
or doubles or floats.
We don't have a getBull because it's not really
useful to just get a true or false value, typically.
But we could have invented it.
We just chose not to.
But we'll frequently use these here functions that you can access by using
that there header file.
But where are we going to put these values?
And how are we going to display them?
Well, turns out there's more than just percent s.
So percent s was a placeholder for a string.
But if you want to print out something like a char, a single character,
you're actually going to use percent c.
If you want to print out a floating point value,
you're going to use percent f, an integer, percent i, and a long integer.
That is a long.
You're going to use percent li instead.
So in short, there's solutions to all of these problems.
These are not intellectually interesting details,
but they are useful, practical things to eventually absorb over time.
So let's go ahead and do this.
Let's do just a few more examples together.
And a little bit will adjourn for a short break
during which snacks will be served every week out in the transept.
But before we get to that, let's focus on these here variables.
So in Scratch, we had the ability to store a bunch of values in variables
that we could create ourselves by creating new puzzle pieces.
In C, you can essentially achieve the same.
So for instance, suppose that in Scratch,
we wanted to keep track of someone's score using a counter.
Well, we might create a variable called counter
and set it initially to 0, and then eventually add 1 to it, add 2 to it,
and so forth as they drop trash into the trash can, for instance.
Well, in C, you're going to do something almost the same.
You can choose the name of your variable, just like I did previously with answer.
You can assign it a value like 0 initially.
But per earlier, what more am I probably
going to have to do in C on the right-hand side here?
Yeah.
I got to give it a type, and a counter, in so far as it's numeric,
is not going to be a string of text.
And I don't think I need to worry about decimal points if I'm just
counting the equivalent on my fingers.
So int will suffice.
And int is the go-to number, and at least if 2 billion plus values
is more than enough for your case, which this is going to be.
Still one minor thing missing?
Yeah.
The semicolon to finish the thought.
So that on the right is the equivalent to doing this here on the left.
Suppose that in Scratch, you wanted to increment the counter
and add 1 to the score, add 2 to the score, and so forth.
It might look like this.
Change counter by 1, implicitly going up,
unless you did negative, which would go down.
In C, you can do this actually in a few ways.
And this looks a bit wrong at the moment.
How can counter possibly equal counter plus 1?
This does not mean equality, per se.
The single equal sign recalls assignment.
And it means take the value on the right
and copy it to the value on the left, or to the variable,
in this case, on the left.
So this takes whatever the current value of counter is, 0,
adds 1 to it, and then stores that 1 in the counter variable.
So now the value is 1.
And if you do it again, it goes to 2, goes to 3, goes to 4, and so forth.
But honestly, this incrementation technique
is so common that there's more shorthand notation for it.
You can also just do this.
Looks a little weird at first glance.
But counter plus equals 1 semicolon does the exact same thing.
You can just type fewer keystrokes.
And honestly, doing this is so down common in C
that you can even do this.
Counter plus plus.
It's the exact same thing by adding 1 to the variable.
There's no plus, plus, plus, or plus, plus, plus, or more pluses.
It's only for incrementing individual values by 1.
So arguably, this version and this version, albeit more verbose,
are a little more versatile, because you can add 2, or 3, or more at a time.
And there are equivalents for you doing decrementation
and doing minus, minus, or the minus symbol more generally in there.
All right, so let's actually use this technique in some code.
Let me go back into VS Code here.
Let me close my file explorer.
And let's go ahead and create maybe this time
like a little calculator of sorts.
Let me propose that we implement a very baby calculator, or rather,
not even a calculator yet.
Let's just compare some few values.
So let me do this.
Code of compare.c to create a brand new program called compare.
And then in here, I'm going to do a bit of boilerplates.
I'm going to go ahead and include cs50.h.
I'm going to go ahead and include standard.io.h.
And I'm going to go ahead and do int main void, more on that next week.
And then inside the curly braces, let's use these new techniques.
Let's give myself a variable called x and set
it equal to the return value of get int, that other function I promised exists.
And let's prompt the user for a value for x with a sentence like,
what's x, question mark.
And then a space just to nudge the cursor over.
Let's get another variable, y, set it equal to get int again
and ask the user this time, what's y?
Essentially using the same function twice, but to get two different values.
Now, let's go ahead and do something pretty mindless.
If x is less than y, go ahead and print out.
With print def, x is less than y, backslash n to move the cursor.
Close quote, semicolon.
So it's not that interesting of a program,
but it's at least dynamic in that now I'm prompting the user for two numbers.
So let's do this.
Make compare, enter.
Seems to have worked.
And in fact, I can check that it worked by typing what command to list
the files in my directory, ls for short.
And now you'll see I've got hello.c, but no hello,
because I deleted that with RM a few minutes ago.
I've got compare.c, which I just created.
And then I've also got a program called compare.
And the asterisk there is just a visual indicator that this is executable.
It's a program you can run.
It's not just a simple old file.
Even though I didn't type ls previously with hello,
it would have similarly had an asterisk next to it in this context.
But you don't see that in the file explorer.
If I now do compare, well, let's do something silly like 1 for x, 2 for y.
OK, x is less than y.
Let's do it again.
Dot slash compare.
2 for x, 1 for y.
OK, and I see nothing.
Well, why am I seeing nothing?
Well, logically, I didn't have a condition
for checking for greater than, let alone equal to.
So let's enhance this a little bit.
Let me go ahead and minimally say, all right, else,
if x is not less than y, let's go ahead
and print out x is not less than y backslash n, close quote, semicolon.
So I'm at least handling that situation too.
Let me clear my terminal window.
Do make compare again.
Dot slash compare.
1 and 2 works exactly the same.
Now let me go ahead and do 2 and 1.
There we have better output.
Of course, it's not really complete yet,
because if I do dot slash compare again and do 1 and 1,
it'd be nice to be a little more specific than x is not less than y.
It's not wrong, but it's not very precise.
So I can add in to the mix what we did earlier, and I can say, OK, well,
else if x is greater than y, say x is greater than y,
else if x equals equals y, go ahead and print out x
is equal to y backslash n, close quote.
But here too, someone observed that this is sort of stupidly inefficient.
What line of code should I actually improve here to tighten this up?
Yeah?
Yeah, so line 17, I think I can just get rid of that unnecessary question,
because logically, that's going to be the case at this point.
And now I can go ahead and recompile this with make compare dot slash compare
again, enter 1 and 1, and now we're back in business,
catching all three of those scenarios there.
Questions on any of these things here?
Why have I deliberately not done this?
Let me rewind just a moment, and let me hide my terminal window
just to keep the emphasis on the code here.
Why not do this and keep my code arguably simpler?
Like, why not just ask three questions, step 9, step 13, and step 17 here?
Yeah, what don't you like?
Because then it would check each and every condition,
even though, for example, the first one might be fulfilled,
it would check the second and third, and that way for the time and time.
Exactly.
It's another example of bad design, because now, no matter what,
you were asking three questions on lines 9, 13, and 17,
even if x ends up being less than y from the get-go,
you're still wasting everyone's time by saying, well, is x greater than y?
You already might know that it's not.
Is x equal to y?
You already might know that it's not.
And so these three conditionals at the moment
are mutually exclusive, whereby you're checking all three of them,
no matter what, even though logically, that shouldn't be necessary.
So our first approach was actually quite better.
And in fact, just to show you the density difference here,
let me go back to this very first version here, whereby I was only
checking that one condition is x less than y.
Well, if you're more of a visual learner,
you can actually draw out what code looks like in flowchart form.
So here is a drawing of a program that starts here,
and ideally stops down here.
And each of these figures in the middle sort
of represent logical components of the code.
Here in the diamond here is my Boolean expression,
which represents the start of the conditional.
So if x is less than y, I have a decision to make.
Yes or no?
True or false?
Well, if it is less than y, true, well, let's go ahead and print out,
quote unquote, x is less than y, and then stop.
However, the first version of that program recall just
said nothing if it were not the case that x were less than y.
That's because false just led to the stop of the program.
There's no keyword stop.
There's just no code to handle that situation.
But the second version of the code, when I actually added in else,
looked fundamentally a little different.
So now, second version of that code asked is x less than y,
and if true, behavior is exactly the same.
But if it weren't true, it were instead false.
That's when I got the message x is not less than y.
But in the third version of the code, where I added the if, else,
if, else, if, then the picture gets a little more complicated.
And let me zoom in top to bottom.
Here we have a longer flow chart, but the questions are really the same.
When I start this program, I ask is x less than y?
If so, I print out x is less than y.
However, in that last version of the program,
I was still foolishly asking the same question, well, wait a minute,
is x greater than y?
Wait a minute, is x equal to y?
And that's the version in which, again, I had all of that unnecessary code,
which I just undeleted here, asking three questions at a time.
Ideally, I don't want to make that mistake by doing it again and again
and again, so if I instead revert that code to else, if, and else, if,
then my flow chart looks a little bit different,
because notice the sort of shortcuts now.
If x is less than y, true, we do this, and we're done, super quick.
If x is not less than y, fine, we do ask one more question.
x is greater than y?
Well, if so, boom, we make our way to the end of the program by just printing
that, only if it's the perverse case where x equals equals y.
Do we check this condition?
No.
This condition?
No.
This condition?
And then, OK, now we can print out x is equal to y,
because it must be logically.
Of course, it's been observed multiple times.
This is a waste of everyone's time, so we can prune this chart more
and just have one question, two questions,
and that alone tightens up the program.
So again, if you're more of a visual learner, most any block of code
you can translate to this sort of pictorial form,
but it really just captures the same logical flow
that the indentation and the syntax and the code itself is meant to imply.
All right, how about a final exercise with one other type here?
Recall that this is our available types to us.
Actually, two final examples here before we have a bit of a break.
Here we have a list of types that we can use,
and here we have a list of functions that we can use.
Let's go ahead and make a program that's representative of something
we do quite often nowadays, but using a different type.
So let me go back into VS Code.
Let me close compare.c.
Let me reopen my terminal window and clear it, just so we have a new prompt.
And let's go ahead and create a program called agree.c.
It's all too often nowadays that we have to agree to terms and conditions
to be fair, it's usually in the form of a pop-up and a button that we click,
but we can do this in code at the command line as well.
Let me go ahead and include to start cs50.h and include to start standard io.h.
Let me again, for today's purposes, do int main void,
but we'll reveal next week why we keep doing that.
And now, for a yes-no answer, it suffices just
to ask for a single char or character, not a whole string.
So let's do this.
char c equals get char.
And let's ask the user, quote unquote, do you agree?
Question mark, for instance.
And now, I can actually compare that value for a quality with some known answers.
For instance, I could say if c equals equals quote unquote y,
then go ahead and print out, for instance, agreed period backslash n
close quote semicolon.
Else, if c equals equals n, in quotes, let's go ahead and print out,
for instance, not agreed period backslash n semicolon.
Now, there's still room for improvement here,
but notice we're just now using the same building blocks
in C in different ways to solve different problems.
But notice, on lines 8 and 12, I've used single quotes, which I alluded to earlier.
Why is that the case?
Why single in this case here?
Yeah, it's a single character, and this is just the way you do it in C.
When you want to compare a single character,
you use char and you use single quotes.
When you want to use strings of text, like multiple characters,
multiple words, multiple sentences, or paragraphs, you use strings.
So this would seem to work, but arguably, I
could be a little more efficient if the user doesn't type y.
I mean, frankly, I could just chop off this else if and make it an else
and just assume if you don't give me a y answer, then at least I'm
going to assume the worst and you don't agree.
But even here, the program's not all that great.
Let me go ahead and do make agree, and then do dot slash agree.
And do I agree?
Sure.
I'm going to go ahead and type y.
Meanwhile, if I type anything else like n, or even emphatically no,
that would seem to, whoops, why did that not work?
Yeah.
Exactly.
So among the features of CS50's functions, like get char,
is that it will enforce what type of data you're getting.
So even though because I used get char, if the user doesn't cooperate
and types in multiple characters, get char, like some of our other functions,
is just designed to prompt them again, again and again,
until they cooperate, that's useful so that you don't have
to deal with that kind of error checking.
But here, I could type n in uppercase, and that seems to now work.
But that only works because of the else.
Let me go ahead and do this, which is very reasonable.
I'm going to go ahead and type y, capital Y, which you would hope works.
That feels like a bug at this point.
Like, it's fine if we don't want to support yes and no.
We just want to support y and n.
But it's kind of obnoxious not to support the uppercase version thereof.
So how can we fix this?
Well, let me hide my terminal window.
And I could go in and fix this as follows.
I could say, well, else if c equals equals, quote unquote, capital Y
in single quotes, and then I could do print out, agreed, period, backslash,
n, semicolon, and then I can do else, that would work.
That would work there.
But what rubs you the wrong way, perhaps, about this solution,
even if you've never programmed before?
Just applying some of the lessons from last week.
Yeah.
It's redundant.
I mean, I didn't technically copy and paste,
but line 14 is identical to line 10.
So I might as well have copied and pasted.
And that's generally bad practice.
Why?
Well, if I want to change the English language to say something else in that case,
now I have to change it twice.
And it's just I'm repeating myself, which is just bad design.
So there are ways to address this through other types of operators
that we haven't yet seen.
If I want to ask two questions at once, that's fine.
I can do something like this.
Well, if c equals equals, quote unquote, y,
or c equals equals, quote unquote, capital y,
I can tighten things up using so-called logical operators,
whereby I am now taking a Boolean expression
and composing it from two smaller Boolean expressions.
And I care about the answer to one of those questions being true.
So whether it's lowercase y or uppercase y, this code now will work.
And if it's anything else, we're going to default to not agree.
So the two vertical bars, which is probably not a character you type that
often, and it varies where it is on your keyboard,
depending whether it's American English or something else,
just means logical or.
This is not relevant here.
But you could also, in some context, use two ampersands to connote and.
But this does not make sense why.
Why is it clearly not correct to say and in between these two clauses?
Yeah?
Yeah?
Yeah, because it's a lowercase y.
Exactly.
The variable can't both be lowercase and uppercase.
That just makes no sense.
So this would be a bug, but using two vertical bars here is, in fact, correct.
All right, well, let's do one final flourish here besides conditionals.
We had these now loops.
Recall that a loop is just something that does something again and again and again.
Here, for instance, is scratch how we might meow three times.
In C, there's going to be a few different ways to do this.
Here is one.
You can in C declare a variable like I for integer or whatever you want to call it
and set it equal to three, the number you care about.
You can then use a loop.
And the closest to the repeat block is arguably a while loop.
There is no repeat keyword in C. So we can't translate this verbatim.
But we could say while i is greater than 0.
Why?
Because that's sort of logically what I want to do.
If I start counting at 3, maybe I can just sort of decrement one at a time
and get down to 0 at which point I can stop doing this thing.
So I'm going to initialize a variable to i, a variable i to 3.
And then I'm going to say while i is greater than 0, go ahead and do the following.
And at the end of that loop, before whipping around again,
I'm going to use this line of code, which we haven't seen, but you can infer.
i minus minus just means subtract 1 from i.
So this is going to have the effect of starting at 3, going to 2, going to 1,
going to 0.
And as soon as it goes to 0, this Boolean expression will no longer be true.
And so the loop will just implicitly stop because that's it.
So what are we going to put inside of the curly braces besides this decrementation?
Well, I think I can get away with just saying meow.
And that will now print 1, 2, 3 times.
And yet that's interesting.
I sort of counted instinctively 1, 2, 3, even though I'm proposing that we count 3, 2, 1.
So can we implement the logic in the other direction,
whereby we count up from 0 instead of down from 3?
Well, sure.
We just have to make a few changes.
We can set i equal to 0 initially.
We can change our Boolean expression to check that i is less than 3 again, again, and again.
And on each iteration of this loop, let's just keep incrementing i with i plus plus.
And at this point, it will have the effect of doing 1, 2, 3.
3 is not less than 3, so I won't put any more fingers up.
I will meow in total 3 total times.
And again, if you're a visual person, here's
how we might start counting at 0 initially.
Check that i is less than 3, which it is initially.
And if so, we print out meow.
Then we increment i, and we get whisked around again to the Boolean expression,
because that's how while loops work.
You constantly have the condition being checked again and again.
That's just how C works.
As soon as I've incremented i from 0 to 1 to 2 to 3,
3 will eventually not be less than 3, so the answer will be false,
so the loop will just stop.
So that has the effect of achieving the same.
But it turns out that looping, some amount of times,
is so darn common that you don't strictly have to use a while loop.
A for loop, so to speak, is another alternative there, too, whereby
the syntax is a little weird.
It's a little harder to memorize, but it allows
you to write slightly less code because you write more code on a single line.
So the way you read a for loop is exactly the same in spirit.
You initialize the variable, everything
to the left of this first semicolon.
Then check the condition, and the computer does all this for you.
If i less than 3, if so, you execute what's inside of the curly braces.
And then automatically, the thing to the right of the second semicolon
happens, so i gets incremented from 0 to 1 in this case.
The condition is checked is 1 less than 3.
It is, so we print meow again, and c increments i to 2.
Is 2 less than 3?
Yes, so we meow again.
i gets incremented to 3.
Is 3 less than 3?
No, so the for loop stops.
So it's exactly the same, but just more magic
is happening in this first line of code here.
More than you yourselves have to actually write,
and it's just arguably more common convention,
but both of them are perfectly correct if you'd like to do that yourself.
So let's go ahead and actually implement now this beginning of a cat in VS Code.
Let me go back to VS Code and close agree.c.
Let me reopen my terminal window and create a actual cat in cat.c.
And let's go ahead and do this initially the wrong way.
Include standard io.h int main void.
And then inside of main, let's go ahead and print out quote unquote meow
backslash n semicolon.
And then heck, let me just copy paste.
So this is obviously the wrong way, the bad way to do this,
because I'm literally copying and pasting.
But it is correct if I want the cat to meow three times.
I can make this cat.
I can do dot slash cat.
And I get my meow, meow, meow.
But let's now actually use some of those new building blocks,
whereby we converted scratch to C. And let me go back into this code.
And I'll do the while loop first.
So I could instead have done int i equals 3, if we count down initially.
While i is greater than 0, then go ahead and print out quote unquote meow
backslash n.
And then do i plus plus or i minus minus?
i minus minus, because we're starting at 3.
Now let me go back to my terminal window and clear it.
Do make cat again dot slash cat.
And we get three meows.
And this is now arguably better implemented.
What if I want to flip things around?
Well, I could now change maybe do the normal person way.
I could start counting at 0.
And I can do this so long as i is less than 3.
And I can do this so long as I increment i on each iteration.
Now I can do make cat again dot slash cat, enter.
And that too works.
But there's another way I could do this.
If I want to count like a normal person, like start counting from 1
and count up 2 and through 3, I could do this.
But this is arguably, this is correct.
It would iterate three times, but it's a little confusing,
because now I have to think about what it means to be less than 4.
OK, that means equal to 3.
I could be a little more explicit and say, well, do this while i is less than
or equal to 3 using yet another one of those operators.
So I can make a cat yet again dot slash cat.
And that too would work.
Now which of these is correct or best?
The convention truthfully is, in general and code,
to start counting from 0, start counting up 2,
but not through the value that you want.
So at least you see the starting point and the ending point on the screen,
if you will, at the same time.
But of course, I can condense all of this a bit more
and turn this whole thing into a for loop.
And I instead could do for int i equals 0, i less than 3, i plus plus.
And then down here, I could do print out quote unquote meow.
And if only because I typed fewer keystrokes that time,
like this feels a little nicer, it's a little tighter and more efficient
to create even though the effect is the same.
Indeed, when I make this cat and do dot slash cat a final time,
this here too gives me the three meows.
So what could go wrong?
Well, sometimes you might be inclined to do something forever.
And we might have done that in Scratch.
And indeed, we did when we had some things bouncing back and forth
off of walls and so forth.
You can achieve the same thing in code.
In fact, in C, we could use a while loop.
But there is no forever block.
So while suffices.
But recall that the while loop expects a Boolean expression.
And if I want to do something forever,
I essentially need an expression here that's always true.
So I could do something stupid and arbitrary,
like while 2 is greater than 3 or while 1 is less than 2.
I mean, make a statement of fact that never changes.
Ergo, it's just going to run forever.
But if the whole goal here is to do something forever
and to get this Boolean expression to be true,
the convention in programming is just to literally say while true.
And that implies and functionally means that you will do this thing forever
unless you somehow prematurely break out of those curly braces.
More on that before long.
So if I want to meow forever, I could now just do this.
And this would be an infinite deliberate loop.
But unlike a game where you might want it to keep going and going
and going for some time, I'm not sure this is going to be the best thing for us.
Let's go ahead and try this.
So let me go ahead here and include for good measure CS50's library,
if only because it too is giving us features like Bools.
Here I'm going to go ahead and say while true.
And then inside of my curly braces, I'm just going to print out meow.
Let's go ahead, backslash n, semicolon.
Let's go ahead here and make cat one final time.
Let me go ahead here and do dot slash cat and this is like the annoying cat game,
just like meowing, meowing, meowing endlessly.
Like I've now kind of lost control over my terminal window.
And mark my words.
At some point, you might do this too.
But let's go ahead and take a juicy 10 minute break here.
We have some delicious blueberry muffins out in the transept come back in 10.
And we'll figure out how to stop this here, cat.
All right, so it's been about 10 minutes and like VS code
is freaking out with high code space CPU utilization detected.
Consider stopping some processes for the best experience.
So this is what happens when you have intentionally or otherwise an infinite
loop in so far as I've been printing out meow endlessly.
And I was warned by my colleague that I probably shouldn't let this run too long
because we might lose control over the environment altogether.
But the answer to how to solve this is going to be control C.
So there's a few cryptic keystrokes that you
can use to generally interrupt things as in this way.
And in fact, if I go back and you'll see I kind of lost control over my code space
here, I'm going to go ahead and try to reload the window altogether.
But had I hit control C in time, let's hope this doesn't now go off the rails.
Control C would have been our friend.
There we go, and we're back.
OK, so now that we've got control over our so-called code space again,
how can we go about making our meowing program a little more dynamic?
And so far as let's start asking the user how many times they want the cat
to meow, certainly rather than do it an infinite number of times
and even rather than do it three times alone.
I think we have all of these building blocks so far.
So let me go ahead and stay in cat.c here and go ahead and delete
the body of the contents of my main function.
And let's go ahead and do this.
Let's give myself an int, and I'll go ahead and call it n for number,
though I could be more verbose than that if I wanted.
I'm going to set it equal to the so-called return value of get int,
which recall is going to get an integer from the user.
And quote unquote, let's ask the user, what's n?
Just like I asked earlier, what's x and what's y,
where n is the number of times I want the cat to meow.
Now, how can I use this variable?
Well, we have that building block too.
I could use a while loop or a for loop.
And if I use a for loop, I could do this.
I could initialize a variable i for integer, set it equal to 0 initially.
I could then do i less than not 3 this time, but n.
So I can use that variable as a placeholder inside of the loop
to indicate that I want to do this n times instead of 3.
And on each iteration through this loop, I can do i plus plus.
Of course, I could be counting down if I prefer by using documentation.
But logically, I would say this is canonical.
Start at 0 and go up to, but not through, the value that you actually care about.
And I'll go ahead now and print out, quote, unquote, meow with a backslash n
semicolon back down to my terminal.
Make this cat again dot slash cat enter.
I'm prompted this time for n.
I can still give it 3.
And I'm going to get 3 meows this time.
However, if I run it again with dot slash cat and a different input,
like 4, of course, I'm going to get 4 meows instead.
Now, what is get in doing for me?
Well, it does a few things similar to get char doing a few things for me.
For instance, suppose that instead of answering this question correctly
with a number n, I say something random like dog.
That is not an integer.
And so the get in function is designed to reject the user's input implicitly
and just re-prompt again, again, and again.
I can try bird, and it's going to do this again.
So somewhere in the implementation of get in,
there's a loop that we wrote that does this kind of error checking for you.
But it doesn't do everything because an integer is
a fairly broad category of numbers.
It's like negative infinity through positive infinity,
and that's a lot of possibilities.
But suppose I don't want some of those possibilities.
Suppose that it makes no sense to ask the cat to meow like negative one time,
and yet the program accepts that.
It doesn't do anything or anything wrong,
but I feel like a better design program would say, no, no, no.
Negative one makes no sense.
Let's meow 0 or 1 or 2 or more times instead.
So how can I begin to add some of my own error checking
and coerce the user to give me the type of input I want?
Well, let me clear my terminal window and go back up into my code.
And why don't I do something like this?
After getting n, let's just check if n is less than 0,
because if so, I want to prompt the user again.
And I can prompt the user again by doing n equals get
int quote unquote what's n?
Question mark, semicolon.
Now, what's going on here?
Well, on line six, I'm doing two things.
I'm getting an integer from the user, and I'm not only storing it
in the variable n, I'm also technically creating the variable n.
So I didn't call this out earlier, but on line six,
when you specify the type of a variable and the name of the variable,
you are creating the variable somewhere in the computer's memory.
And that's necessary in C to specify the type.
If the variable already exists, though, and you just
want to reuse it and change it later on,
it suffices as in line nine just to reference it by name.
It would be sort of stupid to specify the type again,
because C already knows what type it is, because you told
C what it is on line six.
So that's why line six and nine are a little bit different.
So let's see how this now works.
Let me go back to my terminal window and remake this cat.
Let me do dot slash cat again.
Let me not cooperate and type in negative one again.
And notice I am reprompted this time.
Fine, fine, fine.
Let's type in three, and now it works.
But you can perhaps logically see where this is going.
Let me go ahead and run this again, dot slash cat, type in negative one,
type in negative one, and, huh, it didn't prompt me again.
But that's consistent with the code.
If I hide my terminal window here, you'll
notice that I've got one, maybe two tries to get this question right.
And after that, there's no more prompting of me.
Now, you can kind of imagine that this is probably not the best way to do this
if I were to go inside of line nine and then move the cursor down and say,
OK, well, if n still is less than 0, well, let's just do getInt again
and ask what's n question mark.
And heck, OK, if it's still less than 0, well,
let's just keep asking the same.
Why is this bad?
I'm repeating myself.
I'm essentially copying and pasting, even though I'm retyping.
I mean, this just never ends.
Like, how many chances are you going to give the user?
In spirit, you'd hope that they don't not cooperate this many times.
But really, to do this the right way, we should probably prompt them
potentially as many times as it takes to get the correct input.
So this is not the right path for us to be going down.
But of course, we have already now this notion of a loop,
whereby we could just do this in a loop, ask the question once,
and maybe just repeat the question again, again, but the same question.
So how might I do this?
Well, let me go ahead and delete all of this.
And let me just try to spell this out logically.
So I want to get a variable n from the user.
And let's go ahead as follows.
While true, I know how to do infinite loops now.
And even though that created a problem for me with the cat,
I bet we can sort of terminate the loop prematurely,
like I proposed earlier, as follows.
I could do this int n equals get int and ask the user again,
what's n question mark?
And then I could do something like this.
If n is less than 0, well, then you know what?
Go ahead and just continue on with the same loop.
Else, if it is not the case that n is less than 0, what do I want to do?
I want to break out of this loop.
So this is new syntax.
This is something you can do and see, whereby if n is less than 0, fine.
Continue means go back to the start of the loop
and do the same exact thing again.
Otherwise, if you instead say break, it means break out of the loop
and go to below whatever curly brace is associated with that loop.
So continue essentially brings you to the top.
Break brings you to the bottom, if you will.
So logically, I think this is right.
But this code, curiously, isn't quite going to work and get me a value for n.
Let me go ahead and open my terminal window again.
Let's make this cat.
And, huh, cat.c line 19, character 25 is an error,
use of undeclared identifier n.
Well, what does that mean?
Again, cat.c line 19, let me hide my terminal window, highlight line 19.
n is being used in line 19.
But I created it in line 8.
And so what's the problem?
Why is it not declared, seemingly?
Yeah?
Because you are using n within the loop that you wrote.
Yeah, this is a subtlety, but I'm using.
I'm creating n inside of this loop.
I mean, literally between the curly braces on line 7 and 17.
The implication of which, because of how c works,
is that that variable only exists inside of that for loop.
This is a problem of what's known as scope.
The variable n only exists inside of the scope of the while loop in which it was declared.
So how do I actually fix this?
Well, I need to logically somehow declare that variable n outside of the loop
so that it exists later on in the program as well.
And there's a few different ways I can fix this.
But the best way is probably to move the declaration of n, so to speak,
the creation of n outside of the curly braces and maybe kind of squeeze it
in here, below line 5, so still inside of main, whatever that is,
more on that next week, but in the same curly braces as everything else.
So I can, in fact, do this.
And this is where the syntax gets a little bit different.
I can solve this quite simply as follows.
I can go down to a new line 6 and just say int n semicolon.
And that's it.
This declares a variable called n.
It creates a variable called n.
And initially, it doesn't give it any value.
So who knows what's in there, more on that another time.
But now on line 9, I don't need to recreate it.
I just need to assign it a value.
And because now n has been declared, on line 6,
and between the curly braces, on line 5 and all the way down on 24,
now n is in scope, so to speak, for the entirety of this code that I've written.
So let me reopen my terminal window and clear that old error.
Let me do make cat again.
Now the error message is gone.
Let me go ahead and do dot slash cat.
What's n?
Now I'm back in business, and I can do 3 for meow meow meow.
But better yet, because I'm inside of a loop now,
watch that I can do negative 1, negative 1, negative 1, negative 1,
negative 2, negative 3, negative 50.
Finally, I can cooperate with something like 3.
And because I'm in a loop, that by design may very well go infinitely
many times until the user actually cooperates
and lets me break out of that exact loop.
Now, strictly speaking, don't need both continue and break.
I wanted to demonstrate that both exist, but this is like twice as much code
than I actually need.
If logically, I just want to break out of this loop
if and only if n is greater than or equal to 0,
because I'm sort of comfortable with the idea of 0,
meow, but negative makes no sense, well, I can just flip the logic.
I can say if n is greater than or equal to 0, then go ahead and break.
And I've tightened up the code further.
I could technically do something else.
I could say something like if n is less than 0, but wait a minute,
I wanted to gate that.
You can start to do tricks like this, an exclamation point
with some additional parentheses so you can invert the logic.
It's already going to be a little hard to read,
even though that would be logically correct.
I'm just going to say more explicitly as before,
if n is greater than or equal to 0, break out of this here loop.
So this is one way to use an infinite loop,
but it turns out there's another construct that you can do altogether
that is in a feature of C. Instead of using a while
loop and forcing it to be infinite by using while true
and then eventually manually breaking out of it,
there exists another type of loop altogether, and that's called a do
while loop.
And you can literally say the word do, which means do the following,
then you can do exactly what we did before, n equals get end quote unquote
what's end question mark.
So exactly like before, but then after those curly braces,
you use a while keyword.
So at the end of the loop instead of the beginning,
and that's where you put your Boolean expression.
I want to do all of that while n is less than 0.
So you can kind of invert the logic and now kind of tighten things up
further by just telling the computer, do the following.
What's the following?
Everything in between those curly braces, while n is less than 0.
And this implicitly handles all of the continuation and all of the breaking
by just doing what you've said.
Do this while this is true.
But the difference between this do while loop and a normal while loop
is literally that the condition is checked at the bottom instead of the top.
So when you say while parentheses something, that question is asked first
and then you proceed maybe.
This condition is only asked at the very end.
And why is this useful?
Well, oftentimes when writing programs where you want to do something at least
once, like you obviously want to ask the user this question at least once,
there's no point in asking a question like while true or while anything else.
You should just do it.
And then you should do it again if the expression evaluates to true
and tells you to do something.
Now, you haven't played with these loops yet, most likely,
unless you have programmed before.
There's a fun sort of meme that's apropos of this moment.
So let's see if this maybe causes a few chuckles.
If you remember Looney Tunes here, this is funny for people in the know.
There we go.
Thank you.
OK, this doesn't make sense.
It eventually will.
And it still might not be funny, but it will at least make sense.
And it illustrates the difference between do while loop,
like the road runner is stopping because he's checking the condition.
While not on edge, he'll run.
But if he is on the edge, he's not going to proceed further.
But of course, the coyote here, he's going to do running no matter what,
and then only too late.
Does he check?
Ha ha, he's still on the edge.
All right, so ah, thank you.
All right, now you're cool.
All right, so many more memes will now make sense as a result.
But let's go ahead and revisit this code and maybe do something a little bit
different here, whereby we no longer want to just
futz around with some of these conditionals and these loops.
Let's actually make the software a little better designed.
And to do this, we'll revisit an idea that we touched on last week
and had to do with problem set 0, which was create your own function.
Like C does not come with everything you might want.
CS50 library is not going to come with everything you might want.
And at the end of the day, a lot of programming
is about abstracting away your ideas so you solve a problem once,
and then reuse it, reuse it, reuse it.
And heck, you can package it up in a so-called library like we have
and let other people use it as well.
So here, for instance, in Scratch is how we could have implemented
the notion of meowing as by getting the cat to play the sound meow until done.
We abstracted it away, and then we had a magical new puzzle piece called meow.
In C, this is going to be a little weird today,
but next week these details will start to make more sense.
You would instead do the following.
Literally type void, the name of the function you want to create,
and then void again in parentheses.
For now, know that this is the return value of the function.
So void means it returns nothing.
This is the input to or the arguments to the function.
Void means it takes no inputs.
And that makes sense, because literally meow doesn't return anything.
It doesn't take anything.
It just meows.
It has a so-called side effect, audibly last week.
So this means, hey, see, invent a function called meow that takes no input,
produces no output, but does have a side effect of printing meow on the screen.
Meanwhile, if I wanted to do something like this in code last week where
I meowed three times, well, that's fine.
We have the building blocks for this.
And here's where inventing your own function starts to get more compelling.
I can abstract away the notion of meowing now.
Like, this doesn't come with C. It doesn't come with the CS50 library.
I just created in the previous code this meow function.
So I can encode with a for loop and that new function meow three times.
But I can abstract this away further.
We've called that the refinement in Scratch last time was this.
I could edit the new function, and I can say it actually does take an input,
otherwise known as an argument, called n.
And I clarified that this means to meow some number of times.
And then inside of those Scratch blocks, I repeated n times the meowing act.
Well, in C, I can achieve the exact same thing,
even though it's going to look a little more cryptic,
but meow still returns nothing.
It has an audible or visual side effect, but it doesn't return a value.
But this version does take an input.
And this might look a little weird, but just like before,
when you create a variable in C, you specify the type and the name.
When you invent your own function in C and it takes one or more inputs,
a.k.a. arguments, you specify the type and the name of those as well.
No semicolons up there just inside of the parentheses.
And you'll get used to with practice this convention.
But the rest of this code is exactly the same,
except instead of three, I'm now using n.
So again, I'm just composing the exact same ideas as last week,
even though it looks way more cryptic this week,
but it will come more and more familiar with more and more practice.
So how can I go about implementing this myself?
Well, let me propose that we do something like this.
Let me go back to VS Code here and let me go ahead and let's really
delete most of the code that I've written inside of main.
And let me just suppose for the moment that meowing exists.
And I'm going to go ahead and say, for the first version, for int i equals 0,
i less than 3.
So we're not going to take input yet, i plus plus.
And then I'm going to go ahead here and say, meow is what I want this function to do.
Now, if I scroll back up, you'll see there's no definition of meow yet.
So I'm going to invent that too.
I'm going to go up here and say void meow void.
And again, this first version means no input, no output, just a side effect.
And that side effect super simply is going to be to say just quote unquote meow
with the backslash n.
And now, if I go and open my terminal window, clear it from before,
do make cat, so far so good, dot slash cat, we're back in business.
But I've abstracted the function away.
Now, much like last week where I sort of dramatically
dragged the meow definition way down to the bottom of the screen
just to make the point that you don't need to see it anymore, out of sight,
out of mind, let me sort of try to do the same here.
Let me highlight and delete that and go way, way, way down arbitrarily
just to be dramatic and paste it near the 100th line of code and scroll back up.
Now, out of sight, out of mind, I've already
implemented the idea of meowing.
We don't need to see or talk about it again.
But there is a caveat in C. When I now clear my terminal and make this cat,
now I've introduced a problem.
And there's more problems, it seems, than code.
Let me scroll back up to the first such error and you'll see this.
Online nine of cat.c, character nine, there's an error.
Call to undeclared function meow and then something fairly arcane.
But that means that meow is no longer recognized as an actual function.
I know that it doesn't come from CS50.h and I know it doesn't come from standard.io.h.
It's just down there.
But why is the compiler being kind of dumb here?
Yeah?
Yeah, because insofar as the first version worked, logically,
it would seem that putting it at the bottom was just a bad idea
because C compilers are fairly simplistic.
Like they won't proactively do you the favor of checking all the way down
at the bottom of the file, they're going to take you literally.
So if meow doesn't exist as of line nine, that's on you.
Like that is an error.
So I could fix this by just undoing what I did and move it way back up to the top.
But let me argue that, in general, when writing C programs,
the main function, which I keep using and we'll talk more about next week,
is literally meant to be the main part of your code.
And so it kind of stands to reason that it should be at the top.
Because when you open the file, it'd be nice to see the main program
that you care about, the main function.
So there's an argument to be made that it's a little annoying to have
to put my functions all at the top, which is just
going to push main further and further down.
So there is a solution.
And this is, dare say, the only time copying and pasting is appropriate.
Let me delete most of these blank lines, which is unnecessarily dramatic.
And just move it below main as over here.
The way I can, the solution here, though, is to do this.
To copy the first line of the main function, it's so-called signature.
And then just put that one line and only that one line with a semicolon above main.
And this is what's known as a prototype.
So a prototype is just a bit of a hint to the compiler, a promise, if you will,
that, hey, compiler, there will exist a function called meow.
It takes no input and it returns no output semicolon.
And it's on the honor system that it will eventually exist later in the file.
We'll talk more about this next week why that works,
but this is sort of a promise to the compiler
that it will eventually be defined.
Now, what I've done here on line four as an aside is what's generally known as a comment.
I just wanted to put on the screen exactly what I was verbalizing.
Anything in C that starts with slash slash
is a note to self, like a sticky note in scratch, which is just for the human,
not for the computer.
And it's a way of reminding yourself or someone else what's going on on that line
or those lines of code.
But I'll go ahead and delete it for now is unnecessary,
because now if I go back into my terminal and clear those errors,
make this cat again, now it does work because the meow function has been
defined exactly where it should be.
And now I can make the new version of this cat even better.
I could change the function meow to take a variable n as input for the number of times.
And then in here, I could do something like my for loop for int i equals 0,
i less than n, i plus plus.
And then in this for loop, I can print out quote unquote meow.
And then I'm going to have to change this too,
because I have to copy and repaste it, if you will, or just manually fix that.
But now I can get rid of all of this and do meow 3, for instance.
And this now will be the second version of the scratch code, if you will.
Make cat still going to work exactly the same meow meow meow.
But now I've implemented my own function that does take input,
even though it doesn't happen to return any output.
All right, questions on any of these examples just yet?
Confusion.
All right, let me add one other feature to this
to demonstrate that we can take not only input, but actually produce output if we want.
If I go back into this code here, let me
propose that it's a little silly to be hard coding that is fixating 3.
It'd be nice to get input from the user.
So I could do this.
I could use int n equals get int and say something like, what's n?
Question mark.
And then I could pass n in, if only to demonstrate a couple of things.
So one, now the program's dynamic.
I'm going to ask the user how many times to meow.
And I'm going to pass in that value n.
Now this deliberately is confusing at the moment, because wait a minute.
I've got n defined here, used here, but then redefined here, and then reused here.
So it turns out that even if you create n up here and use the name n,
no other functions can see it for that same issue of scope.
So for instance, suppose I didn't quite remember this,
and I sort of naively just said void, meow doesn't need to take any inputs,
because heck, n is already defined in main.
Let me go ahead and open my terminal and clear it, make cat,
and see what error comes out here.
Well, error cat dot, oh, sorry, I made two mistakes here.
I also have to change the prototype up here to say void, which means,
again, meow takes no inputs.
Let me go ahead now and rerun, make cat.
And there we have an undeclared identifier, again, n.
So in cat.cline 14, which is here, it doesn't like that I'm using n.
But wait a minute, I created n here, but for the same logic as earlier,
that's fine.
You created n on line 8, but where does n
exist in what scope?
Yeah, only between the curly braces, which is lines 7 and 10.
So by the time you get down to 14, it's out of scope, so to speak,
so it just doesn't work.
So the solution is exactly what I did the first time.
I can pass it into meow as input, and I have to tell c to expect that input.
And I can use the same name.
But arguably, that's going to get confusing sometimes.
But let me do this.
Let me go back into my code.
Let me undo this change such that now meow does take an input.
But instead of just calling it n and using n everywhere for number,
this is crazy, let's just call this like times.
So meow takes some number of times, and then it uses that value.
And now I'm passing in on line 9, n.
But in the context of the meow function on lines 12 onward,
that same variable, n, is now referred to as times,
because you're passing it in as input and giving it its own name.
And that's totally your prerogative.
It's just a matter of scope.
I mean, I could have called it m or some other letter of the alphabet,
but times is even more clear, because that's the number of times
I want the cat to meow.
But again, the whole point here is just this matter of scope.
All right, so let's take a higher level look now at some of the things
we've been thinking about, and then we'll do a final deep dive or two
on some of the problems that we can solve with all of these building blocks
and some of the problems that we're sort of ignoring for now.
So when it comes to writing good code, CS50 and really the world in general,
tends to focus on these kinds of axes, correctness, design, and style.
What does this mean?
Correctness just means, does the code work the way it's supposed to?
In the context of a class, it should do exactly what the homework assignment,
aka problem set, tells you to do.
In the real world, it should do exactly what someone decided the software should
do, the product manager, the CEO, or the like.
Correctness just means it behaves as it should.
That's different, though, from how well designed the code might be.
And we've seen that a few times.
I've had some simplistic examples in Scratch and C
that were 100% correct.
Like, it did the right thing logically, but I was wasting the computer's time.
I was wasting the human's time by asking more Boolean expressions
than I needed to and so forth.
So design is more about, like, in the world of English,
like, not only saying things that are correct, but doing it well,
like, in making a good cogent argument, not just one that happens to be correct.
Style, meanwhile, is the third axis on which we might evaluate the quality
of someone's code, and that's more of the aesthetics.
Like, is everything pretty printed?
That is nicely indented.
Are variables well-named and not just called x, y, z arbitrarily or something
like that?
So style matters really to other humans, not to the computer, but to other humans.
And to illustrate these, you'll see that in problems set one onward,
you'll be given a number of tools that you can use.
So one of those tools is called check50.
And in each problem set, problem in C and Python and other languages,
you'll be showed how you can test your own code.
And you can literally run a command that CS50 created called check50.
You'll then specify what's called a slug, which just means a unique identifier
for that homework problem.
And you'll get quick feedback on whether or not your code is correct.
It doesn't mean it's well-implemented or well-designed
or pretty that is well-stylized, but at least that's the first gauntlet
in getting good code submitted.
Design, though, is much more subjective.
Design is something you get feedback on from a human, for instance,
in section or a teaching assistant, or in software.
You can actually see atop VS Code, there's
a couple of buttons that I haven't yet used, but could.
Design50 is built on top of the CS50 duck, whereby if you have a program
open in a tab, you click Design50, you will
get chat GPT-like advice on how you can improve not the correctness
of that code, but the design of that code, the quality thereof,
which is a bit more subjective and modeled after what
a good teaching assistant might say.
Style50, meanwhile, is a third tool that will provide you with feedback
on the style of your code.
It will show you on the left what your code looks like,
and on the right what your code really should look like in so far as
that you'd be consistent with what we've taught in class
and consistent with CS50's so-called style guy.
And those of you who have some prior programming experience
undoubtedly won't like some of CS50's stylistic choices,
and that's going to be the case in the real world, too.
But as I alluded to earlier in typical companies,
you would have an official style guide or tool to which everyone adheres
so that everyone's code actually looks the same as everyone else's,
even though people have contributed different solutions to problems.
So correctness, design style is not only how we, but really the world writ
large tends to evaluate the quality of code,
and we do it by way of these CS50 specific tools here.
All right, how about one final flourish then to this here program?
Back in VS Code, I've got a correct solution right now.
It's well-styled, I'll stipulate, even though it
could stand to have some more comments.
So for instance, I could do something like this,
like meow some number of times, a comment to myself,
or up here I could say something like get a number from user.
Just to remind myself and my TA or my colleague what it is this code is doing,
but what more could I do in the way of design?
Well, this function here, get int, will indeed get me an integer,
but not just positive or zero, but negative.
And I could go in and add a bunch of code like before,
like I could actually do.
Instead of this line, I could do something like int n semicolon,
do the following, or a n equals get int.
And then I can say, what's n question mark?
And then after that, I can do something like while n is less than zero,
keep doing that.
So I can have a pretty verbose implementation of getting user input.
Or I can implement another function of my own
that only gets a positive integer or a non-negative integer from the user.
For instance, I might do something like this.
I could declare at the bottom, maybe below my main function,
a function like this int, how about get n.
And then inside of this, I might say void,
because I'm not going to pass in any inputs.
Then inside of this function is where I'm going to do int n,
do while n equals get int, quote unquote, what's n question mark.
And then down here, I'm going to do while n is less than zero.
But rather than do something immediately with n,
because I'm no longer inside of my so-called main function,
what I'm going to do, which is new, is return this value n.
And notice that this notion of returning a value,
which is the first time I've done this explicitly,
is consistent with this little hint here on line 19, which
implies that this get n function, which I'm inventing,
is going to return, not void, which means nothing, but an integer.
And that's the whole purpose of this function in life now.
If I scroll back down here, I can get rid of this whole block of code
and just say get n from the user.
And then I can immediately call meow with that value.
I need to do one other thing.
I need to highlight this line of code here.
And I'm going to go ahead and add another prototype up top, which
is the only time again for now that copy, paste is encouraged, and best to do.
So I've invented my own function, get n, the whole point being now
I have this sort of abstraction here of a function whose whole sole purpose
in life is to get me not just an integer, but one that is zero or positive and not negative.
If I open my terminal window, clear the mess from before,
make this cat dot slash cat what's n3, I'm now back in business.
And again, we've essentially translated from scratch last time
into see this time exactly how we might modularize now the code,
abstract away these lower level details, and ultimately create my own function
that, as before, takes not only arguments, but in this case
has not only side effects, so it doesn't have side effects,
but rather a return value this time.
All right, so as you walked in, we had a little
walkthrough of Super Mario Brothers playing from yesteryear, which
was a side scrolling game in which Mario would jump down and go up down
left to right and try to collect coins and make it to the end of the level.
There's a lot of obstacles throughout this kind of game,
whereby the world might look a little something like this,
like there's a pit that Mario's got to jump over,
and then there's these coins hidden typically behind these question
marks that he can jump up and hit his head with and actually accrue points.
Now, we're not going to do anything graphical just yet.
We're leaving graphics behind for now in the form of scratch.
But with C, we can implement some of these ideas.
For instance, if I were to write code to generate
just this row of four question marks, I dare
say there's a bunch of ways we can do this.
In other words, let's see if we can't use all of today's building blocks
to start implementing our own tiny version of Super Mario Brothers
in a file, say, called Mario.c.
So let me open and clear my terminal window.
Let me run code Mario.c, and let's just try to do something super simple
like print four question marks in a row.
Well, to do this, I need printf, so I'm going to include standardio.h.
I'm then going to do int main void, more on that next time.
And inside of main, my default function that just automatically,
as before, gets called for me, I'm going to print out
the simplest possible implementation.
Just print out four question marks like that.
So no need per se for a loop just yet.
But I think we can go down that rabbit hole too.
Let me go down into my terminal window, make this version of Mario,
dot slash Mario, enter, and voila.
We have a very black and white version, textual version
of four question marks in the sky.
Now, I'm kind of cheating here by just hard coding four question marks.
What if I wanted not four, but three, or five, or some number, other number?
Well, we could do that with a loop too.
So let me change this code here and do something like this.
For int i equals, say, 0, i less than, say, 4 for now, i plus plus.
Then inside of this loop, I can print out one question mark at a time, semicolon.
Now, let me go back to the bottom, make this version of Mario, dot slash Mario,
enter, and voila.
It's not actually correct this time.
So why am I getting a column instead of a row with this here change?
Yeah?
Yeah?
So you give the backslash n after the question mark.
Yeah, so I foolishly included the backslash n after each question mark.
OK, so that seems like an easy fix.
Let me get rid of that.
Let me now recompile Mario, rerun Mario, and now so close.
Now I've just done something stupid.
All right, I need the backslash n.
So I think I do want this here.
Or what do you propose instead?
I think I should really put the backslash n outside of the loop.
So once I'm done printing all of the question marks, then I get the backslash n.
And that's fine, even though we haven't seen this before.
Backslash n is an escape sequence that you can certainly print by itself.
So I do quote unquote, backslash n outside of the loop,
below those curly braces.
Now if I do make Mario, dot slash Mario,
now I get the four question marks in a row, as well as the new line at the very end.
So again, kind of a little baby exercise, but demonstrative of how
you can just take a few different techniques, a few different building blocks
we've used to compose a correct solution to what a moment ago was a brand new problem.
Well, let's try another.
So later on in Super Mario Brothers, when you go into the underground world,
you see, or rather it's still above ground,
you see a column of bricks like this that he has to jump over.
So those here.
How might we make a column?
Well, we kind of had that solution already.
And in fact, if I go back to VS Code here and just change this version of Mario,
I think we can design this thing to be pretty simply the same.
I is less than 3, though.
And I do want to put the backslash n at the end there.
Make Mario, dot slash Mario.
And albeit textual, I've got my column of three, of, let's see.
I don't want question marks.
Let's make this a little better.
Maybe we'll use the hash symbol because that kind of sort of looks like a square.
So make Mario, dot slash Mario.
OK, now we're back in business.
But let's make it more interesting by going into Mario's underground now.
And here's the third and final Mario problem, whereby
we want to implement like this 3 by 3 grid of bricks circled here.
So this one's interesting because we've never done something in two dimensions.
I did horizontal.
I did vertical.
But we haven't really composed those ideas into the same.
So let me now think a little harder this time about how I can print out row, row,
and this is where if you have in your mind's eye any familiarity with old school
typewriters, it's kind of the same idea where you want to print a row of bricks,
then go back to the beginning, a row of bricks, then go back to the beginning,
and a row of bricks.
And that's kind of what printf has always been doing for us.
It's printing line by line by line of text.
It's not jumping around.
So we can leverage that perhaps as follows.
Let me go into my main function here.
And if I want to print out something two dimensional,
let me kind of think about it as rows and columns.
So maybe I could do this for int i equals 0 i less than 3 i plus plus.
Why?
Well, I want to do something three times.
Even if I have no idea where I'm going with this solution,
I at least want to do something three times, like three rows of text.
But how about this?
On each row, what do I want to do?
I want to print out three things.
So I could steal this idea, like int i equals 0 i less than 3 i plus plus.
And then inside of this loop, let me just print out one brick at a time.
No new lines yet, one brick at a time.
But there is a bit of a problem here.
This is correct to nest loops in this way.
Totally fine to have an outer loop, totally fine to have an inner loop.
But I probably don't want the inner loops variable competing with the outer loops
variable by giving them the same name.
And that's fine.
It is pretty conventional in code when you want another integer,
and it's not i because you've used it already, fine.
You can use j.
So using i and j and k is generally fine.
If you're using l, m, and o, at that point you're probably doing something wrong.
There's no hard line, but at some point it gets ridiculous,
and you should be coming up with better variable names.
But i and j, maybe k is fine.
So now what's really happening?
Let me propose that this is my for each row.
This is my for each column.
I want to print one brick.
Now this isn't quite correct, but let me go ahead and make this version of Mario,
dot slash Mario, and oh, now there's what?
1, 2, 3, there's nine bricks there.
So I'm close.
It's supposed to be 3 by 3, nine total.
What do I want to do, though, to get this just right?
Yeah, up around the left.
Yeah, on what line number would you, or where would I put the new line?
Because I think I don't want to put it here,
because I'm going to get myself into trouble as before.
How about in back?
After the what?
After 13.
Yeah, so after I've finished printing each brick in the column from left to right,
I'm going to go ahead and print out, I think, a single new line here, nothing else.
And now if I open my terminal, run mic mario again, dot slash Mario, now we've got it.
And it's not a perfect square, like this one is,
because the hashtags are more vertical than they are horizontal.
But it's pretty darn close.
The takeaway here being you can certainly nest these kinds of ideas and compose them.
And honestly, I and J is maybe making this more confusing than necessary.
I could just give these better names, like row, row, row, and then maybe call for column or column.
I can spell it out if that's clear.
Column, column, just to make clear to myself, to my TA, to my colleagues,
what exactly these variables represent.
And indeed, like an old school typewriter, the outer loop is handling row by row by row.
But each time you're on a row, you first want to do column, column, column, column, column,
and that's what logically the nesting is achieving.
And again, if I do make Mario dot slash Mario, all I've done is change variable names.
It has no functional effect beyond that.
Now, this is a little more subtle, but there is a bit of duplication in this program.
There's a bit of magic.
And this is subtle, but does anyone want to conjecture what still could be improved here?
What is maybe rubbing you the wrong way?
Yeah, I've hard-coded the three here and here.
It's not a big deal.
It's like an in-class exercise.
Like, who really cares if I'm just manually typing three?
But if I want to make this square bigger and bigger and bigger over time,
I'm going to have to change it in two different places.
And I've conjectured last time.
And today, eventually, that's going to come back and bite you.
You're going to do something stupid, or a colleague
isn't going to realize you hard-coded three in multiple places, like just bad design.
So how could we fix this?
Well, we could just declare a variable like n, set it equal to three,
and then use n in both places.
And that's pretty darn good.
That's better, because now we are reusing the value.
But we can do one better than this.
It turns out in C, and in many languages too, there's the notion of a constant.
Whereby, if you want to store something in a variable,
but you want to signal to the compiler that this value should never change,
and better still, you want to prevent yourself, a human, or not to mention
a colleague from accidentally changing this value, you can declare it to be constant,
or constant for short.
So if I go back into VS Code on line five now and say constant,
that means that n is an integer that has a constant value.
So if I do something stupid later in my code,
and I try to set n equal to something else, the compiler won't let me do that.
It will protect me from myself.
So it's just a slightly better design as well.
All right.
Questions on any of these here?
Mario.
Examples.
The first of our sort of real-world problems, albeit simplified textually.
All right.
Let's focus, lastly, on things we can't really do well with computers,
namely some of the limitations thereof.
So here is a cheat sheet of some of the operators we've seen thus far.
We've played with these with comparison and doing some addition or the like.
But here we have addition, subtraction, multiplication, division,
and the modulo operator, which is essentially the remainder operator,
which you can do with a single command, with a single operator like this.
Let's use some of these to make our own calculator
and see what this calculator can and can't do for us.
So back here in VS Code, let me open my terminal.
Let's go ahead and create a program called calculator.c.
And in this program, let's do something super simple, initially,
that just adds two numbers together.
So let's include first cs50.h, so we can use our get functions.
Then let's go ahead and include standard io.h, so we can use printf.
Let's just copy, paste our usual int main void.
And inside of main, let's do this.
Declare variable x, set it equal to get int.
And let's ask the user what's x question mark.
Then let's declare another variable y, set it equal to get int,
and ask the user what's y question mark.
Then let's do something super simple, like give me a third variable,
heck, we'll call it z, set it equal to x plus y.
And then lastly, let's just print out the sum of x plus y.
So this is a super simple calculator for addition of two numbers.
Printf, quote unquote, what's the answer going to be?
Well, it's not percent s.
This was quick earlier.
What's the placeholder to use for an integer?
Percent i, backslash n.
And what do I want to substitute for that placeholder?
Just z in this case.
We haven't quite done this before, but again, it's just the composition
of some of our earlier ideas.
I can go ahead and make this calculator, enter, dot slash calculator, enter.
What's x is one, what's y is two, and indeed, I get three.
So not a bad calculator.
It seems to be working correctly, but it's maybe not the best design.
It's generally frowned upon to create a variable, like z,
if you're only going to use it a moment later in one place.
Why are you wasting my time creating a variable just to use it once and only once?
Sometimes it's fine if it makes your code more readable or clearer.
And in fact, it might if I called it sum.
That's arguably a net positive because I'm making clear to the reader
that it's the sum of two variables.
But even then, I'm quibbling.
I could just get rid of that third variable altogether.
And heck, I could just do x plus y right here.
That's totally fine and reasonable, especially since it's still a pretty
short line of code.
It's not hard for anyone to read.
Feels like a reasonable call.
But this hints at, again, my comment on design being subjective.
There's no steadfast rules here.
Some of the TAs might disagree with me, but like, this feels fine.
It's readable, which is probably the most important thing, ultimately.
Let's make this calculator, dot slash calculator, enter, one, two.
And we still get three.
So the code now is still working.
As an aside, if you're starting to wonder how I type so fast,
sometimes I'm kind of cheating with autocomplete.
So if I know I want to create a program called calculator and calculator dot
c exists, I can start typing C-A-L tab.
And you can hit tab to sort of autocomplete the rest of the file name
if it happens to exist there.
Better still, if I want to go back to previous commands I've typed,
I can actually use my up and down errors to go through my history.
So if I go up, up, up, you'll see all of the recent commands I typed,
and that saves me time too.
So just little keyboard shortcuts that speed things along.
All right, well, let's do something like this.
Not just addition.
Why don't we use some multiplication?
So how about we prompt the user not for two numbers, but how about just one,
initially, x?
And let's go ahead and multiply x by 2.
And I would do x asterisk 2, which is the multiplication operator in C.
Let's make this version of the calculator, dot slash calculator.
And now what's x?
Let's do 1.
So 1 times 2 is 2.
Let's do this again.
Let's type in 2.
2 times 2 is 4.
Let's do this again.
3, 3 times 2 is 6, and so forth.
That's fine.
It seems to work.
But maybe let's implement like a recent meme from the past year or two.
How about this?
Let's see if you recognize it as we go.
So I'm going to get rid of this code altogether.
And inside of my calculator, I'm going to do something like int,
dollars equals $1 by default.
Then I'm going to deliberately induce an infinite loop just for demonstration's
sake.
Then I'm going to do a character from the user and say something like this,
using get char, which gets a single character.
How about I'll tell the user, here's this many dollars, percent i,
with the US dollar sign before it.
Double it and give to next person, question mark,
if familiar with that one.
And I'm going to prompt them for yes, no answer.
But I'm going to plug in the current number of dollars
so they know what they're wagering on.
Then below this, I'm going to say, if the character the human typed in
equals equals y for yes, then I'm going to go ahead and do
dollars times equals 2, which recall was our shorthand notation
for doubling something in this case.
I could more pedantically say equals dollars times 2.
But again, I can save some keystrokes and do dollar times equals 2 instead.
There's no plus plus.
There's no star star trick, asterisk risk trick.
You have to do it in this way, minimally.
However, if the user does not want to double it and give it to the next person,
then let's do it in else and just break out of this infinite loop altogether.
But notice what I've deliberately done.
In get char, similar to printf, I have included a placeholder.
Why?
We implemented get char and get in and get string just like printf
in that you can pass in placeholders and plug in values.
Why?
Well, again, for the meme's sake, I want to be able to tell the user how much
money I'm about to hand them when I ask them the question, do you want
to double it and give it to the next person?
I want to see the number.
And the dollar sign is just because we're talking about dollars.
The percent i is because we're talking about integers.
All right.
If I didn't mess this up, let's make this version of a calculator, or meme,
so far so good, dot slash calculator, enter.
Here's $1, which was the initial value of my dollar's variable, online six.
Double it and give it to the next person?
All right.
Why?
Here's $2.
Double it and give it to the next person?
OK.
OK.
OK.
OK.
OK.
I'm going to do it faster.
It's getting pretty good.
You can see the power of exponentiation.
It's getting pretty high.
Let's keep going.
Keep going.
Lot of doll.
Too far.
That does not happen in the memes.
What happened here?
What's going on?
Yeah.
What do you think?
Exactly.
Good intuition.
Because the computer only has a finite number of bits allocated to each integer,
I hypothesized earlier that it's usually 32 bits, maybe 64 bits.
But it's finite, which means you can only count so high.
And it's roughly 4 billion.
Or again, an integer, by default, can be negative or positive.
So it's roughly 2 billion.
And that's pretty close to what we were getting here.
In fact, we overflowed the integer in memory.
In fact, integer overflow is a term of art, whereby you can overflow an integer
by trying to store too big of a value in it.
And the reason for this is, again, to make this clear,
this is a piece of memory inside of a laptop or a desktop or some other device.
And in these little black chips is a whole bunch of bits or really bytes
that can store information electronically.
But they allocate those bits in units of 8, maybe 16, maybe 32, maybe 64,
but finitely many per value.
And whether we're using 32 or 64, you can only count so high
if you have a finite number of bits.
And we've seen this problem even on a small scale with our light bulbs last week.
If we have a three-digit number, as represented by three physical light bulbs
or three tiny transistors in the computer, I can count from 0 to 1 to 2 to 3 to 4 to 5 to 6 to 7.
If I want to count to 8, though, I need a fourth bit.
But as the red suggests, if you don't have a fourth bit,
for all intents and purposes, that number is just 0.
Or as an aside, depending on how you're representing your number,
sometimes a leading 1 indicates that the number itself is negative,
which is why in VS Code, we actually saw both symptoms.
First, we went negative because we wrapped around logically,
much like that 1 resulted in our getting back effectively to 0.
And then we did indeed end up on 0 ultimately.
So how can we chip away at this?
Well, a couple of solutions, perhaps.
Let me close my terminal window here.
And instead of using an int, well, let's just kick the can down the road.
Let's use a long, which is 64 bits.
So at least we can give away even more money in this scenario.
I can't use percent i and need to use percent li now for a long integer.
But I think at this point, if I go back to VS Code's terminal window here,
oh, and I quit that program by hitting Control-C quickly,
now I'm going to go ahead and do make calculator again, dot slash calculator.
And I'm just going to keep hitting Y. But because I'm using a long int now,
and thus 64 bits, if I do this long enough,
it's going to get crazy high and much, much higher than before.
High enough that I'm not going to keep clicking Y Enter
because we're never going to hit the boundary.
But eventually, especially if I did this in a loop automatically,
it would certainly, oh, oh, OK, I guess exponentiation works fast.
OK, so it did work.
I didn't think I was going to hit it enough times.
But the same problem happened again.
We overflowed this long integer, even using that many bits.
Because I was talking so long, I kept hitting Y enough times
to overflow even that long integer.
So that too was a problem.
And this happens truly in the real world.
So picture here is a Boeing 787 from a few years back,
long before there were all the more recent problems with Boeing planes,
whereby after 248 days of continuous power, which is kind of a thing
in the aviation industry, like time is money,
and generally they want the planes in the air as much as possible,
which means they want them powered on as much as possible, which means
they don't turn them off at night.
They keep them going and flying.
After 248 days, the New York Times reported a few years back
that a Model 787 airplane that has been powered continuously for 248 days
can lose all alternating current electrical power
due to the generator control unit simultaneously going into fail-safe mode.
This condition is caused by a software counter-internal to the GCUs
that will overflow after 248 days of continuous power.
Boeing is in the process at the time of developing a GCU software upgrade
that will remedy the unsafe condition.
So literally what this means is that the power to these planes
would just shut off if the planes were on for more than 248 days at a time.
And this was a common thing for planes to be maximal power.
Why was this actually happening?
Or what was the solution?
Well, the short-term fix, because it took a while for Boeing to fix this,
was what?
What would you do if the symptom is that the plane shuts off mid-flight
after 248 days?
Yeah?
Literally turn it off and back on again, much like you've probably been taught
with your phones and computers and any other electronic devices
that somehow freak out on occasion.
Reboot the plane.
Now, why is that?
Well, anytime you reboot a phone or a laptop or a plane,
all of those variables get reset to their default values,
which if it's the first line of code, like in some of my examples,
gets set back to zero again, for instance.
The first line of code is executed from top to bottom.
So this effectively solved the problem.
But when they finally rolled out a fix, then you didn't have to do that anymore.
But the source of the problem is essentially
that they were probably using 32-bit integers, but also negative values.
So they had 31 bits at their disposal to count to positive numbers.
And 248 days is roughly how many tenths of a second there are,
which means once you count in tenths of a second for 248 days,
you would overflow an integer.
And the power would shut off effectively because something ended up going to zero.
So there was a lot of marketing speak or technical speak in that,
but it boiled down to just a simple integer overflow.
There's a historical bug in Pac-Man, if you've ever played this in any of its forms,
whereby you can play up to level 255.
But because there was a missing if condition that
checked what level you were on, you could accidentally garble the screen
if you were amazing at Pac-Man, because they too would overflow an integer
and just random characters would end up appearing on the screen.
So it's sort of like a badge of honor to actually hit level 256 in this way because of this bug.
But there's yet other issues we can see here.
And if you don't mind, we might go a couple minutes over,
but let me just demonstrate what these examples can do for us here.
If I were to revamp my calculator here as follows,
by clearing my terminal window after hitting Control-C to kill that,
let me go ahead and get rid of all of this meme code here,
scrolling down to the inside of main.
Let's just do a couple of things like this.
int x equals quote unquote, what's x?
Question mark.
Then let's go ahead and do int y equals get int quote unquote, what's y?
Question mark.
Then let's go ahead and print out just x divided by y.
So here's a percent i backslash n, x divided by y.
This would seem to be a calculator now for division, which I can make as before.
And actually, sorry, I don't want to do missing terminating.
Oh, sorry, missing a double quote there was an unintended bug.
So if I make this here calculator, do dot slash calculator, type in 1, type in 3.
I get 0, which is weird.
What if I do instead maybe 2 and 3?
It's 0 instead of 0.66.
What if I do 3 and 3?
Well, that curiously works.
But if I do something like 4 and 3, which would be 1.33,
that 2 doesn't seem to work.
So there's this other issue in computing when you have finite numbers of bits known as truncation,
whereby even when you're trying to do floating point math, like with a decimal point,
if you are using an integer, you're going to throw away everything after the decimal point
unless you're explicitly using the right data type.
And we saw an illusion to this earlier.
If I actually go in now and change my values from integers to floats
and change the percent i to a percent f and remake this calculator,
now I can do 1 divided by 3.
And I actually get back that there response.
But there's another issue latent here, which happens to in the real world,
whereby I'm going to tweak this percent f to be a little arcane.
It turns out you can tell C how many digits you want to show,
how many significant digits you want, if you will,
by just using a dot and then a number, like 50 arbitrarily.
And contrary to what you might have learned in grade school,
this calculator would seem to think that dot slash calculator 1 divided by 3
is not 0.33333333 infinitely many times.
There's all this random stuff happening at the end.
Long story short, this is because computers, one, only use finitely many bits
even to represent floating point numbers.
And if there's an infinite number of those, you can't possibly represent
every possible floating point value.
So we're essentially seeing an approximation of 1 third precisely.
But this too happens quite a bit in the wild.
There's really no solution to this other than by throwing more bits at the problem
using a double instead of a float, or at least somehow trying to detect this
and catch this.
That then is what we'd call floating point imprecision.
But to tie this together and sort of induce a bit of fear
and for the coming years, these things happen all of the time.
Back when I was finishing school, there was the so-called y2k problem,
or year 2000 problem, whereby for decades,
computers had been using not four digits to represent years,
but just two because it was convenient.
It was more efficient because you use half as much memory to represent
maybe the year 1999 just using two digits instead of four.
Of course, when the year rolled around from 1999 to 2000,
if you didn't have these numbers even in memory,
you might confuse 2000 with 1900, which was the presumption
if you're only storing two digits.
So we screwed that up.
And thankfully, the world scrambled.
And if you read up on Wikipedia and news articles from the time,
everyone thought the world might very well end.
But it didn't.
So you'd think we'd learned our lesson.
Unfortunately, another such problem is coming up in the year 2038,
whereby historically, since the 70s and prior,
computers have generally used 32-bit integers to keep track of time,
the date and the time, by means of counting how many seconds have passed
since January 1, 1970.
And all of the math is just relative to that date
because that's when computers were really starting to come onto the scene,
if you will.
Unfortunately, there's only 4 billion values you can count to, or 2 billion,
if you're doing negatives, from January 1, 1970.
And so on the date, January 19, 2038, we will overflow a 32-bit counter.
And suddenly, if this problem is not fixed by you or other people before the year 2038,
our computers and phones and other devices may very well think it's December 13, 1901.
So there are solutions to these problems.
CS50 is all about empowering you with solutions to these problems.
But if you'd like to scan this here code,
this will add that date to your Google Calendar or your Outlook Calendar.
Keep an eye on it.
That, though, is week one for CS50.
Problem set one will be in your hands soon.
We'll see you next time.
Thank you.
you
