DAVID J. MALAN: All right, this is CS50 and this is the
end of Week 1, so study cards are in and the total number of
classmates that you have this year is 745.
And we're so thrilled to have so many of you in the course
this semester, but even though this course is so large,
realize that it's these things like Friday lunches, like
office hours, 15-person sections, and so forth that
are really meant to create this more intimate
environment.
So even though we may gather once or twice a week, here in
Sanders, know that much of your experience in this course
will very much be hands-on on alongside of your classmates
and the teaching staff.
So here we go, Fall 2012.
So recall that last time we ended on a note of
imprecision, talking about the representation of numbers.
And we looked at one of the failures that happened, in the
real world, when it came to misunderstanding
how computers work.
Well, today we'll revisit another one of those ideas as
well as continue our discussion of the CS50 client
and C, and what it means really to program.
But first, just a couple of whirlwind announcements.
Sections begin this Sunday, and the first week is always a
bit of a rapid start, because we just got in
your section forms.
We're doing the same, now, with the teaching fellows, now
that they know their schedules, so we're aiming, by
Saturday morning, to follow up with you with your section
assignment and TF.
Realize that they'll start the next day, Sunday
or Monday or Tuesday.
And then we will accommodate section changes as they arise
in the days to come.
Office hours, meanwhile, are, again, tonight, 8 PM to 11, in
Annenberg, as well as tomorrow night.
Even though the problem sets are generally due on Thursdays
at noon, realize you have up to five late days to spend,
hence the availability of office hours on Thursdays if
you cash in one of those late days and, thereby, submit
something on Friday.
Let me suggest that Scratch is among the more fun, among the
more low key problem sets.
And I would hang onto those late days, strategically, just
for later in the semester when life starts to get in the way
with midterms and other classes.
Problem Set 1, meanwhile would be posted on the course's
website this Friday, as will be at the course's walk
through video, which will be filmed at 2:30PM on Friday,
and then posted online by the next day.
So we ended on Monday.
I want to, actually, one note here.
Because the classes- because Sanders doesn't exactly lend
itself to casual Q&A back and forth terribly well, we're
going to try something a little new this year, whereby,
if you're not comfortable raising your hand or you just
would rather not raise your hand in a room like this,
we're going to have some of the teaching fellows and CA's
manning cs50.net/discuss during lecture.
So if you have a question, because I was completely
unclear about something, or you're curious about some
topic, try posting it there.
Tag it with a label of Lecture, and we'll do our best
today and next week-- we'll try this out-- to field it
either electronically, online, or if it feels like we should
really address it en masse, one of the teaching fellows or
CA's will raise their hand and ask
anonymously on your behalf.
So we'll give this a try and see how well this works.
But, by all means, continue to raise hands as you would like.
So last time we revealed that 10% or one-tenth, is not, in
fact, 0.1, like you were taught many years ago.
It's in fact, a value like this.
But that's not wholly true, right?
We just have this constraint in computers whereby, if you
only have a finite amount of memory, RAM or more
specifically, bits, well, there's only a finite number
of things you can represent.
For instance, if we have the number zero, and we want to
represent in binary, using eight bits, where a bit,
again, is a zero or a one, we might
represent it as 00000000.
And that's a little unnecessarily verbose.
Just like in the decimal system in the real world,
generally, if you want to write the number 123, we
humans probably don't write 00000123, even though,
functionally, that's the exact same number.
We just drop leading zeros, so to speak.
In binary, we can do the same.
Binary and zero, zero and binary is just zero.
You only need a single bit.
But again, the most common unit of measure in the world
of computing is bytes, eight bits.
Just because one bit, pretty useless.
Eight bits, not terribly useful, but at least it's more
useful than a single unit of measure.
So with eight bits, we'll typically represent binary.
So here we have the numbers zero, one, two, and then, as
Nate did in our video last Wednesday, we can continue
counting up to seven, to eight.
And then if we use more and more bits, we
can count up to infinity.
But if you only have a finite number of these bits, as is
the case in any computer system, or even missile
system, well, indeed you only have a
finite amount of precision.
So if you want to represent the number like 0.1, well, the
computer has to pick and choose.
And if it only has a finite number of numbers it can
represent, it can come close to 0.1 for you, but it can't
necessarily give you exactly the value that you want.
And this is just one of the challenges of computers,
underneath the hood, thankfully.
As we move farther in the semester, these lower level
details become much less interesting, but certainly the
application of these ideas and have some very real world
ramifications.
One is a bit fun.
How many of you have seen the amazing movie, Office Space?
If you haven't, that's your unofficial
homework for the week.
But in Office Space, the fellows in that movie take
advantage of precisely this idea, but they actually stole
it from another movie that you might have
seen, even years prior.
So let me give you a few seconds of this trailer from
this amazing movie and this is very much
academically relevant.
[PLAYS MOVIE]
Actually I'm being promoted.
I can program a virus that will rip that
place off big time.
Well, how does it work?
Every time there's a bank transaction where interest is
computed, there are thousands a day, the computer ends up
with these fractions of a cent.
But I'm not going to do anything illegal.
Illegal, Samir, this is America.
You have to swear to God.
If nobody knows about this but us, all No family members, no
girlfriends, nobody.
[INAUDIBLE].
Don't worry, man, I won't tell anyone either.
[MOVIE ENDS]
All right, so maybe I found this movie better than most
people here, but in any case, the virus that they were
trying to create--
and in the movies, anytime they'd call something a virus,
it's generally not actually a virus.
It's just a program that someone wrote to do something.
And in this case, the guys were trying to write a program
that took advantage of their company called Innotech's
computing system, whereby computers, too, even when it
comes to money, can't necessarily
represent money precisely.
So even though you might have $0.10 in your bank account,
0.10, well, the computer might actually think that you have
this many cents in your bank account.
And so what these guys were aspiring to do-- and they
stole the idea, it turns out from Superman III, where
Richard Pryor did the same thing, they were essentially
writing programs that aspire to take all of the trailing
numbers, all of the tiny, tiny, tiny fractions of
pennies, and siphoning those off so that the humans just
thought, oh, it's only $0.10 there, but all of those
trailing numbers eventually add up.
So a wonderful idea and it backfired hilariously in that
particular film.
But in the real world, too, there are much more concerning
implications of these sorts of things.
And one of the reasons to dive into the field of computer
science is, again, its applicability, not to the
engineering sciences alone, but just to
other fields as well.
So this is a less lighthearted look at what can go wrong, but
it's also quite eye opening, I think, when it comes to
understanding exactly how omnipresent technologies like
computers and programming or these days,
in the form of software.
So this next clip is just about six minutes, continues
where we left off last time, but allows us to look a little
more in depth at one such of these issues.
Let me skip ahead to where we left off,
which was right here.
And we have part two of two, for about six minutes here.
[PLAY MOVIE]
Plagued modern rocket technology.
In 1991, with the start of the first Gulf War the Patriot
missile experience a similar kind of
number conversion problem.
And as a result 20 people, 20 American soldiers were killed
and about 100 others wounded, when the Patriot, was supposed
to protect against incoming scuds,
failed to fire a missile.
When Iraq invaded Kuwait, and America launched a Desert
Storm in early 1991, Patriot missile batteries were
deployed to protect Saudi Arabia and Israel from Iraqi
scud missile attacks.
The Patriot is a US medium range surface to air system,
manufactured by the Raytheon company.
The size of the Patriot interceptor itself, it's about
roughly 20 feet long and it weighs about 2,000 pounds.
And it carries a warhead of about, I think, it's roughly
150 pounds.
And the warhead itself, is a high explosive which has
fragments around it.
The casing of the warhead is designed to act like buckshot.
The missiles are carried four per container and are
transported by a semi trailer.
The Patriot anti-missile system goes back at least to
20 years now.
It was originally designed as an air defense missile to
shoot down enemy airplanes, in the first Gulf War.
When that war came along, the Army wanted to use it to shoot
down scuds, not airplanes.
The Air Iraqi Force was not so much of a problem but the Army
was worried about scuds.
And so they tried to upgrade the Patriot.
Intercepting an enemy missile traveling at MACH 5 was going
to be challenging enough.
But when the Patriot was rushed into service, the Army
was not aware of an Iraqi modification that made their
scuds nearly impossible to hit.
What happened is the scuds that were
coming in were unstable.
They were wobbling.
The reason for this was the Iraqis, in order to get 600
kilometers out of a 300 kilometer range missile, took
weight out of the front warhead and
made the warhead lighter.
So now the Patriot's trying to come at the scud, and most of
the time, the overwhelming majority of the time, it would
just fly by the scud.
Once the Patriot system operators realized the Patriot
missed its target, they detonated the Patriot warhead.
To avoid possible casualties, it was allowed
to fall to the ground.
That was what most people saw, as big fireballs in the sky,
and misunderstood as intercepts of scud warheads.
Although, in the night sky, Patriots appeared to be
successfully destroyed scuds, at Dhahran, there could be no
mistake about its performance.
There, the Patriot's radar system lost track of an
incoming scud, and never launched due
to a software flaw.
It was the Israelis who first discovered that, the longer
the system was on, the greater the time discrepancy became,
due to a clock embedded in the systems computer.
About two weeks before the tragedy in Dhahran, the
Israelis reported to the Defense Department that the
system was losing time.
After about eight hours of running, they noticed that the
system was becoming noticeably less accurate.
The Defense Department responded by telling all of
the Patriot batteries to not leave the systems
on for a long time.
They never said what a long time was, eight hours, 10
hours, 1,000 hours.
Nobody knew.
The Patriot battery stationed at the barracks at Dhahran,
and its flawed internal clock, had been on over 100 hours on
the night of February 25th.
It tracked time to an accuracy of about a tenth of a second.
Now a tenth of the second is an interesting number because
it can't be expressed in binary, exactly, which means
it can't be expressed, exactly, in any
modern digital computer.
It's hard to believe.
But use this as an example.
Let's take the number, one third.
One third cannot be expressed in decimal, exactly.
One third is 0.333 going on for infinity.
There's no way to do that with absolute accuracy in decimal.
That's exactly the same kind of problem that happened in
the Patriot.
The longer the system ran, the worse the time error became.
After 100 hours of operation, the error in time was only
about one third of a second.
But And in terms of targeting a missile traveling at MACH 5,
it resulted in a tracking error of over 600 meters.
It would be a fatal error for the soldiers at Dhahran.
What happened is a scud launch was detected by early warning
satellites and they knew that the scud was coming in their
general direction.
They didn't know where it was coming.
It was now up to the radar component of the Patriot
system, defending Dhahran, to locate and keep track of the
incoming enemy missile.
The radar was very smart.
You would actually track the position of the scud, and then
predict where it probably would be, the next time the
radar sent a pulse out.
That was called the range gate.
Then, once the Patriot decides enough time has passed to go
back and check the next location for this detected
object, it goes back.
So when it went back to the wrong place, it then sees no
object, and it decides that there was no object.
It was a false detection and drops the track.
The incoming scud disappeared from the radar screen.
And seconds later, it slammed into the barracks The scud
killed 28 and was the last one fired during
the first Gulf War.
Tragically, the updated software arrived at Dhahran
the following day.
And the software flaw had been fixed, closing one chapter in
the troubled history of the Patriot missile.
PATRIOT is actually an acronym for "Phased Array tracking
Intercept of Target."
All right, so, a much more sorrowful outcome of not quite
understanding this world.
And the takeaway, really, from the film, is that the solution
there was, reboot your missile defense system
every once in a while.
And so, again, onen of the aims of this course, certainly
on a more humble scale than these applications, is to
really open everyone's eyes to exactly how you go about
making machines do what you want them to do and how you go
about doing that correctly, while along the way doing it
well, elegantly, and so forth.
And so today let's dive into a little bit more of this, but
so that we don't go too fast, particularly for those less
comfortable, but also so that we can retain the interest of
those more comfortable, let's just quickly begin with a bit
of recap of some of these ideas.
And again, ask away, both in person or online, if you have
questions, So we can keep everyone on the same page.
So the CS50 appliance, quick step back, is
what, in your own words?
It's OK if you have no idea what those words may be yet.
[INAUDIBLE]?
OK, so a universal interface for--
that we can all share.
Absolutely.
So we could, for instance, tell you to go to some
website, download this software for Mac OS.
Download this software for Windows, configure in
this way and that.
But the reality is, then we run into innumerable technical
support issues.
And also, these days, much of what's being done server side,
in the world of computing, especially when it comes to
web development and web based applications, is actually
quite often done on Unix or Linux computers, as opposed to
Macs or Windows PCs.
So among the upsides, then, of using this thing called the
CS50 appliance is, we'll all have the same environment.
So everyone's on the same page no matter what hardware you
came to campus with, but also will be dabbling in exactly
the same environment that you'll be using toward
semester's end as well for web development, as well as in the
future for real world applications thereof.
So more concretely, the CS50 appliance is a free piece of
software that allows you to run another operating system,
Linux, in this case, on your own computer, in a window.
Now you can't just download the appliance.
You need a program with which to play or run the appliance.
And that piece of software is generally known as a
hypervisor.
Hypervisors come in the form of products like VMware,
Virtual Box, Parallels.
All sorts of companies make software, both free and
commercial, alike.
What you'll see in Problem Set 1 are instructions on how to
obtain free software with which to
run the CS50 appliance.
And once you do, you'll have, in a window on your Mac or PC,
a desktop environment that looks quite like this, where
it has a Start menu-like thing in the
bottom left hand corner.
It has a Home folder or Home directory in the top left
there, a trash can, and so forth.
So we'll tease apart the necessary details as we go.
But now let's start to dive into C.
So C is this programming language, similar in spirit to
Scratch, but far less graphical, but ironically, far
more powerful once you get savvy with it.
So in C, we write programs by writing source code, which is
this English-like syntax, similar to the Socks
demonstration that we did last time that allows you to
express yourself fairly precisely, if
arcanely, to the computer.
So what kind of product software do you actually need
to write programs in a language like C, based on our
conversation on Monday?
[INAUDIBLE]?
OK, you just need a text editor.
You don't need any special software
per se to write programs.
You just need a text editor like Notepad, on Windows,
TextEdit on Mac, or something called gedit, in the world of
Linux, which we'll use here.
And that's it, to write the programs.
But writing's only half of the equation.
You then need to run them.
But in order to run programs--
you can't run source code.
You instead, have to do what to it first?
Yeah.
DAVID J. MALAN: You need to compile it.
DAVID J. MALAN: You need to compile it.
And someone else compiling the source code means--
[INAUDIBLE]?
DAVID J. MALAN: Exactly.
Translating it to zeroes and ones.
Because all of us, these days, have computers that have CPUs,
almost all of which are made by a company called Intel, and
Intel Inside, means there's this brain inside of your
computer, known as the CPU.
And one of the things that that brain does is, it knows
how to understand patterns of zeros and ones.
It knows what pattern of bits represents addition, what
pattern represents subtractions, what pattern of
bits represent printing, and creating an audible sound.
So in short, the CPU, coupled with the operating system,
which is a piece of software that runs on top of the CPU,
so to speak.
Together, those two pieces know how to run programs that
we humans write.
So if I go into the CS50 appliance here, I need to open
up a text editor and I can access this
in a couple of ways.
I can either go to Menu, Programming,
and then I can select--
nope, not that menu.
I can go to Accessories/gedit here, to open
my little text editor.
Or more simply, I can click this little icon in the bottom
left hand corner.
And now I get this environment here.
It's pretty reminiscent of a simple text editor with just
one or two differences.
In the top, where the cursor is blinking now, this is where
I can start writing my code.
In the bottom is this thing called the terminal window.
And what kinds of things can I do in this
so-called terminal window?
[INAUDIBLE]?
OK so I'll run commands.
And some of the commands we ran on Monday were a little
cryptic at first, but LS for List, CD for Change Directory,
RM, for Remove.
So it's sort of an old school way of navigating your
computer, using only your keyboard and textual commands,
and not, generally, using a mouse.
But we'll see, before long, this actually gives us a
little more power and a little more precision with which to
express ourselves.
And then over here at the left, we're just going to see,
as we start writing software, a little summary of the
program that we've written at top left.
But we'll see that again before long.
So let's do something fairly simple here.
Let me first go ahead and go to File, Save and I'm just
going to click on jharvard here, over at left, and
jharvard is, again, the name for my home folder, my
personal files, me, now being John Harvard, as all of you
will soon be John Harvard in this environment.
He's got this Home directory, My Documents and so forth,
that I want to save, now, this file in.
So I'm going to call it Hello.C Then I'm going to go
ahead and click Save, and now what I have here is a tab
called Hello.C, so much like another type of editor.
So now let me go ahead and start writing a program.
int main (void).
And then I just did last time, print f, for Print Formatted,
hello comma world exclamation point close quote closed
parenthesis semicolon.
Almost complete.
But I'm missing a couple of details.
What is missing from this program that I really need for
it to actually compile?
Yeah.
[INAUDIBLE]?
DAVID J. MALAN: Yeah, so I need to include the standard
I/O library.
So this needs to go above Main, so I'm just going to
make some room for it there.
And I'm going to do Sharp Include standard I/O dot h.
And notice the angled brackets there.
And as an aside, gedit is a little user friendly, if
sometimes a little confusing, in that it tries to help your
eyes notice what lines up with what.
So the fact that my cursor is right next to this angled
bracket, notice how it's highlighting the other angled
bracket plus this one.
And that's just to draw my attention to the fact that I
have symmetry.
And symmetry in programming is generally a good thing.
Similarly, if I move my cursor to the end, notice how the
curly braces line up to show that, yes, I have an open one
and a closed one, so to speak.
All right, And I can do one other thing.
It's not strictly necessary, but good practice, to say
return zero.
And return zero does what for us here?
Or signifies what?
[INAUDIBLE]?
DAVID J. MALAN: But nothing's wrong.
And frankly it's hard for much to go wrong in a
program this short.
So I'm just being super explicit that all is well.
I, the human, when I run this program, will probably never
see that zero.
generally these numbers that are being returned from Main
only rear their heads to the humans when
something goes wrong.
And you get a little pop up saying, system error, one,
two, three, or whatever the number was that
was actually returned.
So anything else wrong with this?
[INAUDIBLE]?
DAVID J. MALAN: So this is not so much a functional mistake,
but an aesthetic one.
I should probably put in a new line character there at the
end of this string so that we actually move the cursor to
the next line, and the program just looks a little prettier.
So now let me go down to my terminal window
and zoom back in.
And this thing here, it's called the prompt.
It's just reminding me who I am, jharvard@appliance, where
appliance is the name of the computer I'm on.
In parentheses is this tilde, the squiggle symbol, which
represents what?
AUDIENCE: Home directory.
DAVID J. MALAN: Home directory.
So that's just a shorthand notation for saying you are in
your personal folder where all of your files go by default.
So now, in here, I can type LS for list.
And the only thing I see here at the moment is hello.c.
And that's good because I just wrote this.
So let me go ahead and do make hello.
And notice I'm not typing makehello.c.
Instead, I'm only typing the base name of the file, hello.
And Make as, we saw on Monday, will just infer that I want
the dot c file.
So now I've typed make hello.
Now I see this fairly long command and will continue
seeing this again and again.
But to be clear, clang, the word that just appeared,
that's the actual compiler.
Make is just a build utility.
It's just a more user friendly program that saves me, in the
long run, from having to type out incredibly tedious long
commands like that one.
So you could run Clang manually.
You could type all of that manually.
Just gets very tedious.
And so Make is just a program that simplifies our lives and
runs more complex commands for us.
All right, so now what file should I have
in my current directory?
Not just hello.c, but also, hello.
So if I want to run hello, recall that I say dot slash,
where dot means go into the current folder, wherever you
are, and then run the program called Hello, and indeed, now,
I have hello world.
All right, any questions on the workflow or the syntax of
what we just did?
All right, so let's take things up a notch again.
So this was underwhelming to just write a program that only
ever says, hello world.
So we changed it, on Monday, to say, hello, David.
It's a little more personal, but just as hard coded as the
first version.
So not all that compelling.
But there exists, in c, functions certainly besides
Print, functions that let you get user input.
And the way we did this, the other day, was as follows.
Before I actually print out something, I first got
something from the user.
So let me do this again.
String s, where s is just an arbitrary name, here, for a
string, equals get string.
So what was GetString?
It obviously gets a string, based on its name.
But what is it, more technically?
It's a function.
So it's a function that doesn't come with C per se.
CS50 staff wrote this.
And so to use this, you can't just include standard IO.h,
You also need to include what?
Yeah.
So CS50.h, which is just a file we wrote some years ago.
We installed it on the appliance for you alongside
all of the standard files.
And so in these first few weeks of the class, we'll use
this just to simplify the process of doing very basic
things like getting user input.
Because as we'll see in a few weeks, getting user input in a
language like C is actually surprisingly complex,
particularly because you don't know in advance, usually, how
much stuff the user is going to type in.
And when you don't know how much data to expect, you don't
know how much memory to allocate.
And if you actually have an adversarial user who's trying
to hack into your system, crash your computer, well,
generally the first method of attack is, just as I did on
Monday, type a whole long sequence of random characters,
hit Enter, and see what breaks.
Because generally, if a program breaks, that hints at
a security flaw, potentially.
It certainly hints at you, the programmer, having made a
mistake, but more dangerously, you might have made a
security-related mistake, and generally we'll see that
that's how machines are compromised to this day, both
in the world of the web and programs at the
command line, like this.
All right.
So string s equals GetString.
So more concretely now, what does line six do here?
So the function on the right gets string,
takes how many arguments?
OK.
So none.
A little sanity check.
An argument, again, is just an input to a function to alter
its default behavior in some way.
But in this case, I don't want to alter
the behavior of GetString.
I just wanted to get a string.
So I put nothing in the parentheses, and then I put a
semicolon demarking the end of the line of code.
Now, the equals sign doesn't mean equal, per se.
It means assignment, which means put whatever gets
outputted on the right inside of whatever is on the left.
So on the left, we say that we have declared a
string called s.
And more specifically, we've allocated memory inside of
which we'll put the bits that represents a sequence of
characters.
But for today, we have allocated
or declared a string.
All right.
So now once I've done this, I want to plug in the value of
s, not the value of David.
So this is wrong why?
So this is just literally the hard-coded s.
It's to say "Hello s," which is not what I want.
So I can do this.
The percent sign is a placeholder, but now I need to
pass into print a second argument.
Recall that arguments to functions are separated by
commas, so the next comma here I put after this the letter s.
And in this context now, s is the variable, and print s,
upon receiving those two inputs--
the string on the left and the variable name s on the right--
it will plug the ladder into the former [INAUDIBLE], and
just print out a nice beautiful string containing
whatever it is that we typed in.
So let's try this.
Going to go ahead and zoom out, back
to my terminal window.
Zoom in at the bottom here.
Retype makeHello.
Clang seems to have been rerun.
I'm going to type dot slash hello, Enter.
Nothing seems to be happening yet, but it's at this prompt
that I can type something like Nate, Enter, and now we have
Hello Nate.
And I can do it again with Rob, and so forth.
So hopefully now this program is behaving as I intend.
Now, not all that user friendly.
I've no idea what's being expected of me.
So we can certainly clean this up a little bit.
Let me go back up here, and instead of just diving in and
asking the user for a string, let me explicitly tell the
user what I'm expecting.
So print F, enter a string colon space
quote unquote semicolon.
So no variables here.
No percent signs.
Just the simple English phrase.
Let me now go to the bottom here and rerun my program.
But nothing seems to have changed.
Why?
I have to recompile it.
So easy mistake to make.
But you have to not only save the file, but
recompile the program.
And so if I re-run makeHello, now clang runs.
Now I can run dot slash hello, And now I see, enter a string.
OK.
Now it's a little more user-friendly.
Nate, Enter, Hello Nate.
Well, let's try this again and start thinking
about corner cases.
So just like in scratch, you're encouraged or are being
encouraged in the spec to kind of think about what could go
wrong if you don't anticipate everything the user might do.
Bugs, therefore, might ensue.
So enter a string--
what's a corner case here?
What's a scenario that I, the programmer, might not have
anticipated?
Yeah.
OK.
So what if I type in a number like this?
All right.
So it still works.
Gramatically makes no sense.
But at least the program works.
What's another corner case?
Something I didn't expect.
Yeah?
OK.
So we could do something like a really, really big number.
So let's do this here.
Let me zoom out for a second, let me highlight.
Can't copy and paste down here.
All right.
So I can't copy and paste in this terminal window, so we'll
just simulate it.
Let me zoom out, going to wrap.
I'm not going to do this for too long, because it won't
actually break in this program.
But it could.
Enter.
But it didn't.
All right.
But it's a genuine corner case.
And the only reason that behaved correctly, so to
speak, is because the CS50 function getString is actually
designed underneath the hood, as we'll see in a few weeks,
to automatically allocate more and more RAM, more and more
memory from the operating system, when it realizes, wow,
you've really typed in something quite long.
Now, this is a bit of a white lie.
If we actually paused for quite a while and I typed in
something like 5 billion characters at the keyboard
here, or really hit copy-paste quite a bit, we quite possibly
could cause the program to crash.
Just a little harder to simulate that with a finite
amount of time.
What's another corner case we could try?
Yeah?
Yeah.
So why don't we answer nothing?
So we actually have to take some action, because otherwise
the program's going to sit there blinking all day.
But if I just hit enter--
OK, now, still looks wrong.
Didn't crash, but maybe there's now an opportunity to
start implementing a more rigorous program
that actually checks.
And if the string, the sentence, the word that I
typed in has length 0, well, maybe I should yell at the
user, or tell him or her to enter a string again, so that
we actually get what looks like correct behavior and not
just complete oversight on my part.
Question in back?
Decimal number.
So we could try that, too.
1.10000005.
Seems like that's going to just work actually OK.
And that actually is expected.
Even though we might have typed that number there, or a
bigger number earlier, realize that when we're typing user
input and we're using the getString function, doesn't
matter if what I'm typing looks like a number.
Recall that according to ASCII, everything on your
keyboard has a numerical value that can be mapped to a
character, a char.
So in this case, even though I might type a number, the
computer is going to think of it, so to speak, as a string--
something that looks like a number, but is
not actually a number.
But that's actually a perfect segue, because now we can
actually transition to writing programs that
use other data types.
So besides using chars, or rather, besides using strings,
realize that we have other data types in C as well as in
other languages.
A char, as the name suggests, is just a single character.
A float is a floating point value, and that's just a fancy
way of saying a real number-- something that has a decimal
point in it with some numbers to the left and/or right.
An int is an integer, which is just a number like 123.
And now more interestingly are things like double.
What did we say a double was last time?
It's 64, right.
So whereas typically a float is 32-bit--
so it's this long in memory, it uses 32 zeros and ones to
represent values-- a double is literally twice that, which
means that you can either represent bigger numbers, or
you can represent more precise numbers.
So you can have more numbers after the decimal point, but
as the fellow said in the video, even something simple
conceptually like 1/3 cannot be represented precisely by a
computer, because eventually you run out of bits, and
therefore you can only do .33333333 so many times, at
which point you just have to say, I'm out of representation
space, I have to just call it a day and represent it
somewhat imprecisely like that.
And a long-long--
stupid name, but it's because there's a data type known as a
long in C which coincidentally is often 32 bits, just like an
int is 32 bits, but a long-long is
generally 64 bits.
So it just means you can represent bigger
numbers than this.
All right.
So if we actually want to start representing things, we
might need more sophisticated types, and
that's why we have string.
So in the CS50 library, that file called CS50.h, we've
actually declared a data type, so to speak, called string,
but string does not actually exist.
This is, again, one of these layers we're going to peel
back in a week or two and throw it away, and actually
look underneath the hood at what a string actually is, and
how it's represented.
We're also going to look at Boolean values.
So what was a bool in the context of Scratch?
It's true or false.
So it's just a value that's true or false, on or off, 1 or
0, however you want to view the world.
So in C, thanks to the CS50 library, which in turn
includes another library underneath the hood, we have
access to a data type that's called bool, which will
literally allow us to assign the values true or false to
things in a program.
So let's go ahead here and write a little program that
does something involving numbers instead.
So let me go back to the CS50 appliance, let me go ahead and
create a new file here called--
let's just say something simple, like math.c.
All right.
And now go up to the top of my program, I'm
going to do the usual--
include standard IO.h, so that I can use
printf int main void--
we'll come back to, eventually, what int means
here, what void means here, but for now, just know that
you have to start programs like this.
Now I'm going to go ahead and say something a little
different-- print s, give me a number, close quote, closed
parenthesis, semicolon.
Then I'm going to do int n equals get int, because it
turns out get int is another function in the CS50 library
besides getString.
And now I'm going to do something stupid, like, thanks
for the percent d, for decimal integer, and then comma n, and
then return 0.
So again, return 0 has nothing to do with the number I'm
asking the user for.
Just signifies at the end that all, presumably, is well.
So let me go down to my terminal window here.
Let me type make math, enter.
And something's wrong.
Make math did not work.
Here's the first of our error messages.
Why?
A little cryptic, but--
Yeah.
So CS50.h.
So again, especially early on, you'll write your first
program for pset 1 in sections, in office hours, and
frankly, it'll be overwhelming sometimes what these things
are actually saying.
But you'll realize very quickly, they start to fall
into buckets whereby this one means that you've left off one
of the sharp includes at the top of the file.
And clang, the compiler, is informing you of this by
saying, rather fancifully, implicit declaration of
function getInt is invalid.
All right.
So what does that mean?
It just means you've implicitly declared getInt in
the sense that you haven't explicitly declared it.
To explicitly declare int, you have to teach
clang that it exists.
And the very simple solution for that is to teach it by
including this other file, CS50.h, at
the top of your file.
Because literally, what sharp include does is it tells clang
pretty much to go find the file called CS50.h, copy,
paste it automatically into the top of my program for me,
and then convert the source code to zeros and ones.
But that would obviously be very tedious if we humans had
to go copying and pasting these big files all the time,
so sharp include just does all that automatically without
muddying my code with someone else's library.
And a library is just another file containing functions and
other things that someone else has written, that we are
leveraging for our own sake.
All right.
Let's try again.
Make math.
Enter.
Good.
So that seems to have worked.
So let me now run math dot slash math.
Enter gives me a number, 123.
Thanks for the 123.
Now let's try another corner case.
A string is pretty generous, because a
string can be anything.
Can look like numbers, look like
characters, look like words.
But an int is an int.
So now if I'm the difficult user, and it's saying, give me
a number, what if I try to do something like 0.1?
Well, that's a number, and it's consistent with the
grammar I used here, but underneath the hood, I'm of
course using the function get int.
So let's see what happens.
Enter, retry.
All right.
Let me be difficult and just hit Enter again.
Enter, Enter.
So now it seems that getInt is a little more
powerful than getString.
We the staff implemented in such a way that if we notice
you have not given us an int-- and an int is a decimal number
containing zeroes, ones, twos, threes, fours, fives, sixes,
sevens, eights, or nines.
And that's it.
No decimal points, no characters, no punctuation.
It seems that we have to cooperate.
So let me try four, five, six, and that gets me through.
So really, to be anal here, I should probably say, give me
an integer in order to convey to the user
exactly what I intend.
Now let's think about underneath the hood.
We're not going to look at how CS50.h is implemented today,
that library.
But if it keepes saying retry, retry, retry, what programming
construct am I presumably using underneath the hood to
implement that.
So just a loop, right?
One of the simplest ideas that we saw on scratch.
The forever construct, the repeat construct.
Presumably, in this program, underneath the hood, even
though it's written in C and not scratch, it's doing
something called equivalent to forever if the user has not
typed in an integer, say retry, retry, retry, retry, so
then finally when we do get an integer, then you
break out of that loop.
So what other functions can we use here in CS50's library?
Well, kind of the obvious ones, at least based on the
data types that we now know exist.
We implemented getString, getInt, but throughout the
first weeks of the term, you can also use getFloat and
getDouble and getLongLong in order to get precisely the
data types that you want.
Because the key detail here is that unlike some languages,
like Python and Ruby, where you don't necessarily have to
declare the data type of a variable--
you don't have to tell the computer what kind of stuff
you're going to put into the variable--
in C, you have to be ever so precise.
So if you want a variable called n, a chunk of storage
that in this case happens to be 32 bits by convention, you
have to tell the computer that these bits are
going to store a number--
not a character, not a string, not a float.
And this addresses the issue we effectively
ran into last week.
If we're using zeros and ones numbers to represent not just
numbers but A's and B's and C's, how in the world is a
computer going to know that this pattern of bits actually
represents the number 65, as opposed to representing the
letter A, which coincidentally, according to
ASCII recall, was equivalent to 65?
So these data types provide context to the computer, so
that it knows if these bits should be interpreted as an
int or as a string.
Any questions, then, on this?
Yeah?
DAVID J. MALAN: Good question.
If we take out standard IO.h, we save the file--
let me go down to the terminal window and run make math, zoom
in and enter--
now I get a different error, because I'm implicitly
declaring what function now?
Printf.
So now I've created a new problem, whose solution is
essentially the same.
I need to put that back.
AUDIENCE: [INAUDIBLE]?
DAVID J. MALAN: Oh.
So if I take out--
OK, yeah.
So if I remove the standard I/O libraries inclusion up
here, and then you're saying, if I get rid of printf, get
rid of printf?
Yes.
This should work, because I'm no longer using any-- whoops.
Did I just lie?
Oh, OK.
This won't work because of a new error we just introduced.
And this one is a little more self-explanatory.
What's the problem?
Unused variable n.
So this error is the result of our having configured the
appliance to be particularly pedantic, to really yell at
you for every possible mistake, even if it's kind of
a non-fatal mistake like this-- is it really such a big
deal that I allocated an int, put a number in it, and then
just did nothing with it?
So functionally, no.
That's not going to break the program.
It's just kind of stupid, right?
There is no value to having asked for that number, stored
it in a variable, if you're never going to do anything
with it, whether print it or save it.
So in this case, clang is acknowledging as much, and
it's saying, unused variable n.
So we can fix this if we really want.
But again, this is not good programming now.
So I could just do this--
go down here, let me clear the screen, make math.
That works.
Now I can run my math program, takes input--
well, that was bad.
Takes input, and that is all it does, in this case.
So actually, let's try one other corner case that we
didn't think of earlier.
In this case-- here, let's go back to the one that's a
little more user-friendly.
Let me go back here and run make math again.
Whoops, what did I do wrong?
Got to roll back further.
OK.
Now we're back in the original state, where all
is hopefully well.
And now if I run math, 123 seems to work.
But this is not really an effective proof, to just say,
OK, that works.
789, that works.
Time to submit.
Because there's other corner cases here.
A string did not really give us much trouble by typing in a
whole lot of characters.
But what if I type in this?
Hitting the number 1 a whole lot, whole lot here--
all right, it's getting a little boring, so I'm going to
stop here, and I'm going to hit Enter.
What the hell just happened?
So this can actually be explained.
So did it take it as binary?
A good thought, but no, it's not the fact that it took it
as binary, because indeed, that was just kind of a
coincidence.
We can do this again.
So there's no 2 in the binary system, that's
probably enough anyway.
Enter.
So that wasn't the problem.
What else?
Exactly.
So think back to what an int actually is.
It's 32 bits which collectively are interpreted
as a number.
If you have 32 bits, that means each of the bits can be
a 0 or a 1, 0 or a 1.
So that means there's two possibilities for this bit,
two possibilities for this, two possibilities for this
bit-- so that's 2 times 2 times--
so that's 2 to 32 power, is the total number of
permutations of zeroes and ones, if you have 32
placeholders, zeros or ones, in front of you.
So if I have 2 to the 32, mathematically, that's what?
So it's a lot.
It's 4 billion, give or take.
And 4 billion does not seem to be what was printed out here.
In fact, this looks closer to 2 billion.
But this isn't a coincidence.
The fact that the computer has misinterpreted my huge number,
my huge sequence of ones and then my huge sequence of twos,
as roughly 2 billion, is explained how?
There's a cap on the int.
There's a certainly a maximum value that you can represent.
But it's usually 4 billion, right?
If 2 to the 32 is roughly 4 billion, that seems like
that's the biggest number.
So not a remainder, but a good thought.
Negative numbers.
So if you have 4 billion possible things you can
represent with these various permutations of 32 0's and
1's, well, it stands to reason that we humans might want to
represent not just positive integers,
but negative integers.
And indeed, that's the assumption that C makes.
So with 32 bits, you can represent roughly negative 2
billion all the way up to positive 2 billion.
And so in this case, what we're really seeing is just
the fringe of the capacity of our integer, and we have, so
to speak, overflowed an integer.
We've tried to cram more bits into it than can actually fit.
So the end result is we essentially have all 1 bits.
We've flipped all of our bits on, we've tried to represent
the biggest number possible, but it's clearly not nearly
big enough to represent the two things I've just typed in.
Yeah?
AUDIENCE: [INAUDIBLE]?
DAVID J. MALAN: Good question.
Why can't we just represent that negative
sign as just a character?
So we absolutely could, and we could then save
an additional bit.
But you're still going to have to represent that dash, and
that's going to cost you at least something,
presumably a bit.
So yes, we could have 32 bits used for our number, but now
we need another bit used for our dash
symbol, the minus sign.
So now we have 33 bit values, which we could do, but it's
just not the way it's done.
We made a judgment call years ago, 32 bits is sort of the
unit of measure here.
So we're going to use one of those bits effectively to
represent negativity, and 31 bits to represent the number.
Other questions?
All right.
That's a lot.
Why don't we go ahead and take our five-minute break here?
All right.
So again, not to overemphasize the mathematical applications
of programming, let's just pluck off this one example
before we move into other constructs, just because it
will illustrate a few common stumbling points on some of
the basics when it comes to writing programs.
In this case, doing arithmetic expressions that have some
interesting relevance to the syntax.
So this is just a formula with which we can convert
temperatures from Fahrenheit to Celsius.
If you don't recall, this is just the formula.
So if we plug in Fahrenheit value of 32 degrees, which is
freezing, that's going to be 32 minus 32 is 0, and indeed,
0 in Celsius is also freezing.
So quick sanity check there-- that's the familiar formula.
Let me go over to the appliance, though, and start
writing a program called, like, F to C. Fahrenheit to
Celsius dot c.
And up at the top here, I'm going start off with include
standard IO.h, and I'm going to do int main void, and
again, we'll come back in the future as to what int means
there and what void means there.
And now let me go ahead and implement a program that
essentially does this conversion for us.
So printf, let's say, temperature in Fahrenheit.
And then let me go ahead and get the value from the user.
What kind of value should I get from the user if I want
them to give me a temperature in Fahrenheit?
yeah?
What would you propose?
Yeah.
So float feels pretty reasonable.
We have, again, just a few options that
we've seen thus far.
We have things like char, double, float, int, long-long,
bool, string.
So of these, float feels pretty reasonable.
We could use a double, right, because if we really want to
represent a human's temperature--
98.60000--
we could use 64 bits to do that, but it feels like
overkill for the temperature.
So let's just go with float.
Now I need a variable.
Just to keep things simple, I'm going to call it f, but I
could just is correctly call it temperature,
but either is fine.
So float f equals get float.
As soon as I now have decided to use get float, though, I
need to make one correction.
Yeah.
So we need to include CS50.h, otherwise the compiler is
going to yell at us.
Now that I have a float, let me go ahead and do the
conversion.
So first, let me also get into the habit here of
commenting my code.
So again, a comment is just a notation to the human that's
not part of the program per se, but it, one, helps you
understand what you did the next morning, or the next
month, it helps your teaching fellow understand what you did
or what you intended to do, so it's
generally very good practice.
Every few lines of code, write a comment to
yourself or your colleague.
So here, I'm going to say, "ask user for temperature."
Doesn't have to be a complete sentence, just a short phrase.
But the idea behind commenting in C is that each of your
comments should empower you-- or, say, a TF or a colleague--
to skim your program and know exactly what it does, by not
so much reading the code, but by reading the comments, which
should be succinct and clear.
All right, so now let me go ahead and say, "convert
Fahrenheit to Celsius." So I'm going to go ahead and do,
let's say, float c.
We'll keep Celsius as a floating point value.
And let me take a stab at this.
The formula is, again, up here.
As 5/9 times F minus 32.
So let's do this.
So 5 divided by 9, minus--
whoops, I messed it up already.
Times.
So 5/9 times F minus 32.
So F minus 32, semicolon.
And now, down here, I'm going to go ahead
and say, print result.
And this part's easy. printf.
How do I go about printing this?
Well, I could say, that number in Celsius is %f, backslash n,
just for some aesthetics.
And then what value do I want to plug in here is printf
second argument?
OK.
So just c.
So we'll start with this, and then just return 0.
Again, it has nothing to do with the math.
It just means all is well.
Now, is this correct, this program?
All right.
So I've made some mistakes.
Even if you have no idea what programming is, you can sort
of infer back to grade school what the
mistakes here might be.
What's one of the first?
AUDIENCE: [INAUDIBLE]?
DAVID J. MALAN: Yeah.
So we're missing parentheses.
It was deliberate that I did parentheses in the keynote
slide here, because there's actually this notion of order
of operations, or precedents, whereby certain operation--
multiplication--
have higher binding, have higher precedence, than things
like addition or subtraction.
So we've done this for years just to make clear exactly
what mathematical operation should be done first.
So I should probably mimic exactly this.
Otherwise, the computer is presumably going to try to do
something like divide 5 by 9, then multiply F, then subtract
32 from the whole thing.
Which is not, in fact, what we want to do.
So let's parenthesize.
Let's add my parentheses here, parentheses here.
Let's put this over here.
Here, I just noticed another mistake I made along the way.
Yeah?
AUDIENCE: [INAUDIBLE]?
DAVID J. MALAN: Ah.
Good.
So we actually have the same integer
division issue from before.
I correct that, let's see what the symptom is.
But we're going to have to correct one
other mistake here.
Yeah?
AUDIENCE: [INAUDIBLE]?
DAVID J. MALAN: Yes.
So, stupid mistake I made, but incredibly important.
The computer is not going to just turn a blind eye and let
me use capital F, when I actually declared the variable
as a lowercase f here, in line 8.
So I need to correct in line 11 my capitalization, which a
moment ago was a capital F by mistake.
What about here?
Should this be lower case as well?
It should be, but that's a coincidence.
That %f has nothing to do with the variable.
So just to be super clear here, let me go ahead and
rename this, briefly, "temperature." Let me rename
this "temperature." Let me not rename this.
Because, again, %f globally means, "floating point value."
Has nothing to do with my variable.
But "temperature" all over the place is just a little wordy,
so I'm going to go back to my lowercase "f"s.
Now, let me go ahead down to my window here.
Make f2c--
which was, again, the arbitrary name I
gave to this file.
Seems to have compiled.
Let me run f2c.
Enter.
Temperature in Fahrenheit-- let's do an easy one.
32.
OK, correct.
But, if I now do 212 for boiling--
damn.
All right.
So, clearly not correct, if your TF tries
at least two values.
So what's the problem here?
Well, you already put your finger on what the issue is.
But why is that the case?
Up here, when I'm doing the math of 5 divided by 9.
So conceptually, that's perfectly correct.
Totally consistent with the formula.
But computers only do precisely what you
tell them to do.
And what we're effectively telling the computer here is,
divide the integer 5 by the integer 9,
and give me the result.
The problem is, though, when you do division using data
types, the output is the same data type is the inputs.
And so if the two inputs are both ints, the output is going
to be an int.
And so 5 divided by 9 is point something.
But what's the closest integer to point something?
So, it's actually 0.
So as we discussed on Monday, everything after the decimal
point gets truncated.
It just gets thrown away.
So this a complete mistake, because I might as well just
multiply everything by 0 here.
Now, I can fix this in a couple of ways.
I could do this.
5.0 divided by 9.0.
Technically, I don't even need to do that.
It does suffice to just make one of the numbers a float--
or double-- by putting the decimal point here.
Because what happens now is, when you divide an integer by
a float, or a double, the computer's going to realize,
oh, one of those is more precise.
Let me err on the side of giving you more precision than
you intended.
So it will convert the result--
it will return the result as a floating point value as well.
So that would work.
This would work.
And there's one other mechanism that we'll see in
greater detail next week.
You can actually, as the programmer, tell the computer
to actually treat one data type as
though it were another.
So I could actually do this.
In parentheses, I can say something like, (float) open
paren, close paren.
And this is what's called "casting." Again, more
on this next week.
But this is just the programmatic way of telling
the computer, yeah I know 9 is an integer or a long.
I know it's not a floating point value.
But treat it as such anyway.
So to cast a data type means to convert
from one to the other.
Frankly, this is just a little ugly, so I would propose we go
back to the original suggestion of just converting
these things to floating point values manually.
Because now it's just super clear what's going on, and
it's not all that distracting.
So let's now go back into my terminal window.
Make f2c.
Let me go ahead and run this.
And, as an aside, you get bored typing these commands
again and again, realize that computers, like Linux here,
tend to be pretty smart.
And if I hit Up, we can go through my entire history of
commands, Up and Down.
So in this case here, I can just go Up to run make f2c.
I'm being yelled at, because I just did that a second ago.
It's already up to date.
So now I can run f2c again.
Let's try 32 Fahrenheit.
Now let's try 212 Fahrenheit.
Phew.
Seems to work.
Now, this is good, but it feels a little arbitrary that
I'm showing six numbers after the decimal points.
Right?
If you're the weather person on TV, you're kind of a doofus
if you're reading the temperature to
this degree of precision.
So let's simplify this.
So let's actually go up here, to my printf.
And yeah, I want to float.
But I want to now specify the precision.
And I'm going to be a little succinct.
I'm going to say, give me one point after the decimal place.
So rather than say %f, I'm going to say %.1f.
And this just means, give me one position
after the decimal point.
All right.
Let's try this again.
Let me re-run it after compiling.
And now I type in 32, and I get back a
more succinct value.
And I can type in, now, 212, and get back a more succinct
value as well.
And there's other things you can do with printf.
You can actually specify how wide the number should be.
If you want to have padding on it, so you can right-align or
left-align all of your numbers, or
try to center things.
So realize there are little tricks you can do, there.
But they just boil down to format codes,
like we have there.
So in terms of this issue of precedence.
This is a very boring table to go through methodically.
But realize that the authors of C have come up with the
rules that specify which operators have higher
precedence, more binding power, than others.
So whereas, in grade school, we generally knew that it was
multiplication and division, then addition and subtraction,
then some other stuff.
In a world of programming, there's a much
longer list of things.
And this is not something worth memorizing now.
You'll get acclimated to it before long.
But just like in grade school, parentheses generally solve
the issues of precedence, by making super clear to the
computer and human alike what is intended.
So consider this just a little cheat sheet there.
But most of those details we won't get to for some time.
All right.
Now, let's build up, and move away from these silly little
mathematical examples.
Still use the same fundamentals, but start
expressing ourselves a little more like Scratch.
So in Scratch, we had these Boolean expressions.
And English, a Boolean expression is what?
True or false.
It's something that evaluates to true or false.
1 or 0.
On or off.
Whatever you want to call it.
Now in Scratch, the puzzle pieces looked like this.
And that's the combination, on the bottom there, of three
puzzle pieces.
One conjunction--
something that has blank and blank.
And then two Boolean expressions inside of it.
So you can nest Boolean expressions to get more
complicated Boolean expressions.
For instance, if it is Saturday, and it is nice
weather, then go outside.
Or something arbitrary like that.
So you can join Boolean expressions to come up with a
larger truth value, whose answer is based
on the smaller ones.
Now, in C, thankfully, this mapping is
actually pretty simple.
Parentheses help make things clear here.
Ampersand ampersand is what means "and." So that's how
we're going to convert those blocks from Scratch to C.
And for the conditions we saw in Scratch, on the left hand
side there, I can stack them similarly in C. If x is less
than y, then do this.
Else if x is greater than y, then do this.
Else logically they must be equal, so say
that they are equal.
So we can now start to build these things up.
So let's do an example.
Let me open up a file that's on the course's website, that
I wrote in advance.
That is called nonswitch.c.
And we'll see why in just a moment.
So in this file-- nonswitch.c--
first realize that there's a whole bunch of
comments at the top.
That's just explaining the name of the file, what it
does, and so forth.
And it apparently demonstrates the use of Boolean and-ing--
conjoining two expressions.
So let's see how this program works.
I include my two files. cs50.h and stdio.h.
If I scroll down, now, to the program, it's a little longer
than we've written before, but pretty readable.
So I first print to the user, on line 18.
"Give me an integer between 1 and 10." So
that's right there.
Now I get the integer.
But realize that get int-- pretty simple.
It's just going to get an int.
It's going to ensure that it does get an int, and not a
string or a float.
But it's not going to do any kind of boundary checking.
It's not going to filter the numbers to be between a
certain range.
So we have to implement that ourselves.
So just like in Scratch, where I could do a conjunction of
two expressions, I can do that in C as follows.
If n is greater than or equal to 1, and n is less than and
or equal to 3, I am going to very arbitrarily decree that
number to be a small number.
Just for the sake of a statement.
Else if n is between 4 and 6, print "You picked a medium
number." Else if n is between 7 and 10, print "You picked a
big number." Else print "You pick an invalid number."
Because logically, that follows if we've
exhausted our range.
So realize this is not quite how we write greater than or
equal to in a math class.
Right?
You usually draw it like this, with the line under it.
But there is no such key on my keyboard.
So the symbol in C for expressing greater than or
equal to is two characters, and the symbol for less than
or equal to is two characters as well.
All right.
So if I run this program, you can probably guess what it's
going to do.
If I go ahead and make nonswitch, Enter.
And then run nonswitch.
Give me a number between 1 and 10.
I'm going to give it 11.
I picked an invalid number.
Let me try the number 3.
I picked a small number.
So fairly straightforward there.
What if I want to change the behavior of this program and
do something a little different instead?
Let me go ahead and say this.
Let me get rid of all this.
And let me say, if (n = = 42).
And then I'm going to go ahead and say print "You picked the
right answer." Else we're going to go ahead and say,
printf, "You picked the
wrong answer." OK.
Just because.
Now, a few points we can make here.
One, I did not type the symbol that you would think is the
most reasonable.
When I want to check equality, turns out you don't use the
equal sign.
You instead use the equal equal sign.
And why might that be, just intuitively?
Yeah.
AUDIENCE: [INAUDIBLE]?
DAVID J. MALAN: Correct.
The equal sign is already used for assignment.
Someone didn't really think this through.
And so now, when it comes to equality testing, we have to
be more explicit, and say equals equals.
A very common mistake is to do this.
And, if in office hours, or sometime next week, or in the
next two weeks, you do something like this, and then
struggle with it for 10, 20 minutes trying to figure out
what the damn bug is, realize that you are one of those
people who made this mistake.
So, super easy to make.
But the goal now is to ward off this mistake.
This could evaluate, because what is this really saying?
If n gets 42.
So the verb people use, verbally, to express the
notion of assignment is "gets." So if n gets 42.
So that means assign 42 to n.
Now I have a Boolean expression in parentheses,
inside of this condition, The value of which is 42.
Right?
Because if you evaluate it from the inside out, the total
answer, now, is 42.
So if I asked you the question, "if (42)," what is
that really saying?
Well, Boolean expressions can only be true or false.
1 or 0.
On or off.
So we seem to have a corner case here, whereby all of a
sudden, I'm saying "if (42)." That is not a Boolean value.
So what computers do, though, is it essentially equates
anything that's not 0 is interpreted to be true.
1.
On.
They're all functionally equivalent.
Only the value 0, or the keyword false, as we'll
eventually see, is actually false.
So "if (42)" would actually be true, because it is non-zero.
So to be more precise here, a Boolean value is really not so
much true or false, but for this
case, if is 0 or non-zero.
So false or not false.
And that allows us to encompass all the more
possibilities in the parenthetical expression.
So we need to, ever so carefully, say this--
if (n = = 42).
Simply because we need to specify a test of equality,
not an assignment of a value.
What else is worth noting here?
These curly braces are actually not necessary.
I've been doing them just to be nit-picky here.
But when you have a condition that only has one line of
code, as a convenience, programmers do not need to use
the curly braces.
It suffices to just indent, for aesthetic reasons, and
then write your line of code.
Only when you want to write two lines of code-- for
instance, else printf "Bye!" If you want to do
that, this is broken.
You can indent all you want, but the fact that you have
typed two lines of code without using curly braces,
means that this program would not run correctly, or might
not even compile correctly.
So for now, to be honest, I think the best habit is just
use the curly braces, and you'll find that they make
super-clear what's going on.
Also, as an aside-- especially if you're among those who use
the recommended books or resources online--
realize that there's this notion of style in
programming.
And some people actually prefer to put the curly brace
over here on a line.
Some people prefer to do ridiculous things,
like put them here.
There's no wrong answer when it comes to
style, but this is wrong.
But some people do it.
[LAUGHTER]
DAVID J. MALAN: So I mention this now, simply because, as
you read up on things in books or online, you're going to get
different messages from different teachers and
different authors.
And just realize that, functionally, they're
generally equivalent, but be consistent.
So in this case, we'll standardize on the curly
braces lining up here on the left.
All right.
So let's go ahead now and run this, and see what happens
with this program.
Make nonswitch.
Then Run nonswitch.
Give me an integer between 0 and-- so that's not relevant
anymore, but I'm just going to arbitrarily say 42.
And I picked the right answer.
Well now, let's actually take this in a different direction.
It turns out there's some other syntax we
can use in a program.
Here's a new piece of syntax that is functionally the same,
but just introduces another way of expressing ourselves.
So this is what's called a "switch statement." And a
switch statement operates as follows.
It takes, in parentheses, a value that you want to
"switch" on.
You want to behave differently based on its value.
So this means switch on the value of n.
And what does this mean?
Well, if n is 1 or 2 or 3, go ahead and print out "You
picked a big number," and then break.
The reason for the break is that the way C works is, as
soon as it finds a case that matches, it starts executing
all of the code below it.
So if I have one line of code, as I do here on line 27, it
gets executed.
If I add another line here, it gets executed.
Another line here, it gets executed.
The program only stops executing what's below the
applicable case statement when I explicitly
say, "break." So wrong.
Right, in this case, as a result.
Case 4 or 5 or 6, similarly, is going
to print out a message.
In this case, medium number.
Break.
Case 7, 8, 9, 10, it's going to print out "You picked a big
number." Break.
And then there's literally a keyword called "default"
that's effectively like the "else" construct.
So new syntax, but intellectually,
nothing at all new here.
It's just like the Scratch.
If, else if, else if, sort of block.
So that's the switched version of this program.
But notice that we did it with numbers here.
Let me open up a second version, just to show
something a little more concrete.
So here we have grades instead.
Turns out you can use switches not just on integers, but on
characters.
Not in all data types, but at least on characters.
So here, we could do something like, if the value of C that
the user typed in, in line 19, by using the GetChar function,
is capital A or lowercase a, let's interpret it as a grade,
and say they picked an excellent grade.
And down along the switch statement, do we have other
cases for "B"s and "C"s, and so forth.
But let's now round out one other thing.
And again, kind of flying through
some of these examples.
But it's really so that we can apply them, now--
or really, just translate them now--
to C.
So let me go ahead and do this.
I'm going to open up a new file.
And I'm going to do include .
And then I'm going to do int main (void).
Let me save this file as a loop.c.
Just as an example.
And I'm going to go ahead and do the following.
I want to write a very tedious program.
So 10.
Let's do this.
9.
All right.
As soon as you start doing this in programming, you're
probably doing something wrong.
But we'll start there anyway.
Oops.
That's actually wrong.
7.
6.
5.
4.
Nothing's going to happen when we get to 0.
3.
2.
1.
OK.
0.
Delete.
All right.
So return 0.
So this program's obviously going to do what it very
tediously says it's going to do.
It's going to print all of those
values, from top to bottom.
So let me do make loop.
And then-- whoops.
Make loop.
I was in the wrong directory.
And now let me run loop.
Enter.
Woohoo.
Very nice.
Nice, long program.
All right, but now let's do this better.
Just like in Scratch, when you started using a looping
construct, instead of just copying and pasting all of the
puzzle pieces, let me introduce this thing.
So in C, there are various looping constructs.
One of which looks like this.
A "for" loop has three things, separated by two semicolons.
And then, inside of the body of the loop, so to speak-- the
stuff inside of the curly braces--
we can do something again and again.
So what I have here on the screen, this is
literally just a comment.
The / / means, here comes a comment.
This is not code.
This is just English description, to me, the human.
So initializations, conditions, and updates.
Well, let's see how this works.
This allows me to rewrite this program as follows.
For--
so here comes my loop--
int i--
here comes a variable called "i"--
gets initialized to 10.
So there is my initialization.
I want to do this loop so long as "i" is greater than or
equal to 0.
And on each iteration of this loop, I want
to do i minus minus.
So we saw plus plus on Monday.
Minus minus is the opposite.
It decrements the value by 1.
Inside of this loop, I want to actually do something.
So I'm going to say something like, printf.
I don't want to say 10 now, because this obviously is just
going to print the same value.
What do I want to print instead, probably?
Yeah.
So "i." So %d comma i.
So I'm going to steal one of those ideas from earlier--
this substitution for format codes--
and then down here, at the bottom, I'll return 0, just
for good measure.
But now, I have a more succinct program.
It's only a few lines, and not some 10 or 11 lines.
Let me go to my prompt down here.
Remake loop.
Rerun loop.
And it appears to be the same.
Now, the program's no more interesting.
But now I can type something like this.
I can change this to 100.
I can then recompile my program.
And then I can re-run my program.
And now it goes super-fast on today's computers.
But there I have--
and actually, my scrollback buffer doesn't
even go that far.
There we go.
So there we have all 100.
But it's easy to make mistakes here.
What's going to be the expected behavior if I
accidentally do that?
So it feels like infinite.
So let's see if the computer behaves.
So let me go ahead and make loop.
And let me run loop.
So let's come back to that in just a moment.
We'll let that run.
Because what should we count up to, minimally?
Probably at least 2 billion, if we have that many bits, and
we're using positive values.
So let's jump back here.
Besides these kinds of loops, we have what are called "while
loops." So, just like in Scratch, you had forever
blocks, and you had repeat blocks.
Similarly, in C, you just have other ways
of expressing yourself.
So you have this notion of a while loop, that allows you to
express yourself a little differently.
And then you have something called a "do while loop,"
which operates slightly differently as well.
And we'll tease these apart over time.
But let's take a look at this last one here.
If we go into--
still running.
It's pretty infinite.
So let me go into positive, and show how we can actually
govern user input a little more effectively.
It's a pretty short program.
And here we have, in main, an int n declaration.
So give me a variable called "n." Now, I literally say
"do." And then I've got open curly
brace, close curly brace.
And then notice, right after that, it says while n
is less than 1.
So notice that these lines--
19 to 24--
compose collectively a do while loop.
And again, new syntax, not a new idea.
It's just another way of looping, syntactically.
So what does this do?
Do the following while n is less than 1.
So what do I want to do?
I demand that you give me a positive integer.
Then I actually try to get an integer from the user.
But in terms of the order of operations here, because the
while condition is down here, I'm then going to check, is n
less than 1?
So suppose that the user--
after being demanded in this way--
types in the value 0.
This condition is going to be true or false?
Well, it's actually going to be true.
Because n is 0, and 0 is less than 1.
So what does that imply?
Do it again.
Do it again.
Do it again.
Only once that condition evaluates to false.
If I type in 42.
Well no, 42 is not less than 1.
Therefore this loop is done executing.
And so at that point, the program will say, thanks for
the 42, in this case.
Now that you have this way of expressing yourself--
We should not wait for this to finish.
It's going to take some time.
To be continued next time, perhaps.
But now, you have the ability to, hopefully to understand a
bit of geek humor.
Apologies.
This is not representative of humor in the computer science
world, but it's cute.
[LAUGHS[ Let's end on that note.
And we will see you on Monday.
[APPLAUSE]
