[Week 2, Continued] [David J. Malan, Harvard University] [This is CS50. - CS50.TV] All right. This is CS50, and this is the end of week 2. If you expect to be hungry around this time tomorrow, know that we're going to convene as a small group tomorrow, Thursday, 1:15 pm. There's this URL here if you'd like to RSVP. Space is limited, so please forgive if the form has filled up by the time you fill this out. Another URL, though, that might be of interest is this. In just about a month's time, the course is going to be made available all the more broadly via edX, via which folks on the Internet will be able to follow along, engage in the course quite actively, in fact. They'll be using the CS50 Appliance and CS50 Discuss and most of the various software tools that we already have been using this semester. And one of the initiatives we'd like to take on as an experiment this year is to see just how much content we can translate into other spoken and written languages. So if you might have an interest in participating in this project whereby we will provide English transcripts and subtitles for the course's lectures and shorts and seminars and sections and the like, if you speak fluently or write fluently some other language, we would love to engage you in this project whereby you take on one or more of the videos, translating them into a language you know quite well. To give you a sense of the interface, there's this web-based user interface that we'll be using that will create essentially a UI like this. This was me teaching some Halloween ago, and on the right-hand side there in black next to these time stamps, you'll see the various things that came out of my mouth that day, and then below it you'll be able to translate into some other language exactly what the mapping is between, in this case, English and, say, Spanish. So it's actually a very user-friendly tool. You can rewind and fast-forward very readily with keyboard shortcuts. So if you would like to take part in this experiment and have your words seen and read by potentially thousands of folks out there, please do feel free to participate. One word about the kitten from Monday. Lest we have sent an overly scary message, do realize that, as office hours suggest and as sections suggest, the design of the course is very much to have students collaborating and talking to work through problem sets and problems together, and really the line just comes down to, again, the work you ultimately submit should be your own. And so quite honestly, in office hours it's totally normal, it's totally to be expected even, to be chatting with some friend next to you. If he or she is struggling with some topic and you're like, "Oh, well, let me give you a glimpse of some line of code that I wrote," that's fine, that happens, and that's very much conducive, I think, with the process of learning. Where the line gets crossed is when the head is sort of tilted over here for far too many seconds or minutes for that really to have just been an unblocking opportunity for your friend, and certainly when things get exchanged via email and Dropbox and the like, there too is the line. So by all means feel comfortable and feel encouraged to chat with friends and classmates about psets and more and just realize that what you ultimately submit should really be the product of your creation and not someone else. And so one of the domain-specific problems for pset2, which will come out late tomorrow night, is to dive into the world of cryptography, which is the art of encrypting or scrambling information, and this ultimately relates to the world of security. Now, security for most of us comes in the form of fairly mundane mechanisms. All of us have usernames and passwords, and all of us have very bad usernames and passwords, most likely. If your password is the same on multiple websites, that's probably not the best idea, as we'll discuss toward semester's end. If your password is written on a sticky note--no joke--on your monitor, that too is not necessarily the best design but quite a common phenomenon. And if you're not using cryptography to encrypt your passwords, they are particularly vulnerable. So if you think you're being super clever by having a hidden Word document somewhere on your hard drive that has all of your passwords but it's in a folder that no one's going to look in, that too is not a very secure mechanism. And so what pset2 will introduce is this art of cryptography and scrambling information so that things like passwords are all the more secure. The context here is that with insecure data comes an opportunity to encrypt it and to scramble it. And so this, for instance, is an example of an encrypted message. This actually says something in English, but it's clearly not wholly obvious. And we'll come full circle today to tease apart what this secret message here is. But in the real world of computers, things don't even look like they might be English phrases. For instance, this is what you might find on a standard Linux or Mac or UNIX computer in a file that was once upon a time called the password file. Nowadays it's been moved to other places. But if you look in the right place on a system, you'll see not only your username or that of other people on the system, but you'll see an encrypted version of their password. Indeed, the word crypt there suggests that the following stuff is encrypted, and this series of seemingly random letters and characters and numbers and so forth can be decrypted only by generally knowing some secret-- a secret word, a secret number-- and so indeed, the art of cryptography ultimately boils down to trust of some sort and knowing something that someone else does not. So we'll explore this in a bit more detail today and in the pset to come. And now a word on pass/fail. Especially as some of you have dived into pset1, the Appliance, and a very new world for yourself, realize that frustrations and confusion and just technical difficulties are quite to be expected, especially with the first pset, where there's just so much new, just getting familiar with ls and cd and all these arcane commands and a new environment, and that's separate from the actual material and programming itself. So realize too that there are certainly office hours that exist as a support structure. Sections begin this coming Sunday. But most importantly, if you're feeling just that this is not the world for you, realize that it really does just take time. And were it not for this opportunity years ago for me of taking a class pass/fail, honestly, I never would have even set foot in the classroom. And you can change this up until, say, the fifth Monday of the course, so if you're on the edge now, realize that rather than head into some other waters altogether, do certainly consider just changing to pass/fail. Again, there's not really this culture here at Harvard of taking things pass/fail since everyone really wants to achieve or overachieve, but frankly, this is a wonderful way of trying something out that might not be familiar to you, and you'll end up doing, in most cases, quite fine, perhaps much to your surprise. And in more concrete terms, what I think pass/fail generally does, especially as you might have experienced with pset0, if you put in 10 hours, 15 hours, 25 hours into some pset and you're just banging your head against the wall and it's getting super late at night but you've taken the pset 90% of the way and you just can't figure out one thing, pass/fail really takes the edge off of a class like this, where you can sort of happily say, "Okay, I know it's not perfect, but I worked my ass off on this, I'm pretty happy with where it ended up," and that will meet the expectations for pass/fail. So do keep that in mind. All right. So those of you who have struggled to use the Harvard University Wi-Fi, know that there's a CS50 SSID, a Wi-Fi connection, floating around that you might have better luck for. It's a little ironic that the password for this, if you would like to try connecting to this for better speeds--and let us know if it's no better--is 12345, all the way up to 8 because 8 is more secure than 5. So if you need the Wi-Fi password, connect to CS50 wirelessly here, 12345678, and post on CS50 Discuss if you still have intermittent connectivity issues, and we'll let the powers that be know for this space. All right. So a quick teaser, especially for those of you who are fan boys or girls of all things Apple. What I dug up from a few years back was this file here, iUnlock.c, just to kind of make more concrete and more complex some of the more basic C programs we've been writing. So I opened up this file, iUnlock.c. It's available on the Lectures page for today. On the left-hand side you see a long list of functions. So the fellow who wrote this wrote up a lot of functions, more than just main. He used a whole bunch of libraries here, and if we start scrolling through, what this actually is is the very first, I believe, crack for the original iPhone. When you wanted to jailbreak the original iPhone, which means untether it from AT&T and actually install special software on it and do things that Apple didn't want people to do, someone took the time to figure out exactly how they could exploit software flaws, mistakes, bugs, in Apple software, and thus was born iUnlock.c-- that if you compiled it on your computer and installed it onto an iPhone that was connected to your computer via, say, a USB cable, this would give you administrative or root privileges on your iPhone and let you do pretty much whatever you want. And so there's been this fascinating cat and mouse game between Apple and the rest of the world in particular as they, like many companies, try to lock their stuff down so that you can only do with it what they intend. But thanks to people like this and the understanding of low-level details-- and in this case C programming--and a lot of the familiar constructs that we've started playing with, you are able to really leverage the hardware in a manner you see fit and not necessarily some corporate entity. So for instance, I have no idea what all this is doing, but GetVersion sounds pretty straightforward, and it looks like this is a function that this person wrote. It takes some kind of integer as an argument, doesn't return anything, but appears to loop with a for loop here and an if condition, if condition break, and somehow relates to version numbers if we scroll down, even though a lot of these keywords are going to be new. And there's a whole lot of functions in here we've never seen and might not ever see over the course of the semester. At the end of the day, it follows the same rules and logic that we've been playing with thus far. So this is far too old to crack your iPhone 3s or 4s or soon 5s these days, but know that it's all very much derived from this world that we've dived into. Let's take a look at a little more simple example: this one, just to get warmed up with some syntax and also some other data type that we've talked about but haven't really seen in C. This is a file called positive1.c, and per the comments at the top, this just demands that a user provide a positive number. So it's an example of a do-while loop, which is nice for user interactive programs where you need to tell the user to do something, and if they don't cooperate you yell at them or reject their input. Case in point: I am going to do lines 19 through 24 so long as the user has not given me a positive number. This detail here on line 18, why did I declare n above this whole looping construct as opposed to right next to line 22 where I actually care to get n? Yeah. [student] Scope. >>Yeah, so this issue of scope. And in layman's terms, what does scope refer to? Yeah. >>[inaudible student response] >>Can you speak a little louder? [student] Where you can access that variable. >>Perfect. Where you can access a particular variable. And generally, the rule of thumb thus far has been that the scope of some variable is defined by the most recent curly braces that you've seen. And so in this case, if I made the mistake of declaring n on line 22, that line would work. I would get an int, and I would put it into that variable n in line 22, but which line of code would now have no idea what I'm talking about? >>[student] 25. [Malan] 25, and it turns out 24 as well because in this case it falls outside of the curly braces. So just a little bit of a nuisance but very easily solved by simply declaring the variable outside of the function itself. We'll see later today you can go one step further and you could even get a little lazy. And this is not to be recommended in general, but you could even get lazy and put a variable globally, so to speak, not inside of a function, not inside of a loop, but in the file itself, outside of all of the functions you've written, as I did here on line 15. This is generally frowned upon, but realize this is a solution sometimes to other problems, as we'll eventually see. So for now we'll leave it like this, but let's see if we can rewrite this just to start expressing ourselves a little differently. This program, just to be clear, is positive1. Let me go ahead here and in my terminal window make positive1, Enter. Compiles okay. I'm going to run positive1, hit Enter. I demand that you give me a positive integer. I'll say -1. That didn't work. 0, 99. That seems to work. Maybe not the most rigorous test, but at least it's a nice sanity check that we're on the right track. So now let me go ahead and open version 2 of this, and what is different already? It implements the same thing, but what's jumping out as clearly different this time? This bool in green. It is highlighted in green, this keyword known as bool, which is a data type. It doesn't come built in to all versions of C. You need to include a specific library. In our case, I included the CS50 library so that we have access to bool. But in line 18, we seem to have a Boolean value here called thankful. I could have called this anything, but I called it thankful just to kind of convey some semantic meaning. So initially on line 18, I'm apparently not thankful because the Boolean value thankful is initialized to false in line 18. And then it seems what I've done here in lines 21 through 23 is I've just kind of rewritten my logic. So no functionally different, but in line 22 now I check if the int the user has provided is greater than 0, then I simply change the value of thankful to true. And why do I do that? Because in line 25, apparently I'm going to check a condition. Do this loop while thankful is false. So I proposed this as an alternative to version 1 because it's at least a little more intuitive perhaps, it's a little more grounded in English. So do the following while you're not thankful or while thankful is false. And this time too I apparently don't care to remember what the user typed in because notice there's no variable n, so actually, a little white lie there. Functionally, the program is a bit different once we get to the bottom of it because I'm not remembering what n is. But I wanted to demonstrate here too that even though we've seen GetInt and GetString being used on the right-hand side of an equals sign thus far so that we remember the value, technically, that's not strictly necessary. If for whatever reason you just don't care to save the value, you just want to check the value, notice that we can simply write this as GetInt, open paren, close paren. That function is going to return a value, as we've been saying. It's going to give you back an int. And so if you mentally think of this happening, when I type in 99, GetInt returns the number 99, and so conceptually, it's as though my code were actually this. So if 99 is indeed greater than 0, then thankful becomes true, then line 25 realizes ooh, we're done because I'm now thankful, and in line 26, we simply say, "Thanks for the positive integer!" whatever it happened to be. Now let's do slight syntactic sugar here, so to speak. Let's see if we can clean up this line 25 with this third and final variant in positive3. Notice the only difference now is what line of code? >>[student] 25. >>[Malan] Yeah, 25. And we've not really seen this trick just yet, but we did see the exclamation point on Monday, which denotes what? >>[student] Not. >>Not or negation. So take a Boolean value and flip its value. True becomes false, false becomes true. So this, I would propose, is even a little more intuitive a way of writing the code because I still initialize thankful to false, I still do the following, I set thankful to true when the time comes, but now you can really just translate this code verbally left to right, while (!thankful); because bang or exclamation point denotes the notion of not, so while not thankful. So again, we haven't introduced any new concepts per se. We talked about Booleans back when we played with Scratch, but realize now we can just start writing our code in many different ways. So especially in pset1 if you're sort of struggling to figure out the way to write some program, odds are you're in luck because there can be any number of solutions that you can happen upon. For instance, this is just 3 for even the simplest of programs. All right. And now recall on Monday we left on this note with return values. So for the very first time we wrote a program that doesn't just have main; it also has its own custom function that I wrote here. So in line 31 through 34 I've implemented a cube function. It's not complex. It's just a * a * a in this case. But what's important about it is that I'm taking input in the form of a and I'm returning output in the form of a * a * a. So now I have the ability, much like I used to with prinf alone, to call this function by calling the cube function. And the cube function takes some input, and the cube function returns some output. By contrast, printf just did something. It didn't return anything that we cared about, even though as an aside it does return a value; you just generally ignore it. Printf just did something. It had a side effect of printing to the screen. By contrast here, we have the cube function, which actually returns something. So for those familiar with this, it's a fairly straightforward idea. But for those less familiar with this idea of passing in inputs and getting back outputs, let's try just something super simple. Is anyone comfortable coming up on stage briefly? You have to be comfortable with a camera on you as well. Yeah? Okay. What's your name? >>[student] Ken. >>Ken. All right. Ken, come on up. Ken is going to be a function of sorts here. Let's go ahead and do this. Let's get a little fancy. Nice to meet you. Welcome to center stage. All right. Let's hit this button here. All right. So here you have a modern chalkboard, and what I am is the main function, for instance, and I don't have an iPad in my hand. I don't really remember how to-- Well, I can't say that. I don't really have good handwriting, and so therefore I want you to print something on the screen for me. I am being the main program, and I'm going to have you say this by writing it in my chicken scratch and then passing you an input. So silly though this exercise is, the notion of functions and calling a function and returning a function really boils down to this. I am main, I have just written printf, quote-unquote something on the screen, I am running this program, and as soon as printf gets called, it takes one argument or one parameter sometimes between double quotes. Here is that argument. I'm passing it to Ken. He is a black box written some number of years ago that apparently only knows how to print things on the screen. So execute. That's not bad. Very good. So now Ken is done executing. Does he need to hand me anything back? Not that we've seen thus far. Again, printf does actually return a number, but we're going to ignore that for now because we've never used it. So that's it for Ken. And so now main takes over control of the program again because that line of code, printf, is done executing. And we go about our way, executing whatever other lines are there. So now let's try a slightly different example. This time here let's first clear the screen, and this time we'll do the cubing function, but this time, I expect an output value. So let's go ahead and do this. Now I have a line of code that says x gets cube of x. The line of code, recall, looks like this: x = cube(x); So how is this going to work? Let's go ahead and give you a white screen again. I am going to write down now the value of x, which at this moment in time happens to be, let's say, 2 to keep it simple. I have written down on a piece of paper the value of 2, which is my value x. I hand it to Ken. >>And I just write the answer? >>Yeah, let's just write the answer. Okay. And now he has to return me something. Perfect. Nice segue. So now he hands me back the value of 8 in this case, and what do I do with it? Actually--let's see, get this right. What am I going to do with it? Now I'm going to take this value and actually store it in those same bits in memory. But notice I'm kind of struggling here. I'm a little confused because where do I actually write the value of x, because what I've just done is physically hand Ken a piece of paper that had the value 2, which was x, and indeed, that's precisely what happened. So it turns out that when you call the function and you pass in an argument like hello, world or you pass in an argument like 2, generally, you're passing in a copy of that argument. And so just as I wrote down the number 2 here and handed it to Ken, that must mean that I still have a copy of the value 2 somewhere because indeed, now that I've gotten back the value 8, I need to go back in RAM and actually write down 8 where I once had the number 2. So visually, remember this notion of passing in, literally, a copy of the value. Ken does his thing, hands me back something--in this case a value like 8-- and then I have to do something with that value if I want to keep it around. So all of this will come back to be all too familiar before long. Thank you so much for this demo here, Ken. [applause] Very well done. Let's see how that ultimately relates to some of the function calling that we've been doing here. Let me go ahead and bring us back to the cubing example here. Notice that if we want to actually start taking this further, we're going to have to be mindful of the fact that the number x that's being passed in here is different from what's actually being passed in to the function. So again, this pass by copy is going to become quite germane in just a moment. Let's take a look at something that doesn't quite work right yet. I'm going to go ahead and open a third buggy example, which is flawed by nature, and it's called buggy3 and it implements a swapping function. Here we have a main function that has x and y arbitrarily initialized to 1 and 2, respectively. We could use GetInt, but we just need a simple exercise, so it's hard-coded as 1 and 2. In lines 21 and 22, we apparently print out x and y, 1 per line. Then on line 23, I claim I am swapping these values, dot, dot, dot. I apparently call a function in line 24 called swap that takes 2 arguments. It's totally legit for functions to take 2 arguments. We've seen printf do it already. So swap apparently takes x and y, and as its name suggests, I would hope that it's going to swap these 2 values. So then I claim on line 25 "Swapped!" and I reprint x and y under the assumption that they've indeed been swapped. But if I actually run this program--let me open up a terminal window, let me make buggy3--as the name suggests, this is not going to end well because when I hit Enter, notice that x is 1, y is 2, and yet at the end of the program, they are still, in fact, the same. So based on the demonstration just now with Ken, what's actually going on? Let's dive into this swap function. It's super short. It's only a few lines of code long. But what's the fundamental problem based on the simple story told up here with Ken? Why is swap broken? [student] You're storing to a copy, not the variable. Exactly. We're storing to a copy, not the variable itself. In other words, swap apparently takes 2 arguments, an int, and it's arbitrarily called a and b, and up here I've passed in x and y, which are respectively 1 and 2, but I'm not literally passing in x, I'm not literally passing in y, I'm passing a copy of x and a copy of y. It's almost as though you copied and pasted into swap the values that you want it to actually manipulate. So if that's the case, when I the program start executing line 35 then 36, when I get to line 37, at this point in the story, what is the value of a? At this point in the story, line 37, what is the value of a at this point? >>[student] 1. [Malan] It should just be 1, right, because x was passed in as the first argument, and this function just arbitrarily is calling its first argument a. Similarly is y the second argument, and it's just arbitrarily calling the second argument b. This dichotomy is actually fairly simply explained. Think about it. None of us have met the person who wrote printf, so surely, he or she has no idea what our variables 30 years later are going to be called. So there has to be a distinction between what you call variables in functions you're writing and what you call variables in functions you're calling or using. So in other words, I have written my variables as x and y, but if someone else had written the swap function, he or she certainly wouldn't know what my variables are going to be called, so realize that this is why you have this duality of names. Technically, I could do this by coincidence, but they would still be passed in as copies. It would just be a pure coincidence aesthetically if that person who wrote swap had used the same names. So at this point in the story, line 37, a is 1, b is 2, and now I proceed to swap them. First of all, let me actually do this much more simply. I don't know what those 3 lines of code were doing. Let me just do this: b = a; a = b; done. Why is this broken, logically? It's kind of the intuitive thing, right? So a becomes b and b becomes a, but the problem is that as soon as line 37 executes, what's the value of a and b? The same, 1, because you have clobbered, so to speak, you've changed b to equal a. So once line 37 has executed, that's great, you now have 2 copies of the number 1 inside of this function, so then when you say in line 38 a = b, you're kind of screwed because you're just assigning 1 to 1. You've kind of lost the value you cared about. So in the original version of this, notice what I did. I instead had a third line of code that looked like this. I declare a temporary variable. Tmp is a very common name for a temporary variable, and it's an int because it has to match what I want to make a copy of. I store copy of a inside of tmp, so once line 37 has executed, the value of a is--quick sanity check--1, the value of b is 2, and the value of tmp is also 1. So now I execute line 38. Once line 38 executes, a takes on the value of b. And b was 2, so a is now 2. So at this point in the story, a is 2, b is 2, and tmp is 1, so now logically, we can just plop tmp's value into b and we're done. So we've solved that problem. Unfortunately, when I run this program in this form, it doesn't actually swap any values. But to be clear, why? I fixed the logical problem from just a moment ago, but again, if I run this program, x and y remain unchanged by the end of the program's execution. [inaudible student comment] >>We haven't returned anything, so that's true. But it turns out there's a bit of a problem here because thus far, the only thing we've been able to return is one thing, and this is a restriction of C. You can only return really one value, in which case I'm kind of stuck here because I could return the new value of x or I could return the new value of y, but I want both back. So returning is not the simple solution here. But the problem fundamentally is why? What have we actually swapped? [student] a and b. >>a and b. But a and b are copies of x and y, which means we just did all of this work, we just spent 3 minutes talking about the swap function and all 3 of these variables, and that's great, perfectly correct in isolation, but a and b's scope only is in these lines here. So just like a for loop, if you declare an integer i inside the for loop, similarly, if you're declaring a and b inside of a function that you've written, they're only valid inside of that function, which means as soon as swap is done executing and we go from line 24 to line 25, x and y haven't been changed at all. You just wasted a whole lot of time swapping copies of variables. So it turns out that the solution to this is actually non-obvious. It's not quite sufficient to return values because we can only return 1 value, and I really do want to swap both x and y at the same time, so we're going to have to come back to this. But for now, realize that the issue fundamentally derived from the fact that a and b are copies and they are in their own scope. Let's try to solve this in some way. Let me actually scroll back here and open up, let's say, a fourth variant of this, buggy4. What about this? This is a similar but simpler problem to look at before we take a stab at solving it. This program is called increment, and it apparently initializes an x integer to 1 in line 18. I then claim x is 1, I then claim "Incrementing..." I then call increment, but then in lines 22 and 23, I claim it's been incremented, I claim x is now whatever it is--2, presumably--but this program is buggy. What's the problem? Yeah. >>[inaudible student response] >>Exactly. So x has been declared, obviously, on line 18. That is inside main's curly braces. So the simple answer here is that while x exists here, it does not exist in line 32, so this program actually won't even compile. The compiler when I try compiling this code is going to yell at me about some undeclared identifier or something to that effect. In fact, let's try. This is make buggy4. There it is. Use of undeclared identifier 'x' in line 32. And actually, let's be more explicit here today so that this is useful in office hours and at home. Notice that it's a little cryptically written. But the fact that Clang has yelled at us, saying buggy4.c:32:5, is actually useful. It means that the error is on line 32 at character position 5. So 1, 2, 3, 4, 5. That's, in fact, where the problem is. And also, too, keep in mind at office hours and at home, I'm lucky here. I have one mistake. It's going to be relatively easy to fix. But if you get a whole screen full of overwhelming error messages, again realize that the bottommost one might just be symptomatic of the topmost one. So always chase down your bugs from top down because there might just be a daisy chain effect that is suggesting you have way more problems than you actually do. So how could we fix this if my goal is to increment x? >>[student] Make x global. Okay, so we can make x global. Let's take the shortcut that I warned about earlier, but heck, we just need a quick fix, so let's just say int x up here. That makes x global. So now main has access to it and increment has access to it, and so let me go ahead and compile this now. Make buggy4, Enter. Seems to compile now. Let's run buggy4. And it seems to actually work. This is one of these things that's do as I say, not as I do, as I've just done here, because in general, our programs are going to get much more interesting and much longer than this, and if your solution to life's problems is just put all the variables at the top of your file, very quickly do programs get horrifically difficult to manage. It gets harder to think up new variable names, it gets harder to understand what variable is doing what, and so in general, this is not a good solution. So let's do this better. We don't want to use a global variable here. I do want to increment x, so I could obviously-- at the end of the day, this is kind of a silly story because we just do this-- but if I didn't know about that operator or I wasn't allowed to change it in main itself, how else could I implement Ken over here this time not to cube but to increment? How do I change this thing here? Yeah. [student] Pass in x and then return [inaudible] >>Okay, good. So why don't I pass in x and then rather than return it, why don't I just do return x + 1. A couple more things have to change here. I'm on the right track. What else do I need to tweak? Someone else. Yeah. [inaudible student response] I need to change the return type of increment because it's not void. Void means nothing is being returned, but clearly now it is, so this needs to change to-- >>[student] int. int to be consistent with whatever I'm actually returning. Now something else is still buggy here. Yeah. [inaudible student response] >>[Malan] So I need to increment x? [inaudible student response] >>[Malan] Ah, so I need to pass x. So I need to do this here. >>[inaudible student comment] [Malan] So the prototype, I have to change this up here. So this has to become an int, this has to become-- hmm, I actually have a bug down here. Let's fix this one first. What should this actually be? It's got to be an int something. It could be x, but frankly, if you start calling all of your variables x, it's going to get less and less clear which is which. So let's just arbitrarily choose a different naming convention for my helper functions, the functions I'm writing. We'll call it a, or we could call it-- Let's call it number to be even more explicit. So then I have to return whatever the number is plus 1, and now I have to change 1 other thing up here and one other thing up here. What do I have to change on line 21 first? >>[inaudible student response] [Malan] I have to assign it to x. I can't just call increment(x). I need to remember the answer by changing the value of x on the left-hand side. And even though x is now on the left and right, that's totally fine because the right-hand side gets executed first then gets plopped into the left-hand thing-- x in this case. And then lastly, this is an easy fix now. This should just match what's down below, int number. So a whole bunch of changes for a really stupid function but representative of things that we'll increasingly want to do. So make buggy4. I've screwed up somewhere. Oh, my God. Five mistakes in a 6-line program. So what's wrong on line 18, character 5? So I have to declare this, int. Let's see. There are a whole bunch of other errors. Oh, my God--19, 18, 21--but again, let's just clear the screen, Control L here, and rerun Clang. So 5 problems is actually just that 1. So now let's run buggy4, Enter. Whew, x has been incremented correctly. All right. Any questions on how to increment numbers? Yeah. [inaudible student question] >>Good question. How is it that I can just change x to number and the program will know immediately? Again, think of it as this abstraction. So if I am main and Ken is increment, frankly, I don't care what Ken calls his iPad. I don't care what he calls anything that has to do with his implementation of this functionality. This is an implementation detail that I, main, don't have to care about. And so simply changing it consistently inside of the function--number here and number here-- is all it takes so long as I recompile. It's sort of like if you think about many of us, those of you with driver's licenses who have driven or if you've even driven in a car, most of us have no idea how a car works underneath the hood. And literally, if you open up the hood, most of us--myself included-- aren't going to really know what we're looking at, kind of like you might feel with stuff like this right now. But we don't really have to care how the car works, we don't have to care what all of the rods and pistons and cables inside of the car are actually doing. So something like what you call the piston doesn't matter here in this case. Same idea. Yeah. >>[inaudible student question] If there are more uses of the variable x a moment ago, you, the programmer, would have to change them everywhere. Or you could literally do File, Menu, and then Find, Replace--something like that-- but you are going to have to make those changes yourself. You have to be consistent. >>[student] If there are multiple variables [inaudible] A particular order like here, if this was int another number? >>[student] Correct. [Malan] Yeah. Order matters when you are calling the function. So if I were calling increment here with something comma something, there's a direct mapping. The first variable, whatever it's called, is made a copy of the first argument over here. Sorry. This should not be a parenthesis. The second argument lines up with the second one. So order, yes, matters. All right. Sorry. I took the long way to get there. Other questions? All right. So let's see if we can't paint a picture of what's actually going on here underneath the hood, so to speak. This is a rectangle that might represent your computer's memory. Even if you have no idea how memory works or how RAM works, at least assume that you have bunches of it these days. You've got megabytes of it, you've got gigabytes of it, and we know from week 0 that a byte is just what? >>[student] 8 bits. 8 bits, right? So 8 zeroes and 1. So if your computer has a gig of RAM, 2 gigs of RAM these days, you have a billion or 2 billion bytes of memory or roughly 8 billion or 16 billion bits inside of your computer. Unlike the little Wooly Willy example, it's not magnetic particles typically anymore. Increasingly--in laptops at least--it's solid state drives, SSDs, that just have no moving parts. It's all electronic. It's all electricity-based. So think of this rectangle as just representing the 1 or 2 gigabytes of memory that you have. So it's a chunk of memory. The world of computer science has sort of partitioned off chunks of memory to do different things. For instance, if this is your computer's RAM, as suggested by the rectangle there, it turns out that by convention, at the top of your RAM, so to speak, is generally what's called a text segment. Those are the 0s and 1s that you have compiled. So when we've looked underneath the hood at what a.out is, all these 0s and 1s, when you run a program, those 0s and 1s are loaded from your hard drive into something called RAM, and in the RAM they're put at the top. Meanwhile, you have other things: initialize data, uninitialize data. Those 2 swaths of memory refer to global variables, which you don't often use but sometimes if you do, they end up up there as well. Then there's some other stuff: environment variables, which we won't spend much time on, but then 2 important things that will come back throughout the semester, stack and heap. So most of your computer's memory is reserved when running a program for something called the stack and something called the heap. We're not going to talk about the heap today, but we will talk about the stack. The stack is meant to conjure up the visual of the dining hall meal trays in Mather House or wherever you happen to be where the dining hall staff clean them every day, they stack them up from floor on up, and similarly, in memory, there is this idea of putting something on a stack, putting something on a stack, putting something on a stack. And what do we mean by this? Let's zoom in on just the lower half of this picture, your computer's RAM, to propose the following. It turns out that when you run a program like a.out or hello-- whatever the program is that you've written-- again, those 0s and 1s are loaded from your hard drive, which is long-term storage, stays there even when you pull the plug, loaded into RAM. RAM is faster than hard drives--it's smaller than hard drives-- but it's where programs live while you're running them. So you double click a program on a Mac or PC, it's loaded from hard drive into RAM. As soon as it's loaded into RAM, the 0s and 1s go at the way top, the so-called text segment, but then as soon as your program actually starts running, the main function is called, and main, as we've seen, often has local variables, and it has ints and strings and chars and the like. So if your program that you have written or the program that you have double clicked used some variables inside of main, they end up at the bottom of your stack of memory, so to speak. More concretely, what does this actually mean? This just means that if we were going to number the bytes of RAM in your computer, notice that this might be byte number 0, this might be byte number 1, 2, 3, 4, 5, 6, all the way up to 2 billion would be all the way up there at the top. So in other words, when we talk about RAM or memory in terms of bytes, it just means that someone has decided what to number each of those chunks of memory. So when you need 32 bits for an int or you need 8 bits for a char, where do they end up in memory? Conceptually, they just end up at the bottom of this thing called the stack. But what's interesting now is when main calls a function-- suppose a function called foo, just an arbitrary name-- what happens is main is at the bottom of this stack of memory; foo now is put on top of main in memory. So any local variables that foo has end up sort of conceptually above those in main. If foo calls another function called bar, those variables end up here. If bar calls something else, here, here, here. So what's interesting about running a program is that as you call functions and as those functions call functions and as those functions call functions, you build up this stack of functions in memory. And only once a function returns do you start getting that memory back. So one of the easiest ways to run out of memory in a computer program is to write functions that never return. So for instance, let's demonstrate as much with an intentionally buggy program. Let me go ahead and do #include , int main(void), and I'm going to do while (2 > 1), which probably won't ever change on us, and let me go ahead now and do printf. Actually, that's going to be less visually interesting. Let's do this. For int i = 0; i > 0--let's make this mistake--i++. And let's not printf here. Let's practice what I was preaching. Let's have a method here, void chorus, and we'll say int i, and then I'm going to say printf--no, let's make this more interesting. Let's actually not print anything at all. Let's just do this: chorus(i). All right. So this is buggy because why? I'm making this up as I go because the program doesn't actually do anything of interest. But that's not the goal. The goal is to write a program whose main function does what, apparently? Call itself. And actually, we don't need the loop. Let's even simplify this just so as not to lose sight of really the fundamental bug. Main calls chorus to sing some chorus, then I did something stupid and I had chorus call chorus because I assumed someone else was going to implement it maybe, and now this isn't going to compile yet. I need to do what? I need the prototype, remember. So I need to have up here void chorus(int i); So now if I go down here--actually, let's use the bigger window. Let's go ahead and make chorus. Let's go ahead and make chorus. Use of undeclared identifier i. Oh, that was stupid. We don't need the argument. Let's just do this. I wish we had started this way. It would have been a much easier program to write. There. Now let's go over to my terminal window, rerun Clang, and here we go. That was really fast. What actually just happened, though? Well, now I'll add the print line so we can see. Let me say printf("I'm in here")--no variables. We'll leave it like that. Let me rerun make. Let me rerun chorus. And...come on. Keep going. As an aside, why has it not crashed yet? The segmentation fault happened super fast before. [inaudible student response] >>Exactly. So it takes time to print, right? It just takes more work on the computer's part. And there it is: Segmentation fault. So notice just how fast programs run. If you're not printing anything, super fast. But we still got this segmentation fault because what was happening? If you think about how your computer's memory is laid out, this happens to be main, but here let's just call this chorus, and let's call this chorus. And now if I do my aesthetics right, this is just going to say chorus, chorus, chorus, chorus, chorus, chorus, chorus, ad nauseum, and eventually, what's going to happen? If the big picture, literally, is this, what just happens conceptually? The stack overruns the heap. Or, worse, you just overrun everything, including the text segment, which is the 0s and 1s that represent your program. In short, this is just super, super bad. Your program has spiraled out of control. You're using way more memory than you intended all because of a stupid mistake in this case, or in this case a very deliberately done function calling itself. Now, this is not all bad. Functions calling themselves actually has great power when you use it correctly. I have not used it correctly here. So this is not all bad, but the fact that I never actually stop calling myself is a fundamental weakness here of this program. So where are we going with all of this? What's really happening? When I call the increment function like we were doing in those examples, I have a value like 1 that I pass in. I pass in a copy of the number 1, so the following happens. Let's go into the increment example, this guy right over here. Here's what's actually happening. When I call increment and I pass in x, pictorially, what's going on here is this. If I have the value of 1 stored here and I actually call increment, which is now called chorus--the iPad is throwing me off here. Let's call this increment, and we don't know what this next function is going to be. So what's actually happening is here somewhere in main I have a chunk of memory that is storing the number 1. When I call increment, I'm using another chunk of memory, but now I have the copy of 1. When I increment that value, this becomes 2, but then what happens as soon as increment returns? This memory just gets handed back to the operating system, which means all you've done is nothing useful. The 1 that was originally contained in main is still actually there. So where are we going with this? It turns out that in memory you have this back-to-back sequence of bytes that you can put stuff in, and it turns out that we've already seen something that involves putting things back to back to back to back. What is a string based on week 1 and now week 2? It's just a collection of characters. So it turns out just as you can put numbers in memory, similarly can you put characters in memory. And once we start putting characters in memory back to back to back to back, it turns out that using the simplest of things like a for loop or a while loop, we can iterate from left to right over the characters in a string and start massaging them into different characters altogether-- a could become b, b could become c-- so that ultimately, we can take an English sentence that actually makes sense and convert each of those letters one at a time by walking through our computer's memory left to right to actually encrypt. So let's take our five-minute break here, and when we come back, we'll start this process of scrambling information. All right. Before we dive into some crypto and these things called arrays, let me pause for any questions because I feel like I really kind of muddled some of those topics. So let's fix now if we can. We just talked about return values, we talked about arguments, and we talked about this notion, which we'll come back to in the weeks to come, of viewing memory as a whole bunch of these stacked trays, so to speak, from bottom on up, such that each tray that gets put on the stack represents a function that's currently being called. Any questions? Let me ask a question here. Let me simplify this back to what it was before some of our earlier Q&A. The fact that increment has open parenthesis, int number, closed parenthesis-- what does int number represent? [student] An argument. >>An argument. Okay. But what's an argument? [inaudible student response] >>What's that? >>[student] Something that you pass in. Okay, so something that you pass in. And more generally, it's just the input. If you were writing a function and that function's purpose in life is to do something a little different every time you use it, then the only way for that to happen really would seem to be to provide it with input so that it can do something different with that input each time. So you need to specify two things when a function takes input. You need to specify the name that you want to give to that input purely for your own convenience so that you can refer to it in the function that you yourself are writing, as I did here in line 32. But you also need to specify its type because C is a programming language that just requires that if you want a variable, you have to tell the computer what data type it is, in large part so that it knows how many bits to allocate for that variable because it could be 6--sorry, it won't be 6. It can be 16, it can be 8, it can be 32, even 64, but the computer needs to know. Now, the int on the left-hand side represents what, by contrast? [inaudible student response] >>What's that? >>[student] Type of function. The type of a function and, more specifically, the type of its output. Right. So whereas the thing in parentheses represents its input, if any, the thing to the left represents its output. And in this case, increment apparently returns an int, and so int is the return type of this function. What does it mean to return? Literally, you use the keyword return and then if what you are returning to the right of the keyword is an integer, then that is indeed consistent with what we have promised. You could not do something like this--hello, world--because that is a string. Obviously, it is not an integer. So in short, the burden is really on us, the programmer, to be specific as to what we're returning and to then actually go about returning it. The context here now is that your computer's memory is a gigabyte, 2 gigabytes-- whatever--maybe it's more, maybe it's less, but the computer views it as having different sections. Something goes down there, something else goes up there, different stuff goes in the middle, and today we just begin telling the story, but we'll come back to this over time. For now, the only piece of memory we really care about is the text segment because that just represents the 0s and 1s that Clang has outputted. So when you run a command at the keyboard like a.out or you double click an icon on Mac OS or Windows, your program is loaded from your hard drive into RAM and it's plopped at the top of your computer's RAM, so to speak. Meanwhile, as your program starts running and main gets called in the program you wrote or the program Microsoft or Apple wrote, any of its local variables end up down there at the bottom of your computer's memory. But if main calls another function that itself has variables or arguments, they end up above it. And if that function calls something, they end up above it, above it, above it. And only once a function is done executing does the stack of trays, so to speak, start to get lower and lower. And this is what then, in a nutshell, explains why when you call cube or you call increment, you're passing in a copy of the value. And what that means pictorially is that you're literally writing the number 1 in another part of memory, changing that 1 to 2 in the case of increment or to an 8 in the case of cube and then throwing that memory away as soon as the increment or the cube function returns. Question. [student] Where are global variables stored? Global variables are stored in what's currently called the initialized data or uninitialized data, the difference being if you have a global variable and you assign it immediately a value with the equals sign, it ends up at the top there, and if you just say int x; with no value, it ends up slightly lower in RAM simply by convention. Other questions? All right. So this picture will come back as we get more powerful with what we can do with the computer, but for now, let's have a brief intro to cryptography, a specific type of cryptography that doesn't solve all of the world's problems but does solve some of them. In this case here, we have something called secret-key cryptography. Secret-key cryptography, as the name suggests, derives its security from a secret. For instance, if you were back in grade school and you were passing a little secret love letter to the boy or girl you were crushing on, if you wanted to pass that note through the audience, you probably wouldn't write such a note in English or whatever your native language is. Rather, you might encrypt it or you might just send them a text message these days. But you might actually pass them a note throughout the classroom. And to do this securely in such a way that your friends and the teacher don't know what you're writing, you might come up with a fairly simple algorithm, young though you might be, to just scramble the words. So instead of writing a you might write b, instead of b you might write c, instead of c you might write d, and so forth. Or you could come up with a more sophisticated translation of letters to different letters. But the catch is the boy or girl to whom you're sending this note needs to know something, which is what, obviously? >>[student] What you're sending. What your secret is, like what is that mapping between a's and b's and c's and d's. Is it just adding 1 to each of the letters to go from a to b, b to c? Is it more complex than that? So you and your crush need to have this secret information, but there's kind of a catch-22 here. If this is the very first time you're sending this love letter through the class, how is that boy or girl going to know what the secret even is? So secret-key crypto does not solve all of the world's problems, and there's actually a relationship here that we'll come back to towards semester's end. Similarly do most of us not know someone that works, for instance, at Amazon.com, and yet many of us have probably bought stuff at Amazon.com, and we've been taught to assume that these e-commerce transactions are secure. The URL probably says https, there's maybe a silly little padlock icon somewhere, there's some kind of cryptography securing your credit card information between you and Amazon.com. And yet if cryptography involves knowing some secret and yet I don't know anyone at Amazon and I've certainly not arranged any kind of secret with someone at Amazon, how is my computer or my browser doing this? It turns out there's other types of cryptography altogether that solve that problem. But for today, we'll focus on the simple one where you can arrange in advance to know some secret like +1 or some mapping between a's and b's. And the process of cryptography generally involves this. You have some plain text, depicted here at left, you run it through some kind of algorithm or procedure for encrypting it-- maybe that's just a becomes b, b becomes c-- and then you end up with ciphertext. Meanwhile, once your crush receives this secret note, he or she has to then decrypt it by generally reversing that algorithm so as to get back the plain text. There are physical incarnations of this. For instance, this is a little secret decoder ring, and this is a ring in the sense that there's two dials here. On the outside periphery of this thing, there's letters A through Z, although they're in random order, and on the inside, there's actually some numbers such that with this ring you can kind of turn the outside but not the inside in order to line up numbers with letters. From a movie called A Christmas Story, you'll see that little Ralphie was so eager to figure out what Little Orphan Annie's secret message was to him that had been communicated, I think, in the form of numeric messages on a cereal box and you had to accumulate all the little cards that came in the cereal box, you had to mail them in, you had to get back the secret decoder ring so that you can finally figure out what the mapping is between letters and numbers or letters and letters. How in a computer can we go about implementing or representing things like this? We need a way of expressing ourselves a little more flexibly than our variables thus far have allowed. We've had ints, we've had chars, we've had floats and doubles and a few others, but those are individual pieces of memory that don't really allow us to express things like words and sentences and phrases. Indeed, we've called such things strings, but we promise that this is really just a simplification in the CS50 library that we're intending to peel back. And so let's start to do that here. Let me go ahead and open up a file-- all of these files are available, as usual, online--called array.c to solve a problem unrelated to strings but that paints a picture here of how we might use something called an array. An array is a data type. It's a type of variable of sorts that has multiple smaller data types inside of it back to back to back to back. So for instance, if we wanted to write a little program that gives you your quiz average for a course like 50 that has 2 quizzes, you could very easily write this program based even on some of last week's material by using GetInt and a couple of variables: int quiz1, int quiz2. And it's pretty straightforward. It's maybe 10, 20 lines of code max to implement a program that asks the user for 2 quiz scores and then computes their average by adding them together, dividing by 2, and then printing the results. We could probably do that pretty readily now after some number of minutes. But the problem is that suppose that 50 had 3 quizzes or 4. Suppose that you wanted to use the same program for a class that had weekly quizzes. Think about a class that has weekly quizzes. If there's 16 or so weeks in a semester, now you have 16 variables: int quiz1, int quiz2, int quiz3, int quiz4. As soon as you start seeing this redundancy, this copying and pasting of code, it should start to make you wish there were a better way. And thankfully, because of arrays there is. So let's do this. First, let me introduce a very simple thing that we've not used thus far, but you'll see it occasionally in code. This is what's generally called a constant. So it's a constant in the sense that this value never changes. The human convention when creating a constant is to use all capital letters just so that it really stands out in your code, and the special keyword that you use in C is #define. So we say #define, then a space, then the word that you want to use for the constant's name and then the value of the constant. Notice this is different from assigning something to a variable. There's no equals sign, there's no semicolon. This is what's generally known as a preprocessor directive, but more on that another time. For now, this creates an unchanging value called QUIZZES whose actual numeric value is 2. So anywhere you see QUIZZES, QUIZZES, QUIZZES throughout this file, that's just the number 2. If I look at main now, let's see how this works. First it looks a little cryptic, but it's all stuff from week 1. Ask the user for grades. How do we do this? In line 22--this is really the juicy part--I declare a float but not just a single float. I'm declaring, rather, an array of floating-point values. That variable is going to be called grades, as implied here, but the only piece of new syntax then are these square brackets. The fact that I've said float grades and then open bracket and then a number-- notice if this is a constant this is just like we did this-- this means, "Hey computer, give me 2 floats and let's collectively call them grades." This is in contrast to a much more tedious process like this: float grade1; float grade2; and so forth. So an array allows us to implement this idea but much less messily, in such a way that we can write 1 line of code instead of, say, 16 for a 16-week semester. I didn't want to hard-code 2 because if you think about this now logically, suppose next year CS50 changes to 3 quizzes instead and I had the number 2 here, I had the number 2 here, I had the number 2 here, the number 2 here. It becomes very tedious and very easy to screw up and to accidentally change 1 value to 3 and miss some other value of 2. So I'm going to instead abstract this away and use this constant that, as its name suggests, never changes. And now no matter whether we have different quizzes this year or next, I just have to change it in one place up here at top. So that's all a constant is. Meanwhile, the new conceptual feature is that of an array. So the square brackets give me this many floats and lets me collectively call them grades here. So now let's see what I'm going to do. Here in line 24 is the beginning of a for loop. This is really nothing fancy. It's just using QUIZZES instead of a hard-coded number. But there's nothing intellectually different there from last week. This is just printf, so printf("Quiz # %d of %d:") because I just want to print out give me quiz number 1 of 2 and then 2 of 2. So this is a purely aesthetic thing. But the interesting part now is in line 27. In order to fill in one of the two placeholders with a floating-point value, you again use square brackets. In this case, I'm using i because this for loop has started with i equaling what value, apparently? [student] 0. >>[Malan] 0. So on the first iteration of this loop, it is as though I wrote this in code, but on the second iteration of this loop, it is as though I wrote this in my code. But the fact that I'm using a variable is perfect because, as the name suggests, it's varying its value on every iteration, so I'm filling this array one spot at a time. What does this array look like? The reason I drew the super simple rectangle on the screen here before was for this reason. An array is just a chunk of memory followed by another chunk of memory followed by another chunk of memory and so forth. So if my array is of size 2 in this case here, all I would be doing by typing in my quiz scores like here--I got 100 on this one and then I got a 99 on this one-- then this memory might not even be used because I've only asked the computer for an array of size 2. Those squares are still there, right? You still have 2 gigabytes of RAM even if you're only asking for 2 floats. So the idea behind arrays is that the computer just takes a chunk of memory and then apportions smaller pieces back to back to back to back. And so that's all an array is. It's a contiguous chunk of memory inside of which you can put things. This happens to then do just some boring arithmetic. If I scroll down here, this is where I then iterate over the array. I come up with the summation of all of the values in the array, and then I use the round function here to actually do the sum divided by QUIZZES. But let me wave my hand at that as sort of enough arithmetic for now. But all that's doing for me ultimately is computing an average. So first quiz plus second quiz divided by 2 and then printing it out as an int. But let's now transition to a different example called string1, which paints a similar picture but using strings. Let me go ahead and simplify this for just a moment. Forgive the indentation for now. Notice in line 19 of this example, I get a string from the user. But notice what I'm next doing in lines 22 onward. I'm actually iterating from i up to--and this is a new trick--strlen, string length. This is a function that comes with C that if you pass it a string, it tells you how many characters are in that string. That's all. And the fact that it's strlen instead of string length is just because it's more succinct. Thirty years ago, people liked to write things as succinctly as possible, so we've kept that convention here. i++ just means increment i in each iteration. And now notice this, which is really interesting. In line 24, I say, "Computer, give me a character, 8 bits, and call it c." But what is this on the right-hand side saying? In English, what does that represent? [student] The first character in the array. Exactly. Give me the first character in the array. Or, more generally, give me the ith character in the array. And realize it's important now that as computer scientists, we're actually counting from 0. You don't have the discretion now to start doing this. Now you have to behave in accordance with the computer's expectations and count from 0 because [0] is going to be the first character in a string, [1] is going to be the second, [2] is going to be the third, and so forth. So this program, if I compile it, this is again string1, so make string1, and now I've run string1 in my terminal window. It's waiting for input, so I'm going to type in David, Enter, and now it prints D-a-v-i-d all on different lines because notice what I'm doing. I'm printing one character at a time. We won't go into detail today on this, but I deleted a moment ago this check here. It turns out that if the user is misbehaving, adversarial, or just confused, you can actually fail to give a string of some length. If you hit the wrong key on the keyboard, you might give no string at all, or if you're malicious, you might try to paste in a gigabyte's worth of an essay to fill this string, and if the computer runs out of memory, it turns out that we're going to get back this special value called NULL. So for now, just know that there's this special value called NULL that will allow us to check when we're out of memory, among other things. But if I open up now string2, notice one difference here. Notice one difference here with string2. With string2, this for loop is a little different. Let me delete the NULLs so that we can talk about those another time. What's different about the for loop this time? I can go back to the previous example. So that's version 2, this is version 1. 1, 2. 1, 2. The strlen call is where? It's in the first part of the for loop. Any thoughts as to why I'm doing this? Yeah. [student] So you don't call the function every single time. [Malan] So we don't call the function every single time. Exactly. Recall from for loops that they're super simple once you sort of understand that this is the initialization, the condition, and the update. The problem is that the condition happens on every iteration of the loop. And so in this example here, what is bad about the fact that this is my condition? [student] You're calling strlen. [Malan] You're calling strlen again and again and again. But once I've typed in D-a-v-i-d, the length of that string is 5, and it's not going to change on every iteration of the loop because the string is still D-a-v-i-d. So this is a hint at what's going to become an increasingly important idea known as a design decision where just don't make the computer do unnecessary work. Just as a sneak preview of pset2, pset2 in the standard edition is going to challenge you to actually implement some number of ciphers, some number of encryption algorithms, so that you can both encrypt and decrypt secret messages much like the one Ralphie there decoded. In the hacker edition of pset2, we're going to go a little further. We're going to hand you a file from an actual computer system that contains a whole bunch of usernames and actual encrypted passwords, and the challenge for the hacker edition is going to be to crack those passwords and figure out what cryptography or what secret was used to actually generate those passwords. And we're going to do this by using a new feature here of C that I'll give you just a demo of known as command-line arguments. It turns out, as some of you may have seen in section or in textbooks, main does not always have to be void in parentheses. It turns out that main can also be written like this, with two arguments, argc and argv, where argc is the number of words that you type after the program's name on your command line and argv is the actual words. And as the square brackets there suggest, argv is apparently an array. It's going to be a string after a string after a string in memory. So what we're going to be able to do starting with pset 2 is something like this. If I make argv1, which is an example we'll come back to on Monday, and run it, notice that it doesn't seem to do anything yet. It just prints out its own name. But if I say goodbye class, notice that this program apparently iterates over each of the words that were typed at the prompt. And the means by which we will gain access to words that the user has typed at the prompt is by changing main starting this weekend from int main(void) to int main(argc, argv) and thus will be born command-line arguments. And once you get really sophisticated at this, you'll be able to write really trippy programs such as this one here, which goes above and beyond some of the functionality we've done thus far but all quite powerful. So we'll leave this with this on the screen, and we will see you on Monday. [CS50.TV]