Lecture1 720p en
Lecture1 720p en
DAVID MALAN: Yeah, let's just add another branch here, so to speak.
So if variable C equals equals capital Y, then I can go ahead here
and say printf agreed.
And then let me close my terminal to make more room.
Otherwise, down here, else if C equals equals capital N,
let's go ahead and again say printf not agreed.
And I claim that this would actually now work.
It's a four-way fork in the road, but I'm at least
checking for lowercase, uppercase, lowercase, uppercase for y and n
respectively.
I claim that this is correct, but this too,
even if you've never programmed before, should start today
to rub you the wrong way.
Like, we can do better.
This isn't the best design.
Why might that be?
Yeah?
STUDENT: Could you change the character c to be uppercase, like before you even
[INAUDIBLE]?
DAVID MALAN: Ah, clever.
So could we change the variable c to just be forced to uppercase
or maybe forced to lowercase?
No matter what the human types, we just do that ourselves so that way
we can just simplify this again to two possible scenarios.
I love that, but we haven't seen any functions yet
in C that would let me change things to uppercase or lowercase.
So we'll get there, but a good instinct and correct.
Other thoughts?
STUDENT: Use "or."
DAVID MALAN: So we could use "or" in some sense, like a logical "or."
What I don't like about this, to be clear, is that it's repeating itself.
And there's this principle in programming, and in life in general,
like, don't repeat yourself unnecessarily.
And by that I mean I literally have the same line 10 as 14.
I have the same line 18 as 22.
And if anything, one, I literally wasted twice as much time as I needed to.
Put another way, per our discussion of Scratch,
what if I go in and just change something like,
I want to be more excited, like "Agreed!"?
Well, I might forget to change it in the other place.
And let's just claim for today's purposes that that looks stupid,
it's a bug, because I want them to be consistent.
So don't invite situations where you might change something
in one place but not another.
Just only write it in one place total.
So I like this idea of "or"-ing things together.
So let me go ahead and delete what I just did.
And just to be clear, too, while this is on the screen, when you highlight code
in VS Code based on how we've configured it, these dots just show you
how much I've indented because in C, stylistically, the convention
is generally to indent four spaces and maybe four more spaces.
So those dots just help you count without having
to manually eyeball things yourself.
But let me delete those lines.
Let me delete these lines.
And this is going to look a little weird,
but the way you can "or" two thoughts together, so to
speak, like "or" them together, is you don't say "or,"
but you use two vertical bars, which syntactically
means the English word "or."
And you can just ask the other question, if C equals,
quote, unquote, capital 'Y.'
And then down here, I can say or C equals equals capital 'N.'
So it adds a little more code to each of those lines,
but it doesn't add redundancy, because I've not duplicated my printf.
I've not added more curly braces unnecessarily.
Now as an aside, there's the opposite of "or", logically is the word "and."
Just so you've seen it, I could do this.
"&&" in C is how you express that the thing on the left must be true
and the thing on the right must be true.
But why would this make no sense in this context of line 8?
STUDENT: It can't be uppercase and lowercase.
DAVID MALAN: Yeah, at least to my knowledge,
a character can't be both lowercase and uppercase.
That just makes no logical sense.
So indeed "or" is what we want in this case.
Other questions?
STUDENT: In CS50.h, is there a way to directly compare strings [INAUDIBLE]??
DAVID MALAN: Good question.
Via CS50.h, is there a way to compare strings.
Short answer, no.
But C is going to give us that capability.
And in fact, next week, among the things we'll do is actually compare strings.
And if you've programmed before, you'll see in C
that it actually doesn't work the way that you might expect.
But that's a problem, too, that we will solve.
But that transcends CS50.
That's a question for C. Other questions on this kind of logic?
Just to make this real then, anytime you click one of those EULAs
or terms and conditions on a form in a piece of software,
odds are there is code as simple as this underneath the hood.
Maybe it's graphical.
Maybe it's checking for you clicking this button
or maybe hitting the Enter key.
But underneath the hood is presumably some kind
of conditional checking for those kinds of outputs.
All right, how about another building block
from last time, which we'll now translate to C,
namely loops, things that happen again and again?
And these, too, are everywhere in code.
So in Scratch, here's how we might meow three times, super simple.
In C, it's going to look a little weird.
But you will get used to this over time if you've never programmed before.
It looks like a mouthful, OK.
But let's tease it apart line by line.
And you'll see that you won't have that reaction frequently because it's all
going to start to look very similar to itself.
But what are we doing here?
In C, you don't have the luxury of these cute and fun puzzle pieces
that just do the work for you, repeat three times.
In fact, in C and programming in general, sometimes
the work is on us to actually figure out, OK,
how can I use functions, variables, conditionals, and loops
and implement some idea like repetition, like looping?
And in C, here's how this might work.
How can I go about doing something like printing "meow" three times?
Well, I know about variables now.
We're about to see loops.
And I've seen how I can update variables by plussing or minusing
some value to them.
Let's combine those ideas.
So first, I'm doing what with this highlighted line in English?
If a friend cared to ask you, like, 'what is this line of code doing'
later today, what would you say?
STUDENT: It's creating a variable called "counter" and setting it equal to 3.
DAVID MALAN: Good, it's creating a variable called "counter"
and setting it equal to 3.
I'll use slightly new jargon.
I'm defining a variable, would be the term of our "called counter"
and setting it equal to 3.
So I'll use my hand to represent the counter.
And that's all a variable is.
It's like storage in some case that I'm representing information, using
my hand in this case or the computer's memory here.
Now what happens when using a loop in C?
There's different types of loops, one of which is called a for loop-- oop--
one of which is called a while loop-- spoiler.
A while loop works like this.
Inside of parentheses is a Boolean expression just like inside
of a conditional that asks a question.
But this time the question is going to determine,
do you keep going through the loop again and again and again?
So it's not a one-time thing potentially.
It is checked again and again and again to decide
when it is time to stop looping, to stop cycling.
All right, so it's asking this question first.
Is counter greater than 0?
OK, obviously the answer is true because I'm still holding up three fingers.
So what happens?
C goes inside of the curly braces per the indentation
and executes printf of "meow," which prints out a "meow" on the screen.
The next line of code executes, which, recall,
is the same as just subtracting 1 from counter.
So I think I take down one finger, so I'm left with two.
And what happens next?
Well, this you just kind of have to memorize.
Once you get to the end of the inside of a loop,
you go back to the beginning of a loop here
and ask the same question, the same Boolean expression.
So is 2 greater than 0?
OK, obviously so.
So you go into it, you print a "meow."
You go into it and decrement counter further by one.
So now my hand is holding up one.
Now we wrap back around to the Boolean expression.
Is 1 greater than 0?
Obviously.
We print out a third "meow."
We then decrement counter again, and my hand goes to zero.
We go back around once more.
Is 0 greater than 0?
No.
And now the program just terminates.
Or if there were more code here, it would just
jump outside of the curly braces and keep going lower on the screen.
So that's all that's happening.
And so this is what MIT has the luxury of doing with pictures.
But at MIT, someone probably essentially wrote
code that looks like this to give us the illusion, the abstraction of this.
So what we're learning today is how they invented these puzzle pieces
by just using lower-level plumbing, if you will, like this here.
Yeah?
STUDENT: What would happen if you created the variable "counter"
inside of the curly braces?
DAVID MALAN: A really good question.
What would happen if you created the variable inside of the curly braces?
Short answer, it just wouldn't work in C,
because if I were to try with my slide here,
for instance, to move this line of code here down inside of this, for instance,
now the very top line is trying to use counter before it even exists.
So C is very literal it.
Reads top to bottom, left to right.
And if it hasn't seen you define or create a variable yet,
you're going to get some scary error message on the screen instead.
All right, other questions on this here code?
No?
All right, so if we want to then maybe tighten this up a bit,
let me propose that we could do this instead.
So besides this version of the code, let me just
do something more canonical, more conventional.
So you're totally fine with using a variable like counter .
It's what Scratch uses by default. It's very verbose.
It does what it says.
Frankly, once you get comfy with programming,
like most typical programmers, whenever they
have a single integer in a program whose sole purpose in life is to count,
they'll just use "i" for integer just like I used "c" for character.
When you have larger programs, you don't want
to start using A and B and C and D and E and F and so forth for your variables
because nothing's going to make any sense.
But when you're doing something super simple like counting with an integer,
using a short-named variable is totally stylistically reasonable.
But I can tighten this up further, not just renaming counter to i.
What else can I do, if you recall?
Over here?
STUDENT: [INAUDIBLE]
DAVID MALAN: Sure.
STUDENT: A for loop?
DAVID MALAN: Oh, OK, for loop.
Yes, that was my spoiler.
But while in a while loop, I can tighten this up slightly more.
Over here?
STUDENT: Instead of i equals i minus 1.
DAVID MALAN: Yeah, instead of i equals i minus 1,
I can actually tighten this up this way.
And we didn't see the minus before, but it's
the same idea-- i minus equals 1, or even more succinctly, i minus minus.
So when you get comfortable with programming, any of these approaches
are correct.
This would be more conventional at this point.
So if you want to write code like most other people
write code, adopt ultimately these kinds of conventions.
All right, so that just does the exact same thing, though.
But let's now put this into practice.
Let me go back to VS Code here.
Let me go ahead and clear my terminal and close agree.c from before.
And let me go ahead and create a file called "meow."
So code meow.c.
And let me do this the sort of wrong way.
Let me include stdio.h.
at the top, int main(void) thereafter.
Inside of there, let me do printf "meow."
And then you know what?
I don't want to keep typing that.
Let me just go ahead and copy/paste two more times.
So I claim this is correct, make meow.
./meow, done.
I've got code that prints "meow" three times.
But this, again, should already rub you the wrong way.
Why?
Yeah?
STUDENT: There's duplication.
DAVID MALAN: Because what?
STUDENT: There's duplication.
DAVID MALAN: Because I have duplication.
I mean, I literally copied and pasted it.
And that's kind of a good rule of thumb.
If you, in the future, start finding yourself copying and pasting code
within the same program, you're probably doing something wrong.
There's a better way to design it even if it's correct.
So this is clearly a candidate for a loop.
So let me go ahead and actually do that.
Let me just go ahead and remove all of this duplication.
Let me give myself a variable called i, set it equal to 3.
Let me go ahead and give myself a while loop
and check that i is greater than 0.
Inside of this loop, let me print out just "meow" once.
But I'll reuse that code again and again because here I'm
going to do i minus minus.
So that's the exact same code, the tight version of it that we saw a moment ago.
Let me go ahead and "make meow" again, ./meow, and it still works.
Why is this version better?
Because if you want the cat to meow five times, you change it in one place.
If you want to make the cat a dog, you change the meow to a woof in one place,
albeit changing the file name eventually, but changing it
in one place, not worrying about changing it again and again and again.
But there are other ways to do this.
For instance, let me propose that.
And actually, let's see, let me propose that instead of just doing it this way,
just to be clear--
yeah, let's go ahead and propose that instead of doing this,
we can actually count in different directions.
I'm kind of forcing this idea of starting at 3, going down to 0.
But when normal humans in this room, if you ever count something,
you probably do 1, 2, 3, and done.
Like, that's how we would count in the real world.
Well, we can do that, too, here code-wise.
We could initialize i to 1.
We could check that i is less than or equal to 3.
And we've not seen this syntax before, but there's
no easy way on a typical keyboard to type a less than or equal sign
like in a math book.
So we use two characters, a less-than sign and then an equal sign
back to back.
And that means less than or equal to.
And this is the same idea so long as I plus plus i inside of it
because that'll start at 1, then 2, but it won't stop then.
It will go up to until i is equal to 3.
Once i becomes 4, then that Boolean expression isn't going to be true.
So it stops after three "meow"s total.
But there's another way, too, and this is probably
the most conventional and the way you should do it
even though it's just as correct.
In CS, if you've seen already last week, we almost always start counting from 0.
Why?
Just because, so we're not wasting a pattern of bits.
So generally when you start writing code that counts,
you should, quote, unquote, "almost always" start at 0,
count up to but not through the total you
care about so you don't get one extra by accident.
And so this would be the most conventional way
of doing what we just described.
But they're all correct.
You can make an argument that all of them are equally good.
This is what most people, quote, unquote, "would do."
OK, other questions on this here syntax or logic?
No?
All right, how about--
we got some cookies on the horizon.
But before we get there, let's meow a few more times, if we may.
So how about doing a little bit differently versus the while loop.
And I think we heard it over here.
Turns out there's another type of loop altogether.
So this one here.
And this one, if you can believe it, is probably even more conventional
than the other way.
And this is going to be thematic in programming.
There's rarely one way, one right way to do things.
You're going to have bunches of different tools in your toolkit.
And your code might look different from someone else's because each of you
tends to reach for a different tool in that toolkit.
And here's another tool--
and as you proposed earlier--
a for loop.
A for loop is just another way of achieving the exact same idea
using slightly different syntax.
And it's appealing, frankly in general, because it's a little more succinct.
It just saves some keystrokes even though you have to memorize
the order in which it works.
This code is identical to this code here functionally.
But aesthetically, of course, it looks different.
How does it work?
In a for loop, notice that in the parentheses
is not a single simple Boolean expression.
There are three things.
One, before a semicolon, is a place to initialize a variable
to do your counting typically.
Second is the Boolean expression.
So it's still there.
It's just surrounded on the left and the right by two other things.
Lastly is the update.
What do you want to do at the end of every loop through this block of code?
So you can probably imagine where we're going with this.
How does this work?
The first thing that happens is that a variable
called i is defined and initialized to the value of 0.
That happens once and only once.
Then we check the condition.
Is 0 less than 3?
Obviously yes.
So now we don't do the plus plus yet.
We go into the loop.
And this is where the for loop's a little confusing at first.
We print out "meow."
Then what happens?
There's no more lines.
So we go back to the for loop, and we increment i at that point.
So now i is 1.
Then we check the condition. i is less than 3?
Yes, because 1 is less than 3.
We go back into the loop and print "meow."
Now we go back to the plus plus, so i is now 2.
We check the condition.
2 is less than 3 obviously.
So we go back into the loop and print "meow."
Then we do the increment. i is now 3.
Is 3 less than 3?
No, so we exit the loop, and we're done, or we keep
going down here if there's more code.
But how many times did I say "meow?"
1, 2, 3 total, when my hand was 0, 1, and 2.
Questions on this alternative syntax?
It takes some getting used to, but most people
would write loops using a for loop, I would say.
STUDENT: Could you now in the curly braces, use just one line of code?
DAVID MALAN: Yes.
If you really want to be cool and save syntax,
yes, it is correct and common to eliminate the curly braces if you only
have one line of code therein.
We in class will always put the curly braces there
because this is the kind of thing where, if you get forgetful, you go in later
and add a second line.
Like, darn it, like you forgot the curly braces,
things will not work as expected.
So in general, use the curly braces, but you do not have to strictly.
Other questions on 6?
Yes?
STUDENT: [INAUDIBLE]
DAVID MALAN: Can be used without, what?
STUDENT: [INAUDIBLE]
DAVID MALAN: Oh, could you do it without the condition?
Yes, there are very fancy things you can do that we won't focus on today.
But yes, if you want to get rid of the condition, you could get rid of this
here.
And that would actually make the loop go forever,
which may be a good thing if it's like a clock
that you want to tick forever, but often not a good thing in code.
Good question, though.
All right, so beyond that, let's just go ahead and put this into context.
Just in case it helps you to think about this,
this is just another flow chart, if you're
more of a visual thinker, that represents
what it is this loop is now doing.
Previously, all of our arrows went from top to bottom and stopped.
But now there's an arrow going back, up, and around
because of this loop, this cycle.
So when we start this program, we set i equal to 0.
We then check, is i less than 3?
Obviously it is, so we print "meow."
We increment i, and then we go back to that same condition.
We check the condition.
We print "meow."
i plus plus, go back, go back.
Now, if i equals 3, 3 is not less than 3, so the answer is false.
And we stop.
So again, it's just another way of thinking
about how the code in Scratch, how the code in C
might alternatively work in each of these contexts.
But there's this one other puzzle piece in Scratch,
recall, that's not the repeat block, which
is for finite numbers of repetitions, but forever.
And in C, there is a way to do this, but it's a little weird looking.
There's no forever keyword.
But you can use the while loop or, as you inferred,
you can actually use the for loop without a condition in the middle.
So here, I can actually say this.
If I want to do something forever, I want
to make sure that the answer to my question, the Boolean expression,
is always true, always true, always true,
the easiest way to achieve that goal is just literally write "true" there
because true is true no matter what.
And it's a trick for making the loop forever
go around and around, as you might if you
want the cat to live forever and meow incessantly
or if it is a clock that you want to tick forever or the like.
So here, for instance, is how we might have a cat meow endlessly,
using this so-called for loop instead.
But recall that in Scratch, we also had this ability
to create some of our own puzzle pieces.
And this, too, is something that we're going to be able to do here in C.
And let me propose that we do exactly that
by introducing the C analog of this.
So here, for instance, is, in Scratch, our definition
of a function called meow whose sole purpose in life
was to just play the sound "meow" until it's done.
This is going to look a little weird at first.
But you'll notice some similarities with main.
So recall this thing I keep typing with main, int main(void), int main(void).
That's just the "when green flag clicked" equivalent for today.
But if you want to create your own puzzle piece or your own function in C,
you, for now, literally do this.
You say, void, the name of the function you want to create, and then
void in parentheses.
And technically what this means is that this function has no return value.
It doesn't hand you anything back like get_string or get_int.
And the "void" in parentheses means it takes no inputs.
It only meows.
You don't have to tell it how to meow.
It's just going to meow.
So no arguments, so to speak.
This literally just prints out "meow."
But what this does for me is it abstracts away the idea of meowing.
I don't need to know how to use printf or that you're
using printf to make the cat meow.
I now have a function in life called meow because in Scratch, recall,
I used it like this.
When the green flag is clicked, I could repeat three times this new custom
puzzle piece.
But in C, I could now do this.
In my main program, I can use a for loop just like we saw a moment ago,
copy/pasted from earlier.
But now I can call my own C function called meow.
And let me go ahead now and do this.
If I go over to my C code here, back in VS Code, let me go ahead
and delete everything inside main.
Let me go ahead and do for int i equals 0, i is less than 3, i++.
Inside of my curly braces, let me go ahead and say "meow."
But I now need this meow function to exist because if I
do "make meow" again, notice error.
"Implicit declaration of function meow is invalid in C99"--
the 1999 version of C. What does that mean?
Well, it doesn't know what the meow function is.
And the meow function is not in CS50.h.
It's not in stdio.h.
I have to create it.
So let me type out or really copy/paste what I had on the screen a moment ago--
"void meow meow"--
[CHUCKLES] "void meow(void)" printf, quote, unquote, "meow," close quote,
semicolon.
But here, too, let me scooch this down a bit
so you can see all the code at once.
Let me now do make meow.
And unfortunately, I still have an error.
If I scroll up, still on line 7 of meow.c,
my compiler thinks that meow is invalid, that it does not exist.
This too is a common mistake.
And as simple as this code might be in spirit, where did I screw up?
Yeah, in the middle.
STUDENT: You need to define the function like above where you use it.
DAVID MALAN: Yeah, I need to define the function before I use it.
So again, C is going to take you literally.
If you try to call meow on line 7, you better not define it on line 11.
You better define it higher up.
So the simplest fix is going to be just to do this.
Let me clear my terminal.
Let me highlight and just delete the meow function.
And let me just paste it up here.
And this will actually solve the problem.
Make meow now works.
And if I do ./meow, that, too, works.
But this isn't really the best solution because if your solution is constantly,
oh, well, just put it up there, put it up there, put it up there,
I bet we could contrive a situation where
one function needs to be above the other,
but it needs to be above the other.
And that's just not going to work in general.
And more importantly, it just pushes main lower and lower and lower
in your file.
But the whole point of your main function
is like, that's the entry point.
That is what happens when the green flag is clicked.
And so just in terms of user conventions, it's just useful for main
to always be at the top of a file because then you can find it fast.
Your friends can, your TFs can find it quickly if it's at the top.
So the other solution here would be to leave meow at the bottom
and leave main at the top.
But this is the only time, if I may, that copy/paste is OK.
What I've highlighted here in line 11 is what's called the function's prototype.
It is enough information to give you the return type, the name of the function,
and the return value--
and any arguments.
And so if you just copy/paste that one line and end it with a semicolon
up there, that's enough of a hint to the compiler
that, OK, it doesn't exist yet, but it will.
And it will look like that.
That's the only time it's OK to copy/paste
the very first line of a function you've written
to the top of the file with a semicolon so
that you can make the compiler happy.
So if I do make meow now, still no errors. ./meow, and it now works.
But let me add one final feature, coming back to Scratch here.
And then it's time for a snack.
So here, recall, was sort of the last fancy thing we did in Scratch, where
we created not only our own custom puzzle piece,
but it took an input so that we didn't need to keep using the loop ourself.
We could just let the meow function be told how many times
do you want the cat to meow.
So in C, we don't have to make that many changes except this.
We change the prototype to take an argument inside of parentheses.
And this is the syntax for that.
If you want your own function in C to take one or more arguments,
you give the arguments a name, n, or whatever you want to call it.
But you have to tell C what the type of that input is.
So it's an int n.
So it knows it's a number.
And then you can just use n in your program.
So instead of hard coding, typing manually the number 3,
I'm just using n here.
So this is equivalent to what I did with Scratch, by just dragging and dropping
the n variable there.
And then "meow" will get printed that many times.
If I want to then use this-- notice, this is the last version of the cat
that we did last week-- you just say "meow" this many times.
So in C, this is where now the code gets very succinct
because all the main part of the program does is meow three times.
So this, again, is an abstraction.
I don't need to know, care, or remember how meow is implemented.
I just need to know what its return value, its name, and any arguments
thereto are.
So if I make this change, I think we can get the cat
to meow any number of times.
Let me go back over to my C code here.
Let me go back into the file and change "void" here to be int n,
where n just means number.
I could use i, but n tends to be a quantity instead of a counter.
I then, inside of this function, am going to do a for loop--
for int i get 0; i less than n--
instead of 3-- i++.
And then inside of here, I'll paste that "meow" again.
I need to change my prototype to be identical, so another copy/paste,
or just manually edit it.
But now notice what's cool about main, is
that now I can meow maybe three times.
Make meow, Enter, ./meow.
OK, or if I really want to be cool, I can change this to 30,000 times.
Go back here, make meow.
Increase the size of my terminal window for a dramatic pre-break flourish.
And there are 30-- that was a fast cat.
There are 30,000 meows.
I think now let's go ahead and take-- that's a lot-- a 10-minute break.
We'll see you in 10.
Cookies are now served outside.
All right, so we are back.
And I realize this has been a lot so far, right?
So there's a lot of new syntax.
There's a lot of translation of Scratch over to C.
But among the goals of having spent last week in Scratch
and having spent problems at 0 in Scratch is that none of today's ideas
are really all that new.
It's just a lot of syntax that will get more comfortable and more
in your muscle memory as time passes.
Up until now, though, we've focused largely on these side effects,
like things happening on the screen.
And that was akin to the speech bubble appearing in the world of Scratch.
But let's focus for just a bit-- before we then explore things
we can't do very well in code-- on return
values instead in C. We've seen them already.
Like, get_string returns a value.
Get_int returns a value, a string and an int respectively.
But what if we want to make our own functions that don't just
meow and visually have this side effect of meowing on the screen
but actually hand us back some value?
Well, I bet we can do this in C, as well.
Well, let me propose that to go that route--
let me go back to VS Code here.
And let's make our very simple calculator
that just adds some numbers together.
But the same calculator, we'll soon see, is
going to get us into trouble if you don't understand what the computer is
doing underneath the hood.
Let me go ahead and run code of, say, calculator.c.
And in here, let me go ahead and give myself
access to the CS50 library with CS50.h, the stdio.h library with stdio.h,
int main(void), which, again, we'll just take for granted today that we have
to include atop any of these programs.
And let's just add two numbers together-- super simple calculator.
So it gives me a variable called x.
Assign it the return value of get_int.
And I'll ask the user to give us x.
Give me another variable called y.
Assign it the return value of get_int again.
But this time, ask the user for y.
And then, lastly, let's just go ahead and print out the value of x plus y.
But I don't think I can get away with something
like this, x plus y semicolon, because if I
do this, based on what we've seen before,
what's actually going to get printed out?
STUDENT: x plus y.
DAVID MALAN: Right, literally like x plus y.
So I think this is where I need the F in "printf" for formatting.
What I think I really want to do is print out the value of some placeholder
because, what do I want to substitute for percent i
maybe as a second argument to printf intuitively?
Maybe just x plus y.
So indeed, I can get away with this because it turns out in C,
there's a bunch of arithmetic operators, all of the ones that you might expect,
including addition, subtraction, multiplication, division,
and even this one, the so-called modulo operator,
which generally gives us the ability to calculate a remainder when
you divide one number by another.
But I'll keep it simple with addition.
And indeed, with printf, if I want to print out the value of x plus y,
I can do that.
But I have to tell printf what kind of value to expect, an integer,
thus the percent i instead of %s for string.
And I think this should do the job.
So let me go back to my terminal.
Make calculator, Enter.
All is well so far. ./calculator, and let's keep it simple--
1 for x, 2 for y.
And indeed, I get 3 as the output.
It's not very dynamic.
It can't do a subtraction or multiplication or much more.
But it does at least do those kinds of calculations.
But let me propose now that we maybe make a reusable addition
function, right, because addition is something I'm going to do a lot.
And maybe it should be abstracted away with a function
just like meowing was abstracted away a moment ago.
So let me go ahead and instead of doing this,
let me go ahead and give myself a function called add,
but instead of last time where I had a meow function,
I'm obviously going to call this "add" instead.
And instead of last time, taking in no arguments,
I think I want add to work a little differently.
I don't want add necessarily to take an argument yet,
but I do want add to return some type of value.
And just intuitively, what type of value should an addition function return?
STUDENT: An integer.
DAVID MALAN: An integer, so an int.
So I'm going to change void, which means the absence of a return value--
nothing's coming back-- to literally "int."
But I'm not going to change the thing inside parentheses yet.
I'm going to go ahead and copy my prototype up here.
And I'm going to make this change, return x plus y.
And then here, instead of printing out x plus y, let's go ahead and do this.
Let me give myself a third variable just for now.
z equals the return value of this brand-new
add function that's going to add x plus y for me.
And then let me print out the value of z.
Instead of x plus y, I'm outsourcing now to this add function
so it will do the addition of x plus y.
So similar in spirit to meowing, but the return values, I claim,
are about to create an issue.
So let me make calculator again.
And there's definitely some errors.
So here we have, "use of undeclared identifier x."
And that's on line 17.
So that's pretty far down in the file.
So specifically, my compiler does not like my use of x on line 17.
But wait a minute, x is clearly defined on line 8.
What intuitively might explain this issue
even if you've never programmed before?
Yeah?
STUDENT: Well, because x and y are defined in the main function, not
the add function.
DAVID MALAN: Yeah, because x and y are defined in the main function,
not in the add function.
So the term of art here that we're about to introduce
is something called "scope."
So "scope" just refers to the context in which variables exist--
the context in which variables exist.
So by that, I mean this.
On line 8, I've declared x.
On line y, I've declared--
[CHUCKLES] on line 9, I've declared y.
But the catch is-- and here's where the curly braces are helpful--
those variables only exist in the context of the outer curly braces
that are nearest to them, like this.
So I can use x and y on lines 10, 11, 12, and even up to 13,
but not thereafter.
So I certainly can't use x down here on line 7.
But this is a problem, because if add's purpose in life is to add x and y
but add can't access x plus y, well, we have an issue of scope.
Like, x and y are not in scope for this add function.
But that's OK because remember that every function we've seen thus far
can have maybe a return value or a side effect,
but it can also take 0 or one or two or more inputs, known as arguments.
So what if I instead do this?
Let me clear my terminal window.
And let me update add to not take nothing
as input but maybe two integers.
And I'll call them arbitrarily a and b.
But I have to tell the compiler what type of arguments
they are-- two integers, one after the other.
And now what I can do is this.
Let me change this up here, too-- int a, int b--
just so that the prototype is exactly the same.
And the only purpose of this prototype is just to avoid the previous error,
where the compiler didn't realize add was going to exist
because it came later in the file.
So here on line 11 now, if I want to add two values, x and y,
this is now the syntax.
We saw syntax in Scratch for passing in inputs
to tell it how many times to meow.
So this is just telling add what two numbers to add together.
So now I have to change this to a plus b, for reasons we'll soon see.
And let me see if this is right.
Make calculator.
So far so good. ./calculator.
Let's do 1 and 2 for x and y respectively.
And hopefully we should, again, see 3.
Now, what's going on?
So here, again, if I zoom in on my add function, this "int" here on the left,
on line 15, means what about add?
STUDENT: [INAUDIBLE]
DAVID MALAN: This means that it has a return value, that it's an int.
So it's going to hand me back, metaphorically, a slip of paper
with an answer on it that is of type integer.
It's not a word, like my name.
It's a number instead.
These mentions of int here and here are inside the parentheses, which means
this function, add, takes two inputs.
The first is an int.
The second is an int.
And just so we have something to call them, I call them a and b respectively.
So what happens essentially when I call the add function now on line 11,
I'm kind of passing in x.
I'm passing in y.
But the add function is going to think of them as a and b respectively.
It could call them anything I want.
I could change this to the word "first" and "second."
And then I could literally change this to "first + second."
Those are perfectly acceptable as argument or variable names.
But who really cares?
Like, a and b for such a simple function is perfectly reasonable, too.
Technically, if your mind is going there,
I could even call them the exact same thing.
But let me propose for today, certainly don't do that because it just
confuses things if you've got x's and y's here, x's and y's here,
but they're clearly different.
Just don't do that.
Try to come up with different variables just to keep yourself sane.
But here, I have a function that takes now two integers, a and b respectively.
It just returns the sum of them so that I can now store the return
value of add in a variable called z.
And then, quite simply, print it out.
But there's one other thing I can do here.
Now, if we think about design, even if you've never programmed before,
do I really need the variable z?
Because I'm defining it on line 11, and then I'm quickly using it on line 12,
and that's it?
Like, sometimes you don't need variables.
And they might make your code more readable.
But strictly speaking-- and this is just kind of like substitution in math--
if z is the same thing as "add (x, y)," well, let me go ahead
and just delete line 11 altogether.
Let me get rid of mention of z.
You can actually get away with doing this.
And much like the Join block in Scratch, where I kind of overlaid it
on the Say block, kind of stacking them, you can stack functions in C,
or nest them really, kind of mathematically.
Honestly, it makes it a little harder to read
because your mind has to dive in conceptually deeper and deeper
into this second argument.
But it's perfectly acceptable, too.
And just to connect the dots to maybe something from high school,
this is kind of analogous to a function in math class
being like f of x, where f is some function name,
x is some arbitrary input to that function.
And when you start to put functions inside of functions
so that the output of one becomes the input to the next,
it's like using this syntax, f of g of x and so forth.
If you've never seen that before, don't worry.
But if you have, it's a way to connect some of these dots.
Any questions, though, on just this idea of now
having a function that doesn't just have a side effect
but instead has a return value?
Yeah, in back?
STUDENT: In our declaration of main, why did we
show it as returning an integer instead of void?
DAVID MALAN: In our definition of main, why did I do, what?
STUDENT: Why do we show it as returning an integer
instead of returning as void?
DAVID MALAN: Oh, a really good question that I was trying
to sweep under the rug for today.
But in every one of our programs thus far,
I have indeed said "int main(void)."
Technically speaking, whenever you write a program and it finishes running,
it actually returns a value somewhat secretly.
It returns the number 0 by convention, which means all is well.
And it can return any other integer if something goes wrong.
In fact, on your Mac, PC, or even phone, if you've ever
gotten like a weird message on the screen, like something went wrong
and it's like a weird numeric code, like error negative 129, or something
arbitrary like that, that tends to mean that some program running
on your Mac, PC, or phone had something go wrong with the main function.
And that is the number that was returned.
But that's more than we want to talk about today.
But we'll come back to this.
But main always returns a number.
By default, it is 0.
More on that soon.
All right, so with that said, let's actually tease
apart what it is we've been using underneath the hood
here a little bit by returning to VS Code's interface itself.
It turns out that all this time, even though I
keep alluding to macOS and Windows, which like 99% of us
are probably running on our laptops or desktops,
there's actually other very popular operating systems in the world,
among which is Linux.
So Linux is a very popular operating system,
the thing that turns on-- the thing that boots up
when you first turn on a computer.
And it's very commonly used for servers nowadays.
All of CS50's own servers run some version of Linux.
Those students more comfortable sometimes
run Linux on their own Macs or PCs even.
So Linux is a very popular operating system.
And it's particularly characterized by its textual interface,
its command-line interface, even though it also comes with graphical ones,
as well.
So again, this term we started today with, a graphical user interface
is a thing with menus and buttons.
It's literally what you and I use every day on our devices,
otherwise known as a GUI.
But today onward, you'll get more comfortable
with and more practice with this terminal window
down here, which represents a command-line interface, or CLI.
And just so you have a mental model of what's going on in the cloud here,
when you access cs50.dev, you are accessing this version of VS Code
in the cloud, a piece of software just running in a browser.
But that piece of software is automatically
connected to your very own personal server in the cloud, so to speak.
Technically speaking, it's a "docker container."
But it means that you have essentially your own mini server in the cloud
that only you have access to.
And that server or container is running an operating system called Linux.
And in fact, every time I've been running a command down here,
whether it's code or make or ./hello or anything else,
I've been running commands from here in Sanders Theatre on a server somewhere
in the cloud, my own Linux container or server.
And you'll have the same yourself.
This is the thing that we have pre-configured
for you by installing the compiler in so many other pieces of software
you'll soon see in the class.
But underneath the hood, then, of Linux is a soon-to-be familiar environment
that allows you to run different types of commands.
And those commands include things like this.
And this is something you'll develop muscle memory for over time.
But I wanted to give you a sense of some of the most popular textual commands
because we're essentially about to take away
muscle memory you have from a GUI world and have you type out words that
represent double-clicking on things, dragging on other things,
and other such commands that you and I take for granted.
It'll be a little painful at first in the first days or weeks,
but it will make you far more productive long term so that even after CS50,
if you start using your programming skills in some other domain, class,
or real-world job, you'll just be a lot faster at the keyboard
and able to do more work more quickly.
So with that said, let me go back to VS Code over here.
I'm going to go ahead and open up my File Explorer over here.
And you'll see at left all of the files that I've
created thus far in class and all of the programs
that I've compiled thus far in class.
I also have this source 1 directory, which
you can download from the course's website, which has all of today's code
pre-written in advance, so you don't have to type everything
that I literally type.
But all of these files are things that I've created.
And you'll see that in white are the C files.
And grayed out are actually the binary files, the machine code
that I created that I was running.
So you can click on any of these files in VS Code to open them.
For instance, here is hello.c.
And voila, it opens in the text editor.
But if I try to open "hello," that's not going to work,
because that's zeros and ones.
And frankly, the computer could show me all those zeros and ones,
but it's just not going to be useful.
And honestly, it's too easy to make one mistake and break the whole thing.
So instead, VS Code says that it can't display the text, because it's binary
or maybe unsupported more generally.
So know that you want to only click on the .c files when writing C code.
But let me go ahead and do something else.
Suppose that I decide that, wait a minute, we're nearing the end of class.
And we're not done yet, but what if I want to change hello.c to goodbye.c
or if I want to change meow.c to woof.c and turn it into a dog?
Well, let's actually do that.
I could go over here and right click or Control click on the file,
just like on a Mac or PC.
I can find the Rename option.
And I can do it all via the GUI.
But you should get more comfortable using commands like these here.
And among the commands on this list are "mv" for move, a.k.a.
rename.
So for instance, if I want to change meow.c to be woof.c instead,
I literally type "mv" space, the original file name, space,
and the new file name.
So this is very similar to what I've already
been doing with the code program or the make program.
I not only type the name of the command but also the thing
that I want to code or the thing that I want to make.
In this case, I type the thing that I want to move from old to new.
Now, if I hit Enter in a moment, watch on the left-hand side,
meow.c in the GUI should automatically change
even though I'm doing this all via the command-line keyboard interface.
And now it becomes woof.c.
I mean, it's not all that exciting.
But this is just to say that they are one and the same.
One is a GUI, one is a CLI, but it's the same exact thing.
Moreover, let me go ahead and close now the GUI
at left, the so-called explorer.
And in my terminal window alone, now I'm kind of out of my element,
like wait a minute, what was the file I created earlier?
Well, there's other commands as well.
On this list is, coincidentally, "ls," which lists
the file in your current folder.
And as you might have gleaned here, "mv" for move, "ls" for list,
CS people like to be succinct, terse, and type
the minimal number of keystrokes.
That's why these are all abbreviated commands instead of full words.
But if I go back to my terminal window and type "ls," voila,
there is exactly the same contents of my server but displayed textually.
And there's some heuristics here.
In green with an asterisk is all of the programs
that I made with make that are executable.
So the asterisks just means this is executable with dot-slash.
Meanwhile, the source 1 directory, which only I have because I downloaded it
in advance, has a slash to indicate that it's a folder instead of a file.
But all of these white files ending in .c we created together here today.
Now, what if I really am embarrassed by my very first program, hello.c?
Well, I can very destructively go and use the rm command for remove.
And rm hello.c is going to prompt me, a little cryptically,
"remove regular file 'hello.c'?"
And amazingly, this rm program has code just like we wrote earlier for agree.c,
where I can type 'y' to delete it.
I can type 'n' not to delete it.
But let's delete it.
Let's go ahead and hit y, enter.
Nothing seems to happen.
But in general, that's a good thing.
But if I type "ls" again, notice what is now missing?
And in fact, the list is a little shorter.
So it's one line instead of two.
"hello.c" is now gone.
Now, if you do that, there are not easy ways to get the file back.
So don't do that unless you really want to.
But there are backups maintained of these files, as well.
Well, what else is there, too?
Well, there's all of these other commands.
And you'll experience them over time.
Like, "cp" is copy.
"mkdir" is make directory. "rmdir" is remove directory.
And for instance, let me just show you one folder.
If I type "ls," there's that source 1 folder
that I claimed I downloaded in advance.
If you want to see what's there, you can type "cd"
for change directory, source 1, enter.
And voila, notice that your prompt has now changed.
And let me clear the screen.
Just as a visual reminder of where you are, you can see before the dollar sign
now the name of the folder that you're inside of.
So in Mac or Windows, you'd see obviously a graphical folder.
Here, you just see a little textual reminder of where you now are.
And if I type "ls," you'll see that I wrote a crazy number of files
before class.
And each of these represents different versions of the files
that we've been coding here in real time that I usually
have printouts of just to go through things in series
so you have copies online, as well.
So in short, all of these commands, if you've never used them before,
they will soon become like muscle memory.
And they do the most basic of operations.
But there will be other commands that we'll see over
time that do even much more than that.
But let's go ahead now and solve some actual problems.
And it's no coincidence that we keep showing or alluding
to Super Mario Brothers in some form, an older game
from the Nintendo Entertainment System, that allows you ultimately
to have this two-dimensional world, where Mario moves up and down
and side scrolls from left to right.
But you'll see we can distill even some aspects of "Mario"
into some fairly representative programming problems.
And in fact, let me propose that we consider this screen
from the original Super Mario Brothers.
So there's these four blocks in the sky, each with a question mark.
And if you click on one of these-- or if Mario jumps up
underneath each of these question marks, he
gets like a coin or something else that pops out.
Let's distill this, though, into its essence and consider in C, how can
we make, not a blue sky yet, not a green grassy hill, and so forth,
but how can we just make four question marks in a row,
because I dare say that we do have the building blocks via which to do this.
Well, the simplest way might be to go over here and run code of mario.c.
And then in mario.c, let's include some stdio.h so we have printf.
Let's do int main(void), as we keep doing.
And inside of main, let's keep it super simple--
1, 2, 3, 4, backslash n.
Doesn't get much simpler than that.
This is not going to be the prettiest of games.
But if I make Mario now, ./mario, I get my four question marks in the sky.
All right, so it's not all that interesting.
But this is clearly a candidate for what type of programming feature.
STUDENT: Scratch.
DAVID MALAN: Not to Scratch, though Scratch would make it more interesting.
yeah?
STUDENT: A loop.
DAVID MALAN: So some kind of loop, right?
So print the thing out iteratively instead.
So let me do that.
Instead of just printing this out all at once,
let me go ahead and remove this and do for int i gets zero; i less than 4;
i++.
And then in here, let me go ahead and print out just one question mark
instead.
And now let's run this.
So "make mario" to recompile it, ./mario.
And does anyone not want me to hit Enter yet?
Why?
STUDENT: Because it's gonna print a new line.
DAVID MALAN: Yeah, it's going to print out a new line every time.
So notice it's four question marks, but there each on its own line.
All right, well, let me fix this.
It's obviously because of the backslash n.
So let me remove that.
Let me rerun make mario, ./mario.
And it's better in one way but worse in another.
So wait, but now the dollar sign is doing
that thing where it's on the same line, which
just looks stupid if nothing else.
So how can I fix that?
Yeah?
STUDENT: [INAUDIBLE]
DAVID MALAN: Yeah, so logically, we don't have that many building blocks
today.
It's a lot of new syntax, but it's not that many new ideas.
Let's just use printf to print out literally one and only
one of these backslash n's, but outside of the loop so
it happens after all four of those have been printed.
All right, let me do make mario again, ./mario.
And OK, now we're back in business.
So sort of silly syntactical details, but if you
reduce the problem to its essence, it should hopefully, logically,
become clear over time.
All right, well, how about not just something like that but vertical?
Well, we've done something vertical already.
And so I can imagine we could change the program
to very simply print out three bricks instead of four question marks.
But what if we consider a two-dimensional world?
And later on in this game if you go underground,
everything looks like this with lots of bricks.
And let me propose, for the sake of discussion,
that this big wall here is like a 3-by-3 grid of bricks.
So it's not just a single brick.
It's like three by three, or nine total.
Now things get interesting.
And let me go back to mario.c.
I could take the easy road out and just say, all right, well,
let's printf, how about 1, 2, 3, backslash n, close quote.
And then, OK, let me just copy/paste.
And I'm using hashes instead of the actual bricks.
But aesthetically, it's pretty close.
Let me now go ahead and "make mario" again, ./mario.
And it doesn't quite look like a square.
But that's just because the hashes are a little taller than they are wide.
But it is correct, but not well designed.
So here, too, what would be better designed than just hardcoding, typing
literally all of these hashes?
Yeah?
STUDENT: We could use maybe two loops.
DAVID MALAN: Interesting, two loops.
And why two loops instead of one?
STUDENT: Oh, wait, nevermind.
Well, I was going to say you could do it one for vertical and one
for horizontal.
DAVID MALAN: OK, it's the right instinct.
So one for vertical, one for horizontal.
And even though these predate most of us,
old-school typewriters you might know or might
recall that if you feed a piece of paper into it, you can print like line,
then it scrolls, line, then it scrolls, line, then it scrolls.
This is kind of how the terminal window works, too.
You can print rows and columns, but you have
to print one row at a time, one row at a time, one row at a time.
It's not easy, but it is possible to go backwards and go up and down.
But just going row by row by row is more typical.
So how can I do this?
Well, I could use at least one and maybe even indeed two loops.
And this is where we're just now composing different ideas
from today and even last week.
So let me go ahead and say, for int i gets 0; i less than 3--
for a 3-by-3 grid--
i++.
And now let me cheat slightly.
Let me print out just three of these here, and that's it.
So I'm kind of cheating.
I'm printing out rows dynamically, but I'm still printing three columns all
in one breath.
But let's see what happens.
Make mario, ./mario, and it does work.
But what if you said, no, I want 4 by 4 or 5 by 5 or 6 by 6?
Now I have to change the 3 to a 6, and I have to add another three hashes here.
Things get messy if we don't do this mathematically.
So let me now do this instead.
Why don't I go ahead and print out every row at a time.
But for each row, let me use another loop to decide, like, rat-a-tat-tat,
from left to right, how many do I want to print.
So to do this, I could do another for loop.
I could call this variable something different. j is pretty common.
We start at i, we go to j.
If you go past k, maybe l, you're probably doing something wrong.
You don't want nested, nested, nested loops, but two is OK.
j equals 0; j is less than 3; j++.
And then here, I can print out a single one of these and no new line.
I don't want to screw up like I did before.
So I'll just do one.
Let me go ahead and do make mario now, ./mario.
But when I hit Enter, this is not correct yet.
What's it going to look like?
STUDENT: A single line?
DAVID MALAN: A single line of nine hashes, I think,
because I never used a single backslash n.
So that looks wrong.
So between what line number should I insert a printf of backslash n?
Let me look a little farther back if I can.
How about over here?
Yeah?
STUDENT: 10 and 11.
DAVID MALAN: Between 10 and 11.
So I'm going to go in here.
I'm going to add printf, quote, unquote, "backslash n" semicolon.
Let me go back and recompile mario--
./mario.
And crossing fingers-- voila, perfect.
I printed out now a 3-by-3.
Now, it's correct.
It's not, if we want to be really nitpicky, maybe still not
the best design.
Where am I perhaps repeating myself?
Yeah?
STUDENT: [INAUDIBLE]
DAVID MALAN: Yeah, I mean, it's not a huge deal.
But now I have two, people would call these magic numbers.
"Magic" in the sense of, where did that come from?
You just randomly put it in the middle of your code.
And you also put the same thing here.
Now I have to make sure I don't screw up and make one change but not the other.
So it turns out we can factor these out.
I can actually do something like this, int n equals 3.
And then I can just change this to n and this
to n, which is marginally better because now
I only have to change n in one place if I want
to make this thing bigger or smaller.
It's still going to work the same.
So make mario, ./mario.
There's our 3-by-3.
But if I want to make a 5-by-5, let me change the n to 5, rerun make mario,
./mario.
And now it's a bigger grid, 5-by-5.
But this is a little fragile.
And it turns out there's another trick we should introduce.
It turns out that C supports what are called constants,
whereby if you have a variable that you want to exist because it's useful
but you don't want to accidentally change it,
or if you're working with a partner in class or a colleague at work,
you don't want your partner or colleague to accidentally change
that value with their own code, you can go into your code and tell C,
this is actually a constant integer, a const, so to speak.
And this will just prevent you or someone else
from doing something stupid by accidentally changing it elsewhere.
The code is still going to work the same, ./mario,
but you won't be accidentally able to change it very easily to something
else.
And honestly, what we've now done, too, is set ourselves
up to make this more dynamic.
Let me go up here, and let me add the CS50 library so that we have access
to get_int because now we could do something
fancy like ask the get_int function for the size of this brick wall.
And then we can use n dynamically.
So for instance, let me increase the size of my terminal, make mario,
./mario, size 3.
Gives me a 3-by-3.
./mario size 5 gives me a 5-by-5.
./mario, how about 50, gives me a crazy big one, but it's all dynamic.
And now I don't have to even change the code.
It just now works.
As an aside, if you're wondering how I type so darn fast,
sometimes it's just because I'm hitting the up arrow.
It turns out that Linux will remember, if you
configure it this way, all of your previous commands.
So if you hit up, up, up, I can go through the past couple of hours
of commands that I've typed, which is useful sometimes--
not for hours of commands but the past few--
just to save yourself some keystrokes.
And another trick in a terminal window is to do this.
If I do ./ma and I get bored and I don't want to type out "rio,"
I can also just hit Tab, and it will autocomplete based on the characters
that do match.
So those kinds of tricks, too, will save you time over time.
But let's do this.
It's kind of broken, arguably, if I do this.
How about "cat?"
All right, well, that works.
That prevents me from doing something stupid
because get_int only accepts integers.
But it will accept 0, which does nothing.
It will accept negative 1, which does nothing.
And that's not bad.
It's not doing something weird.
But it would be nice to catch that and force the user
to give us a positive integer instead so we at least
see something on the screen.
So let me go back into my code, and let me propose that now
that we have the CS50 library, why don't we do something like this?
I'm going to change this.
I'm going to get rid of the constant just in case
the user needs to type it again.
And what if I do this?
While n is less than 1--
so if it's 0, negative 1, negative 2, or whatever, let's go ahead
and again ask the user for an int, and ask them for the size again.
And therefore, only once n is not less than 1 will
this loop break out and will proceed with the rest of the code.
So now let me try this.
Make mario, ./mario 0--
didn't like that.
Negative 1-- didn't like that.
Negative 2-- didn't like that.
3-- it did like that.
So using a loop now, I can ensure that the human is providing me
with input that I actually want.
So this is correct.
But I dare say 6 through 10 could be done better.
Why is this poorly designed instinctively?
Yeah?
STUDENT: There's repetition.
DAVID MALAN: What's the repetition, to be clear, what lines?
STUDENT: Lines 6 and 9.
DAVID MALAN: 6 and 9.
OK, so they're literally the same, and that's generally not a good thing.
And maybe I could change this one to remind the user like, hey,
that's not a positive number.
So you might want to customize the message.
But just having copy/paste here for the most part is not a good thing.
So it turns out-- and there's just one feature of C we wanted to introduce you
to today-- it turns out there's one other way that would actually help us
eliminate this redundancy of using get_int twice and particularly asking
literally the same question-- size--
twice in duplicate.
So I'm actually going to go into my code here,
and I'm going to delete the loop as we've written it thus far.
And instead of using a while loop, I'm going
to introduce instead something that we typically
call a do while loop, which is a little bit different.
Indeed, we begin with the keyword "do," and then
inside of the curly braces, what I'm going to do here
is that thing I might want to do once and more times thereafter.
So for instance, I'm going to say n equals get_int quote, unquote, "size."
And then at the bottom of this block of code, then
I'm going to use the keyword "while," as well as parentheses as always
for a Boolean expression.
And here.
I'm going to ask the question, do this while n is less than 1.
But there's one fix I still need to do here because notice on the current line
8, I actually haven't given n a type.
I haven't declared n yet.
But it would not be correct to declare n here, inside of that do block.
But why might that be?
Why would it not be a good thing to declare n inside of these curly braces?
Yeah, so recall that this is an issue of scope.
Recall that the scope of a variable is generally
confined to the most recently opened curly braces in which that variable is
declared.
And so if I declare this variable on line 8,
I'm not going to be able to use it on line 10.
But there is a fix, even though it might look a little strange.
I'm going to go above my do block here.
And before I go into this loop, I'm actually
going to declare n to be an integer, but semicolon, end of thought.
I'm not going to bother giving it a value, because I
know logically I'm going to end up giving it a value anyway now on line 9.
And so what's different about this version of the code
is that the do while loop ensures that we prompt the user for input at least
once.
And then while that input is not what we expect, for instance less than 1, then
it's going to execute again, again, again.
And indeed, the semantics are just that.
Do the following while this Boolean expression is true.
So if I go ahead now and rerun make mario, compiles OK-- ./mario.
And now I'll go ahead and input something that's not correct, like 0.
But I'm prompted again.
I'll input something like negative 1, and I'm prompted again.
But if I go ahead and input, for instance, 10,
now, because that's a positive integer, I indeed get a 10-by-10 grid of bricks.
And there's one other thing we should introduce here, too, in C, too.
C supports comments.
And a couple of you have asked about this
if you come from other programming languages.
Suppose I want to remember what it is I just did with this program.
Let me go in between lines 5 and 6 here and do "// prompt user for positive
integer."
This is what's known as a comment.
And it's grayed out only in the sense that the compiler is not
going to care about this.
The computer is not going to care about this.
This is a note to self, like a sticky note in the context of Scratch.
And it starts with "//," which essentially tells the compiler ignore
this, this is for the human, not for the computer.
But this comment, so to speak, is a way of just reminding yourself, reminding
your colleague, reminding your TF what it
is a few lines of code are meant to do.
And now this comment might be print, and how about n-by-n grid of bricks?
And what's nice about comments is that theoretically you can get away with,
or someone else can get away with, just reading this comment
and then not even have to look at the rest of the code.
They can look at this comment and not have to look at the rest of the code
because you've described for them what it's meant to do.
Yeah?
STUDENT: I just had a question about the hashtag [INAUDIBLE]
DAVID MALAN: Sure.
STUDENT: That's for [INAUDIBLE]
DAVID MALAN: Correct, the hash sign in Python is a comment,
is not the same thing in C. In C, hash include means to include the library's
header files in that way.
Other questions on these here tricks?
No?
All right, so as promised, what is maybe C not actually good at?
Well, let me propose that we consider what's
actually inside of your computer.
At the end of the day, whether it's a Mac, PC, iPhone, Android, phone,
or some other computer device, there's something that looks like this.
And this is memory, otherwise known as RAM, or random access memory,
for reasons we'll get to in a few weeks.
But this is where data is stored.
This is where "hello, world" is stored.
This is where 1 and 2 and all of those numbers are stored.
Any data in your program is stored ultimately in the computer's memory.
And the most important takeaway for today is that all of us
only have a finite amount of memory in our devices.
You might have a high-end device which has a lot of memory,
but it's still finite, which means you can only
count so high with that device.
You can only store so many files with that device.
There are fundamental physical limitations even though mathematically,
theoretically, we should be able to count toward infinity.
So what are the implications for this?
Well, consider this.
In the world of numbers, as per week 0, if you're only using three digits--
and I've grayed out the fourth one just to make the point-- if you're only
using three digits, we can count from 0 to 1 in decimal, to 2, to3, to 4,
to 5, to 6, to 7.
And as soon as you count to 8, you technically, per last week,
need a fourth bit.
But if you don't have it, the number 7 might seem
to be followed by what number instead?
STUDENT: 0.
DAVID MALAN: 0.
The number overflows, so to speak, right?
You carry the 1.
But if there's no place to put the 1, because there's no fourth light bulb,
if there's no fourth transistor, if there's no fourth bit,
the lower bits, the zeros, are going to be mistaken for the number you
and I know is 0.
So integer overflow is a thing in computers
whereby if you don't have enough memory, if you count high enough,
the number will wrap around back to 0.
Or sometimes it will wrap around to a negative number,
depending on whether the code supports negative and positive numbers and 0
alike.
So that has some very real world implications
in integer overflow that's sort of a fundamental limitation of how
numbers are typically stored.
Now, thankfully, we typically don't store things
based on number of digits but number of bits.
And a bit is just a 0 or 1.
And recall from last week that a common unit of measure
is minimally eight bits, or a byte, but even more commonly is 32.
So for instance, here are 32 bits, all zeros.
And if you do out the math, this is the number
you and I know in decimal is of course 0.
But if I change all 32 zeros to ones, this is a really big number now.
If we're only using positive numbers, not negatives,
what number roughly is this, 32 ones?
It's roughly 4 billion in total-- roughly 4 billion in total.
Why?
Well, if you've got 32 bits, each can be two possible values, 0 or 1, that's
2 to the 32nd power, which is roughly--
I'll stipulate-- roughly 4 billion total.
The problem is, what if you want to count to 4,000,000,001?
That's a bit of a white lie.
It's not precisely that.
But what if you want to count just higher than that?
You'd need a 33rd bit because all of the others
are going to go to 0 at that point, and you might count from 1 to 2
to 3 to 4 billion back to 0, or worse if you're dealing with negative numbers,
too.
So the fact that there are finitely many bits used in computers is a problem.
And negative numbers do add a complexity because this is specifically
the 4 billion in question--
4,294,968,295.
That is as high as you can count with 32 bits
if you don't bother with negatives.
But if you want negative numbers, you've got to half
that because you've got to save half of them for negative, half of them
for positive, give or take.
And so if you're supporting negative numbers, as you probably
should for a calculator, for Microsoft Excel, Google Spreadsheets,
you can only count as high up as 2 billion roughly,
or negative 2 billion roughly instead.
So it turns out that when you are using data types in C,
you have some control over how many bits are actually used.
And this list is longer than we've covered today,
but we did talk about integers for a while.
Those are, by convention nowadays, 32 bits.
If that's not enough, you can upgrade your variables to longs, which
tend to be 64 bits instead, which isn't just twice as big as an integer,
it's actually 64 bits, which is exponentially more.
It's an unpronounceable number, at least for me.
That's a crazy big number, but it's available to you.
Moreover, we can see this if we actually are a little reckless with how
we're using code.
And just so you know too, though, there are
functions even in CS50's library that let
you use these larger values, get long of course, will get you a long.
And this one's a little non-obvious, but "%li" is the format code for printf,
just so you know, for printing a long integer and not just an integer.
So it's two characters instead of one.
Suppose, though, we actually want to use code involving some large numbers.
It turns out that certain bad things can happen.
So let me go ahead and do this.
I'm going to go back over to VS Code here,
and I'm going to modify my calculator to do something that, at glance, should
be perfectly reasonable.
Let me go ahead and open up calculator.c, as before.
And where we left off, we had this add function.
And you know what?
I'm going to simplify it back to its very original version.
I'm going to go ahead and get rid of the add function
and just distill it to its essence, which is not to add any more.
But let's just do division.
I want to print out this time maybe x divided by y.
So here we go. x divided by y is a nice simple program in my calculator.
Let me do make calculator again, ./calculator.
And let's divide something like 1 divided by 3, which should--
hm, OK, weird.
It gave me 0 instead of probably 0.3333333,
as you might have expected for 1/3.
So what might the takeaway there be?
Why am I seeing zero perhaps?
Yeah?
STUDENT: If 0's the integer [INAUDIBLE],, then if you want decimals [INAUDIBLE]..
DAVID MALAN: Yeah, so 0 is an integer.
And indeed, that's what I'm telling the thing to print.
And in fact, if we go over to my little cheat sheet here of format codes,
I'm currently using %i.
I should actually, when I do division of numbers that might have floating point
values, a decimal point that floats left to right,
otherwise known as a real number, I want to use %f for float instead.
So I'm actually going to go back to my code here.
And let's try this-- %f instead of %i.
And let me go ahead and "make calculator."
Huh, all right.
Well, this didn't work then.
"Format specifies type 'double,' but the argument has type 'int.'
All right, so that, too, is not quite working.
So I think I actually need to make a change here further.
Let me actually go ahead and do this.
It turns out that besides integers, there
are these things called floats, and also doubles.
A float uses 32 bits, and a double uses 64 bits.
And that doesn't necessarily mean you can
count higher as much as it means you can have
more numbers after the decimal point.
So if you want a more precise value, you throw memory at it
by using a double and 64 bits instead of a float.
But we'll keep it simple, and let me go ahead and do this.
Let me just do the math using the type of variable that I should be here.
Let me do not int, but float z equals x divided by y.
And now let me go ahead and print out the value of z.
Strictly speaking, I don't need the variable.
But I'm trying to be pedantic and actually use a float explicitly
this time so we see a real number.
All right, let me go ahead and do make calculator, ./calculator.
1 divided by 3 equals--
damn, now it's just showing me more zeros, which clearly isn't the case.
Well, this is because of an issue that we'll generally call truncation.
So truncation is just a term of art that means if you take an integer
and you divide it by an integer, even if you get a fractional value,
the fraction just gets thrown away because you're only
doing integer-based math.
So if there's anything after the decimal point,
it just gets truncated, literally discarded.
So what should 1 divided by 3 be?
Obviously, 0.33333333-- ad nauseum.
Fortunately, you throw away everything after the decimal point,
which leaves you still with just 0.
And even though I'm seeing more zeros, that's because we threw away all
of the 3's.
That is just what happens when you use integers and do
any kind of division like that.
But there is a solution.
We can actually convert, or cast, integers to floating point values.
So we can tell C, I know this is an integer now.
But go ahead and treat it as though it has a decimal point, even
if it's .0 At the end of the number.
So I can go into this, and I can use parentheses and literally write
"float" in parentheses.
And over here for y, I can literally use parentheses and convert y to a float.
The term of art here is type casting.
You're converting one type to another effectively, or technically treating
one type as though it's another even if it doesn't necessarily
have a mathematical impact.
But what it means now is that z will be defined
by dividing one float by another.
So truncation will not now happen.
So let me do make calculator, ./calculator.
And now 1 divided by 3, there it is.
Now the math is actually correct.
But my God, we had to jump through hoops just to get this to work.
So to be clear, the two issues we-- well, the issue we encountered
was truncation.
If you divide an int by an int, you will get an int no matter
what it should be mathematically.
But if you instead type cast the values, the variables to a floating point
value, or a double for that matter, then a float divided by a float will give
you a float and preserve all of those 3's.
But here's another catch, or at least a limitation potentially with computers.
What if I go ahead here and do--
let me do this.
Let me show you one trick here, even though the syntax is a bit weird.
Instead of printing out %f alone, let me print out % dot,
maybe 5f So this is weird syntax.
And it's only specific to printf.
"%.5f" means 'show me five decimal places specifically.'
So if I do make calculator, ./calculator, 1, 3, voila,
I get five decimal places.
If I want six, let's do this.
I'll change the code to 6.
Make calculator, ./calculator, 1, 3, and now I get six 3's instead.
All right, well, wouldn't it be nice to be even more precise?
Let's give me 20 significant digits after the decimal point.
So make calculator, ./calculator, 1 divided by 3, and-- woo.
So your middle school teacher seems to have lied to you at this point.
1 divided by 3 is apparently not 0.33333 with a line over it,
or just infinite number of 3's.
OK, that's not quite the right conclusion, though.
Oops.
Why might I be seeing these weird numbers instead
of just lots of 3's, intuitively?
[INTRIGUING MUSIC]