CARTER ZENKE: OK, well, hello one and all and welcome to CS50's week two section. This week we learned about arrays. That is how to store data inside of a computer using our very first data structure, if you will, this way of storing data back to back in a computer's memory. So the goal of these sections here is to help you bridge the gap between lecture and this week's problem set. So we'll go through a few of the lecture topics, have you all ask the questions you want to ask, and get some practice that might help you as you go off and work on the problem set individually on your own. So to begin, my, name is Carter Zenke. I'm one of the course's preceptors here on campus. If you want to be in touch with me, feel free to email me at this email right here, carter@cs50.harvard.edu. But a brief overview of today. Today will look a bit like this. We'll begin focusing on this idea of compilation. How do we take the code that we write in C, for instance, that source code we write in a file, and how do we convert that to the zeros and ones that a computer actually understands and can run? We'll then focus on this idea of arrays. How do we store data more efficiently than we've seen before? And then we'll focus in particular on this idea of a string. How do we store characters that then themselves form entire words or sentences inside of our computers? And finally, towards the end, we'll focus on this idea of command line arguments. So you've already been using programs that use command line arguments in CS50 already. But now you get to see exactly what they are and how you could write programs that actually use them yourself. So let's dive right into compilation then. So in lecture, we learned that compilation was a way of taking the source code we write, let's say some code in C, and converting it into the actual binary a computer understands. And our computer, as much as we might like to think so, it doesn't understand C as a language itself. There's an extra step that we have to follow called compilation that takes that source code and converts it to the binary that our computer actually understands at the end of the day. So here, for example, is some piece of source code in C. And I'm curious, for those of you who are here, can you spot the bug in this source code? This is some C code here. If I were to run, make to compile this code, I might get some error. And I'm curious if you can spot what that error might be. So I'm seeing a few people saying that we're missing the F in printf. There is no function in C called just print, at least in the standard library. So we have to say this is printf. And the point here is that when you're using source code, these kinds of bugs are, well, they're more obvious to catch. If you're writing source code, it's kind of obvious, at least more so in other cases, what bugs you might have. But now let's consider, like we learned from lecture, that the next step in compilation is taking this source code and converting it into this middle language called assembly code. And this is an example of assembly code here. And I'm curious, for those of you in this room, can you spot the bug in this program? Or could you tell me if there is a bug in this program at all? Feel free to take a look at this code, even if you're not familiar with assembly. I'm seeing some shaking heads here. So the point here is that you get this lower level language, going beyond C, which is our source code, moving to assembly code, it gets a little harder to spot the kinds of bugs that arise in our programs. And now let's take it one step further. Let's go from assembly code down to the binary itself. And I'll ask the same question. Could you spot the bug in this code? Feel free to chime in if you think you have it. I'm hearing some folks say, not a chance. You can't find the bug in this code. That's to be expected, right? Nobody among us is going to be an expert in binary that can kind of parse through each individual 0 and 1 and find the bug in this code. So there's this idea of trust in computer science, that when you run this program, called Make at least in cs50, or other programs, like compile other source code, you're kind of trusting that it's going to take your source code as you have it and compile it exactly as is down in to binary. But you might not know if somebody were to be a bit of like a hacker and try to maliciously alter your compile to introduce a bug on the way of converting your source code down to machine code, like 0s and 1s. So it goes to show you that often in computer science we use programs that we need to-- we use programs that we aren't quite sure whether we should trust or we shouldn't. And the only way to find out is to actually be trustworthy individuals. So as you go off in the world of computer science and you write your own programs, write your own source code that converts things perhaps from source code to machine code, you have to kind of trust yourself to be trustworthy in these cases to help us make the programs we want to make at the end of the day. So we'll focus not so much on technical parts of compiling here, but more so on the actual ethical aspects of it too. So questions then on compilation, this idea of converting source code to machine code? Any questions so far? All right, so the key thing to take away here is just that when you are in CS50 and you're working on compiling your code, you'll use this program called Make that converts your source code in C down to machine code. As you go off and learn more computer science, you'll see just how up in the air these things can be and how much you have to actually trust the programs you're using along the way. All right, and a question here. What will we be using in the real world to compile our C code? So in the real world, just like in CS50, you'll likely use a program called Make. And there are various options that Make can have. In this case, in CS50 we've kind of specified those for you. As you go off in the world of computer science and you try to expand your horizons, you might yourself set the options for Make to more clearly specify what you want the end result to be when you convert that source code to machine code. All right, so let's keep going here. And our next topic from this week's lecture was this idea of arrays that is a way of storing data in a computer's memory. So in this week's problem set, you'll also get to see a bit of a game that's popular I believe kind of around the world, one called Scrabble. And if you're not familiar, in Scrabble you get these individual letter pieces, like ones for W, or ones for H, or ones for D, for instance. And each of those letters has on it a certain point value. So let's see. Letter H, that little square that has H on it, that has four points that has been awarded. D, that little square that has D on it, that has two points associated with it. And as you play this game, the goal is to take these letters and convert them into entire words. So if you had, for instance, something that looked a bit like this, you had these five letters, what word could you make from these five letters? You could probably make hello. So you can take all these five letters, convert them into this word, hello. And in Scrabble you'll play a word that looks a bit like this. So notice here that H is worth four points. E is worth one point. L is worth one point. And O is worth one point. And if you add all of these points up, 4 plus 1, plus 1, plus 1, plus 1, well, you get a total of 8 points for playing this word. Now there's actually a correspondence conceptually between this idea of Scrabble and this idea of arrays. So in the same way that we're taking individual pieces of data or individual squares of letters and convert them into one long word or one long space in computer memory, we're doing the same thing with arrays. We're taking these individual pieces of data and lining them up back to back to back in a computer's memory to store that data even better as we go and work on our programs. So let's think ahead. And in CS50 you'll actually get to make your very own final project. And here is, for example, one student's final project in CS50. They wrote a website that allowed you to keep track of your hours of sleep each night. So maybe you yourself could make something similar to this by the end of the course. But they allowed you to go to their website, type in the number of hours you slept that previous night. And they would store it for you and keep track of that day after day after day, so you could look back and see how many hours you've slept over time. Now, if we only had things like variables and not arrays, we might be for something a bit like this. We might have to store this data in individual pieces kind of around our computer's memory. And we might even give them individual name, something like, well, on night one, we slept seven hours. On night two we slept eight hours. On night three we slept six hours. And now I'm going to ask you this question here. Why might this not be very well designed? If we had to create one variable for every single night of sleep, why might that program not be very well designed? And what can we do better perhaps? Any ideas? If we wanted to add more nights, that might not work. In this case, I would say if we're using one variable for every night, I mean, I think you're right. So if we wanted to later on edit our program, we generally specify all the variables that are part of that program at the beginning. So if we wanted to add more, I couldn't quite do that. We'd have to find them all over again if we wanted to add them up. That's a nice idea. So we'd have to think back and be like, OK, did I use the variable night1 for this, or night2, or night3. Which one belongs to this particular number? So I would say that this isn't the best way to store our data using all these individual variables because it can get very hard to keep track of. And so arrays here actually help us solve this problem. They let us take our individual pieces of data and put them all in a metaphorical and actually kind of physical line inside of our computer back to back to back in our computer's memory. So for instance, here's what it might look like to have each of these hours inside of an array. We'd again, just put them back to back to back in our computer's memory. And we would then give this entire collection a single name, let's say nights. And so now we could see, well, on this first night, it looks like we slept about seven hours. On that next night, the next integer here, we slept eight hours, and then six, and then seven, and then eight, again, for a total of five nights of data. Now, if we wanted to access not just this entire list of values, but some in particular, well, we have some special syntax we can use that C gives us. We could say something like this, night[0]. And that will return to us, that will give us that very first value in our array, so in this case, 7. And you might be asking here, why not nights[1]? Well, in computer science, it's kind of a convention that we start counting from 0. As you saw when we wrote our very own for loops, we began them by saying often that i equals 0 or j equals 0. We start counting from 0. And in this case, I would argue it actually kind of makes sense. Like nights[0], 0 means start at the beginning of this array called nights and don't move any further, move 0 places. If we're looking at the beginning of our array called nights and we move 0 places, well, we get back this number called seven. But what if we did this, we said night[1]? Well, we begin. We'd look at the first place in our nights array. And we'd say let's move one step over. OK, now we found that second value. In this case, it was 8. And in the same way, we could say nights[2]. Well, let's begin at the very beginning. And let's find 7 here. But then we're moving two spaces over. So we go 8, and then 6. And now we have that very third value in our array. So key idea here, we start counting from 0 as we're working with arrays. And there's a technical name for this, which is that arrays are 0 indexed. And what we're doing here is using this index, or this number, to find the value of the array that we're actually looking for. So to make this a little more apparent too, you might often draw out an array. And you might try to assign an index to each of its elements. For instance here, we have this same nights array. But down at the bottom, we've indexed each of the elements. So the very first one is assigned the 0 index, the next one the 1 index, the next one the 2 index, and so on. So we could use nights bracket any of these numbers on the bottom to get whatever number we're looking for. Now, questions here on arrays, this indexing process here? What questions do we have? OK, seeing none for now. But feel free to keep chiming in if you'd like. Now, one common question we get is, how can we then actually create an array? We've seen the structure of an array, visually what they look like. But how do we actually create a structure for one? And for that we actually need to keep in mind three different aspects of an array. If we want to create an array, C needs to know three things about that array. So for instance, one of the things it needs to know is, what is the name of the array? What should we call this collection of data in our computer's memory? The next thing it needs to know is, what is the size of this array? How many elements are we storing? In this case, our size is five. It also needs to know though what kind of data we're storing or what type of data is inside this array. So we also tell it what type it will store. And in C arrays only store a single type of data. So in this case, what type might we be storing? It seems like we were storing integers. So to combine these three ideas of the name, the type, and the size of the array, we put all this together in C syntax that looks a bit like this int nights[5]. And so to break this down, we first say the type of whatever we're storing in the array, in this case, an integer. Then we say the name of the array, nights like that. And then in brackets we put the maximum size of that array, how many elements are going to be inside of it. In this case, we had five. And note that this counting is not zero indexed. If I said int nights[0] here, I would be saying I have an array of 0 elements, which doesn't make sense. If I'm going to have an array, I need to have at least one element in it. So make sure that you keep in mind this is not zero indexed, even though later on when you actually try to access values in your array, that will be zero indexed at the end. Now, if we wanted to add items to this array off the bat, let's say we wanted to create the array, declare it like we did here, tell C what type it is, what its name is, how many elements it had, and also initialize it with some values, we could do it with this syntax right here, using braces and then followed by the values we're going to input into that array spaced out by commas here, So 7 comma 8 comma six comma 7 comma 8. Now, one question I see coming up is, can we change the size of an array? So notice here we declared that this array had a size of five. And in C you cannot change the size of an array. If I say it's five at the beginning, it has to be exactly five. We'll see ways later on in the course that you can actually try to allocate more memory and change the size of an array. But a lot of it just involves copying what you currently have in one space of memory into a new space overall. More on that in week four. But for now, you can say that there's really no way to change the size of an array. So if you think you might need a lot of values, you might need to make a lot of spaces to have those values in your array. Let's see what other questions we have here. So let me find a few. Can an array exist on multiple planes, like a 3D array for instance? So you could think-- this is getting a little advanced here-- of an array that actually contains arrays inside of itself. And that is a perfectly valid thing to do in C. You could have an array, where each element of that array is an array in itself. And that way you have kind of like a 2D array, a bit like a grid. And then if you think even further, well, you could have an array where each element is itself an array. And each of those elements then have arrays as their elements too. And that gets you to this like 3D kind of structure. No need to worry if that made no sense to you. But generally, you can take arrays and put other arrays inside of them at whatever level you'd like to do that at. Other questions here too. Let's see. A question here about negative one indexes. So if you've programmed in Python, you may have seen this kind of similar syntax of writing the name of some list, and then typing bracket negative some value, like negative 1 for instance. I believe in C this is not possible. So that's a feature of Python, which gives you, I believe, the last element in your list. But in C there's no such thing as a negative one index. Indexes must be positive. I have a question here. Let's say we have this array of five elements here. Could we add maybe only three and later on add the other two? You certainly could. So if you were to go back to this model of declaring your array, you could specify values for the first three in this array and later on add the other two. Now, you have to be careful, though, because if you don't specify what those values should be, those final two values, they could probably be literally anything. So you don't want to touch them unless you're sure you've already set them from the beginning. Now, if you follow this kind of syntax here, you have to specify every single element of your array. You can't leave any out. All right, so I think that covers most of our questions here about arrays. So let's keep going. Let's actually get some practice using arrays here. So we have a brief exercise in which you're going to write a program that takes an array of integers or actually builds an array of integers. And we want it to be the case that each integer is 2 times the value of the previous integer. So for instance, you could think of a list like 1 and then 2. And then what's 2 times 2? Well, 4. And then what's 2 times 4? Well, 8. And then 2 times 8 is 16. So the entire list is 1, 2, 4, 8, 16. And we want to in this program print the entire array integer by integer. So let's try this out. I'll go over to my code space here. And I'll write up this program. I'll call it, let's say, just double.c, meaning I'm going to double each element of this array. And now I can see here that I have a file called double.c. Now, what's the first thing I should do if I'm writing a new program in C? Any ideas what should I usually do? I want to include the header file. So I'll say I want to include the CS50 header file, which gives me access to things like strings and so on. And I also want to include stdio.h, which will allow me to print things out to the screen. Notice that the standard io library, or stdio library, contains functions like printf. So I'll include that here. And I'll write the beginning of my program int main void and follow it up with what? Well, I probably first want to declare this array, that is to tell C exactly the important features of it, like what type will it be storing? What name will it have? What size will it be? And so on this very first line I'll do that. We're probably going to be storing what type of data here? We want to be doubling numbers, and whole numbers in particular. So we're going to be storing integers. So I'll say int here. Now what should the name of this array be? It could be generally anything. But I think for me I'll just call it something like sequence, that is some sequence of values that will double every time. And then how many elements should we store? I might just say let's go ahead and store five off the bat. We could change this later if we want to. So here I have an array called sequence that stores five values. And what type are those values? Well, they're integers here. So if we go back to our problem statement, we saw that the first element of this array is 1. Now my question for you, how do I access the first element of this array using the syntax we saw earlier? What could I write to find the first element of sequence? You could probably try something like this. I could write the name of sequence and then bracket 0. So bracket 0 means start at the beginning of sequence and move 0 steps, find that very first element in this array. And if I want to assign it some value, well, I could do that here in C. I could say sequence[0] equals some value. In this case, I'll say it equals 1. So now I set the very first value of sequence. And why don't I print it out while I'm here? I'll say printf % i for that integer format code, backslash n. And now I'll say sequence[0]. So to be clear here, what I'm doing is holding a placeholder for an integer. I'm going to put inside that placeholder the value of sequence[0], which according to line 8 we just set to be 1. So now here comes the trickier part. How do I try to go through this array and update each of its values over time? Well, I set the very first one. But now I want to more dynamically set the rest of them. I don't want to do this. I don't want to say sequence[1] equals 2, sequence[2] equals 4. That's getting a little in the weeds. I want to automate this process for me. What kind of structure that we've already seen could we use? I'm seeing this idea of maybe some kind of loop. And we learned in our last section that a for loop is good when we know how many times we want to loop over all. So here we saw our sequence had a total of five values. We already set the first one. So I think we want to loop a total of four times to set the second value, the third value, the fourth value, and the fifth value. So I could write a for loop like that. I could say for int i equals. And now here's a question. What should i be equal to? Well, i in this case, let's say it refers to the index of the array we're trying to set. So what's the very first index we want to set? We already did 0. But now we should do 1. So int i equals 1. Now, how long do we want to iterate for? Well, at least until we get to i is still less than 5. So I'll say i less than 5 and then i++. So now we have i going from 1 to 2 to 3 to 4. And that will update our next four values. It will not go to 5 because, again, in this array, there is no sequence[5]. That would be going beyond the bounds of our array. Even though there are five elements, again, we index from 0. We can't move five spaces total. We can't move forward five spaces from the beginning of our array. OK, so now we have this. And the question becomes, how would I set this value of sequence? Well, I know I want to set sequence[i] in the first iteration. This will be sequence one. And the next iteration it'll be sequence two. But how should I configure this value? I know I want it to be 2 times the previous one. I'm seeing a few ideas here. Some of them involve actually doing a bit of math inside of the brackets here. And that's something you can actually do in C. So I could say maybe let me get the previous value. What is that value? I'll say sequence[i]. And then to look behind this value, I'll say minus 1. So if I'm currently at i equals 1, I'll be saying sequence[i] sequence one, equals sequence bracket i minus 1 or 0, sequence[0], so the previous value here. Now, if I want to multiply that by 2, I could do the very same thing I've done before in C. I could say star 2, which means multiply this particular value by 2. So again, if i is 1 here, I'll say sequence[1] equals sequence bracket-- well, 1 minus 1 is 0. sequence[0] times 2, well, that will set the next value of sequence, and so on and so forth. So once I do that, I can say maybe I'll print out this value. What is the new value of sequence[i]? And then I will go ahead here. And I'll say printf backslash n. Actually, I don't think I need backslash n here. I already have it up there. So I'll leave things as is. And this is our entire program. So let's see if we can run it. I'll go back to my terminal. And I will say make double, make double to compile it. I see no errors. I'll now try this. I'll say ./double. And I seem to be getting somewhere. I have 1, 2, 4, 8, and 16. So I'll go back to my code here. Let me ask, what questions do we have on creating an array, cycling through its values with a for loop, setting them as we go? For those of you who are feeling a little more comfortable, there's another way to do this too that doesn't involve separately setting a sequence[0]. You could simply declare your array and have a single for loop that sets things for you. I'll leave that piece up to you, though, to do on your own. Now a question I see here about this program's design, wouldn't it be better designed to have a variable that says what the size of this array is? For instance, let's say I'll set int size equals 5, like this. And now, maybe I'll replace this with size and replace this with size. And now I could change this it seems in one place. I could make this 10. I can make it 7, or 6, or so on. So I'll leave it at 5 for now. And let's see if that actually works here. So I'll go back to my terminal. And I will do this. I'll type make double. And I'll run it again, ./double. And that seems to work. So I'll go back to my program. Maybe I now want-- let's go with eight numbers overall. I'll go back to my terminal. And now I'll say make double again, ./double. And I seem to have allowed myself to pretty quickly change the size of this array and print out a longer sequence as I go just now by changing one particular value. And this is actually a common, let's say, pattern you'll see in writing well-designed programs. It's not really a good practice to specify what we call a magic number, that is a number in here that we're not quite sure what it is, what it refers to. And it might repeat throughout our program. If you have a number like that, best to create a variable and change it in one place, so you don't have to go through later and update all the places we had, for instance, 5. Now, another question I see here is, could we use getint? Well, we know in the CS50 library we have this function called getint lets the user type in what value they want to give. I'll try that. I'll say size is getint. And I'll say enter a size, like this. And now I'll go back to my terminal. And I'll say make double. And I'll run ./double. Now I'll say, let's go back to-- maybe let's go back to 6. That seems to be right, one, two, three, four, five, six. Now I'll type make double and then ./double again. And now I'll type 9. And I'll see I have an even longer list. So it seems like we could even take user input and then decide the size of our array after that. All right, other questions here on this program? All right, so let's keep moving on. And let's focus now on this idea of strings. So we've seen this idea of arrays, which is this structure we have to store data back to back to back in a computer's memory. And it turns out that strings are actually not all that dissimilar from arrays. In fact, strings themselves are a special kind of array. So consider here, again, our Scrabble example. We had these individual pieces of letters, like H, E, L, L, and O. And they all formed together this word hello when we put them all together back to back. Well, in the same way do strings actually work. We can take individual letters like these. And we can then do something a bit like this. We can put them all together and make entire what we might call in this case a phrase. So strings are nothing more than arrays, where the elements are characters. So here we're now seeing that we have this array called phrase with the letters H, E, L, L, and O. And we can use the very same syntax that we saw earlier. I could say phrase[0], which gives me the very first element of phrase. I could use phrase[1], which gives me the E here, and then phrase[2], which gives me the L. So I'm able to do the very same things I could do with arrays, but now with strings. One fancy feature though that you should pay attention to, particularly for this week's problem set, is that we represent characters in C underneath the hood using integers or numbers. Remember from an earlier lecture we learned about this idea of ASCII, or the American Standard Code for Information Interchange. And we saw a mapping a bit like this, where A maps to this integer 65. B maps to this integer 66. C maps to this integer 67. And so when we see these numbers, 65, 66, 67, and they're the type of character, we then actually convert that to a character. We print it out as a character overall. So consider then that this phrase that we see here, hello, well, it could also be a set of numbers, 72, 69, 76, 76, and 79. These are the ASCII codes that correspond to those letters we saw a little earlier. So with that in mind, let's think about writing a new program, one that actually tells us if a string has characters that are in alphabetical order or not. Now, we can assume here that all the characters are uppercase. So let's begin. I'll go back to my code space. And I'll now create a new program. I'll call this one alphabetical.c. And I'll do the very same things I did with double.c. I'll make sure to include CS50.h. I'll make sure to include stdio as well, include stdio. And then I'll also say int main void. And now I can write the rest of my program. So maybe the first thing I want to do is get a string from the user. So I could say, string phrase equals get string enter a phrase. So I'm using the CS50 libraries get string function. And now I'm able to ask the user for some phrase. But now I want to ask that question, is this phrase in alphabetical order or is it not? And it seems to me like the very first step there would be to go through every individual character in our string. We have to have a way of looking at every character to test, is every character in alphabetical order or is it not? So what can we do to loop through this string or really this array of characters? I'm seeing this idea of a for loop again. So we used it for our array of numbers. And there's no reason that same approach can't work now when working with a string because, again, a string is just an array, but an array of characters. So I'll say this. For int i equals 0. We'll begin at the very first character in our phrase, i is less than. What should i be less than? I mean, I don't know quite how long this string is. If I typed in hello, it would be five characters. If I typed in goodbye, it would be longer. What could I do to find the length of this phrase? So I'm seeing a few folks who are catching on to this, which is that in lecture I believe we saw this function called strlen, S-T-R-L-E-N. And strlen actually can tell us the length of a string if we call it and give it our string as input. So strlen lives in this library called string.h, or our string in general. And the header file is string.h. Now, if I want to test how long this string is, I could say int length equals strlen, and then pass in my string, in this case, the one called phrase. So now I have this variable called length that I could use in for loop, i is less than length i++. So whatever the length is, I'll make sure to first calculate that and then will I test every individual character in my string making sure not to go past the length of that string. Now, a few other ways to do this too. I could also say int i equals 0 comma length equals strlen of phrase, like this. And this is getting a little long. And I have to zoom out for this. But this allows me to put everything on a single line. And it's implied here that if the very first variable i type is of type int, if I type a comma, the next variable will be that same type, in this case, an integer. And I can assign it some value, like the length of phrase. This puts everything in one for loop. What I probably wouldn't want to do is this. I might not want to say i less than strlen of phrase. But why might I not want to do that? Let me show you the full line here. Why would it be better to define length here in this initialization step than here, which is my condition that's checked every loop? So I'm seeing a few good answers, which is that if I know I'm going to be checking this condition every single loop, well, why do I have to run strlen every single time? The length of the string isn't really going to change. And in fact, we'll just add more time to my program as it runs, probably not a whole ton of time if computers are so fast these days. But it still adds some time. So best to put it elsewhere to calculate it once and then use that variable throughout your code. So I'll say int length is the result of calling strlen with phrase. And I'll do it this way, keeping things separate just for line length sake at this point. OK, so now I'm able to access every individual character in my phrase. And to kind of make this a reality, I could say printf %c for an individual character. And now, I'll print out, let's say, phrase[i]. And now I'll open up my terminal. And I'll see if my code actually compiles. I'll say make alphabetical. Seems to compile. I'll run ./alphabetical, type in my phrase, which is hello, hit Enter. And I see it printed back to me. I probably need a backslash n at the end here to make sure that I'm actually returning my prompt down below the result of my program. But I can fix that here. I'll go back in. And I'll scroll down. And at the very end, I'll include a backslash n, like this. Now, though, I think we should take kind of a broader look at this. If I type make alphabetical and I say ./alphabetical hello, I know I'm able to access the H, the E, the L, the L, the O. But now there's a question of, how do I know if something is in alphabetical order? I can't really say-- there's no function I believe in C, at least that I know, that tells me does A come before B or does B come before A. What could I pay attention to instead? If we look back at this mapping here, what pattern do you see? So one thing I notice here is that as we go forward in the alphabet from A to B to C, we notice that the integer representation is actually increasing as we go. So first 65, then 66, then 67. So maybe we could use that pattern in our own code here. I could go back to it. And let me change the format code from %c to %i. That means I want to print whatever data is stored at phrase[i], whatever index of phrase. But I want to print it not as a character, but as an integer. I want to see what underlying number is being represented. So I'll try this now. I'll recompile alphabetical. Then I'll say ./alphabetical. And I'll give it hello. And now I see a lot of numbers. So I mean, that makes sense. I told it to print out now the numeric representation of the characters it's storing. But let me try this again. I'll make it a little clearer. I'll go back to my program. And I'll add a space between every character to separate these numbers apart. And now I'll recompile. I will say make alphabetical, make alphabetical, ./alphabetical. I'll say hello, in this case. And I see 104, 101, 108, 108, and 111. Now these don't seem to match. If I go back here I see A for 65, B for 66, C for 67. Why might they not match do you think? Yeah, so notice how in here I've been actually typing things in lowercase. And lowercase letters have different numeric representations. Let me try this with capital. I'll say ./alphabetical HELLO in all caps, hit Enter. And now I see those familiar numbers, 72, 69, 76, 76, and 79. So we can assume at least in this that all of our words to check for alphabetical order will be all in capitals. So let's keep going now. So I'm able to access each phrase or each character in this phrase. But now, what questions should I be asking? What should I ask maybe if something is not in alphabetical order? Or should I ask if something is in alphabetical order? And how would I convert that here to an actual condition? Any ideas? Yeah, so I'm seeing we could maybe compare letters as we loop through our phrase here. So maybe we could do something a bit like this. I could say if there's some condition here. And maybe this condition is we'll check if characters are not alphabetical, like this because we know that if the characters are not alphabetical, if any two characters are not in alphabetical order, well, then the entire thing is in alphabetical order. So what should I check? What should my condition be to determine if two characters are not in alphabetical order? Well, I could probably look at the first one. I could say phrase[i]. That gives me this very first letter. But now, what condition would be true if the following character is not in alphabetical order? Maybe I could say if this current letter is greater than, let's say, phrase i plus 1, that is the next letter. So here's what we have. If this current letter has a numeric representation that is greater than the previous one, well, that means it's not in alphabetical order. And to this more concrete, let's go back to our slides. Let's say we had B followed by A. Well, we'd first look at B. We'd say the B integer is 66. Then we look at the next one, A. That's 65. So we're seeing now that B has a greater value than A. That means they're not in alphabetical order. We can do the same thing for C and B, for C and A, and so on. So I think we're on to something here. Now, what should we do in this case if these are not in alphabetical order? Well, we could probably print out something like not in alphabetical order. And now logically, what could we do? We know that our program is done. We don't need to check any more letters. If something is not in alphabetical order, if any two characters are not in alphabetical order, we can return and call it good. So I'll return 0 here. And if you're not familiar, as we saw in lecture, return 0 basically means end my program here. Don't do anything else. As soon as you see this line, just quit and end my program. Now, though, let's try this. So I will say go back to my terminal. I'll compile. I'll say make alphabetical. And I'll type in ./alphabetical. And now I'll type in something like CBA, which we know is not in alphabetical order. I'll hit Enter. And we see not in alphabetical order. So what if I did this? I could say ./alphabetical. Now I'll try ABC. Hmm. And I get not in alphabetical order. What might be wrong here? Go back to my program. What do we see? Any ideas? Here's the full screen code again. So I did remove the correct line down here. So I haven't actually said when these things are in alphabetical order. So maybe that's something to consider here. There's a slightly more subtle bug though. And that is let's consider what happens if we go back to our alphabetical order array here. So let's say we checked A and B. Those seem to be in alphabetical order, right? We did that when i was equal to 0. Now, when i was equal to 1, we checked B and C. That seems fair. OK, so those are in alphabetical order as well. Now, when i was 2, we checked C. And what? I mean, what comes after C? I don't think there's really anything out there past C. So I think we made a mistake here. We don't want to be checking against values that are outside of our array. And in fact, that's kind of a common bug, but also a very dangerous one. We don't want to be looking at places in memory that we actually shouldn't be looking because one, we'll get bugs and two, we might touch things we're not supposed to touch in our computer. So let me fix this. I'll go back to our code. And how should we adjust this? Maybe we go from i equals 0 up to length but minus 1. So we get the very end of our phrase. Let's go back to our example here. We check A and B. We check B and C. And if those are in alphabetical order, well, we know the rest is in alphabetical order. We don't need to check C and whatever else comes after it, in this case, some empty value. So let's go and try this again. I will now go back to my terminal. And I'll say make alphabetical ./alphabetical. And I'll run hello. And I see well, that's not in alphabetical order. Now I'll do ./alphabetical again. And I'll type ABC, hit Enter. I don't see anything. Now there's two options here. I could say if this. And I know they're not alphabetical. I could try else maybe print alphabetical order, like this, and then return zero. But I would argue that might not be wise. And why do you think that wouldn't be wise? Yeah, so I'm seeing that we'll probably only check the very first two characters. So notice here, we begin with i equals 0. So i equals 0. We check. Are the characters in alphabetical order or are they not? If they're not, we'll break out our program. That seems fine. If they are though, we'll say everything's in alphabetical order and return 0. But we didn't yet check the rest of our phrase, which we really should be doing. And then further return zero again means exit the program at this particular moment. So we're going to exit and never look at the rest of our code. So this should be really elsewhere. And in fact, it should be probably at the end of our loop. We can only say for sure that this is an alphabetical order after we've gone through every pair of letters and checked that they are, let's say, not not in alphabetical order or that they are, in fact, in alphabetical order. So I'll say printf these are in alphabetical order, like this, backslash n semicolon and return 0 down below. So this then is our entire program. And I'll run it now to test it out. I'll say make alphabetical ./alphabetical. And now I'll type in ABC. I see that's in alphabetical order. I'll do it again with CBA. And those are not in alphabetical order. So questions then on this implementation of our program, or on strings or arrays more generally? OK, so seeing none right now. But feel free to keep chiming in if you'd like. Let's continue on then and focus on this new idea of command line arguments. So our final topic for today is this idea of running programs and giving them input not necessarily while they run, but even before they run. And now you've probably seen similar kinds of programs. In fact, every time I went to my terminal and I typed make alphabetical, until I hit Enter, Make has not yet run. But notice how I'm not just typing Make, the name of the program, I'm giving Make some input or some argument, telling it what to make. I'm telling it here to make the program alphabetical. Now, you've also probably seen something like check50. I can run check50. And this is the program itself, check50. I can hit Enter. And I'll see I get a bit of a help message here. But I also see the following arguments or inputs are required when I run check50. The slug in this case is required. And the slug refers to the problem I'm going to check, something like cs50/problems/ and so on. But notice here how before I even run check50 I'm giving it some input, some additional context to go off of to run as a program. And we can do the very same thing for our own programs. So for instance, in mario, when you first wrote it, you might have done something a bit like this. You might have run ./mario and then while the program was running prompted the user for a height, in this case H. And maybe you typed in eight. Well, in your actual C code you probably had something like this. You had your int main void, your main function in your program. And you had some variable perhaps named height that received the value of getint after we finished running. Now, we're going to transition here and make sure that we allow the user to actually give input before the program is even running. So you can imagine Mario being run like this, ./mario space 8. So before Mario even runs, the user can tell us how high they want that pyramid to be. And if you do something like this and you want to capture this input, well, you need to change your C code. And it turns out you have to change it a little bit like this. What do you notice that's different now? We still have int and main. But what looks different now? Yes, I'm seeing that void is replaced. So before we had void inside parentheses. But now we have what seems to be two different things, int argc and string argv with some braces. So let's go through first conceptually what's happening here. So in our prior version of Mario, notice that when the user ran it, they only ran ./mario. They didn't give any other input. And that's actually reflected in our main function here. You can think of the main function as being the function that represents our entire program. The int tells us the exit status code. If it's 0, that means all was OK. If it's non-zero, something bad happened. But either way, our program will return an integer. Now, main is the name of this function kind of by convention. And here inside parentheses we see void. Our program or this function takes no arguments. And we saw this above. The user just typed ./mario. But they didn't add any arguments. But now if we change this, if the user actually types in an 8, we have to change our C code to take some input now. So our entire program now takes what seems to be a total of two arguments or two inputs. One is called argc, which is of the type integer. The other is called argv, which is itself a string, but actually not just a string, an array of strings. So notice here we see that array syntax coming back? Argv with the braces here, that means argv is an array of strings. And in fact, we'll see it holds the arguments we actually give to our program. So here let's take a look. We're going to write a program here that prints each command line argument given to our program just to kind of practice and get a feel for what argc and argv can do. So I'll go back to my terminal. And I'll type code argv. Actually, I'll just type-- yeah, code argv.c. And now inside of this we're going to get a sense for what these command line arguments are doing for us. So I'll include, let's say, cs50.h. I'll include stdio.h. And now I'll type int main and not int main void. I now want my program to take some input at the command line. I could say int argc and string argv to say my program now has access to something called argc, which is a number, and something called argv, which is, in this case, an array of strings. So now in particular argc is the number of arguments that my program received, the number of inputs it received, including the actual name of the program itself. So for instance, if I go back to that mario example, I type ./mario 8. In that case, argc would be equal to 2. I'm giving two inputs, the name of my program and the number eight. Now, argv would itself have two strings inside. One would be the name of my program. And the other would be the input that I gave, in this case, eight. So let's try this. I'll go back to my code here. And I will try to loop through all the values in argv. I'll say for int i equals 0. i is less than-- how do I know how long argv is? I can rely on argc. I'll say argc then i++. And now I'll print out something like this, argv %i is %s backslash n. And I'll fill this in with a few variables here. I'm going to refer to argv bracket i. So I'll substitute i in there. And then I'll also substitute argv bracket i here. So now I can see when I run this program, I should be able to print out argv bracket 0 is whatever argv bracket 0 is. Argv bracket 1 is whatever argv bracket 1 is. So now I'll go back. And I'll try to compile this program. I'll say, in this case, make argv. I'll type ./argv. And give it some input, let's say, 1, 2, and 3 separated by spaces. Now I hit Enter. And I see my program had a total of four inputs. The first was the actual name of my program. So argv bracket 0 is equal to ./argv. argv bracket 1 is 1, as we saw up here. 2 is 2. And 3 is 3. So let me try this. I can say ./argv. I could even type in something like my name, Carter. And now I see argv bracket 1, name of my program. argv bracket 1 is Carter. Argv bracket 0 is the name of my program here. So to be clear, argc then is the total number of arguments we get. We can use it to figure out how long argv will be. But all the interesting stuff, all the actual input to our program will be stored in argv as a set of strings. So questions then on argc and argv? What questions do we have? OK, so while we're here, I actually see a good question, which is asking, we noticed that argv is storing a collection of strings. But what if we wanted to get a number and use it in our program, like in that Mario example, for instance? So let's consider trying to re-implement Mario, but now using command line arguments. So what if I did this? I can go back to my terminal. And I'll type code mario.c. I'm not going to write the whole thing. But I will try to make it so that I'm able to run Mario using command line arguments. So instead of, in this case, running int main void, I'll start off with int main int argc string argv. And again, this is allowing my program to take inputs at the command line. And it will store them for me in this array called argv. And it will tell me how many there are using argc. So let's try this. I know I want to get the-- I know I want to allow the user to do this, to say ./mario followed by 8, for instance. And now I'm curious. To get this value of 8, in which index of argv should I look, based on what we saw earlier? Seems like argv bracket 1. So keep in mind that ./mario, that will be the value for argv bracket 0. This value though will be the value for argv bracket 1. So now I'll try that. I'll say, well, why don't I make a variable called height and say that it gets whatever is stored in argv bracket 1? Try it. Now I'll compile my program. I'll go up top. I'll say make Mario. I get an error. And this isn't a particularly helpful error. But I do see this. Initializing int with an expression of type string. So it seems like I'm not able to store a string inside of this variable I said was an integer. So what can I do? I have to first convert this value to an integer. And it turns out there is a function for that, one included in the not string.h, included in the standard library. stdlib.h gives me access to those functions. And this function is called a to i. a to i effectively converts any string to an integer, assuming it's able to. If you give it one, like the string one, it will convert that to the integer 1 overall. So I'll try this. I'll say, make Mario. And now I don't get any errors. So it seems like using a to i I'm able to convert this argument, which was previously a string into an integer and now assign it to this value of height. But now, what if I do this? What if I say make Mario ./mario and I don't give any input, I just hit Enter? Why would I have gotten this error? Segmentation faults often occur when I look beyond the bounds of my array. Why would typing just ./mario make me look beyond the bounds of my array, in this case argv? If I only type ./mario, I think I really only have a value for argv. Let's see. argc will be one, which means that argv will only have one element. And I can't look beyond the bounds of argv. So if I had only one element, I could use argv bracket 0. But argv bracket 1 assumes I have two elements. So here's another use case for argc. I could first check. Before I do anything, let me first check if argc does not equal 2. If there are any fewer or any more than two arguments to my program, I want to do something. I want to tell the user that the usage of this program is ./mario followed by some number, like this, backslash n. And then I'll return 1, meaning something went wrong, not 0. You actually use this program incorrectly. So that actually assures me that if I recompile my program now, I do make Mario ./mario, I get this error instead of a segmentation fault. I'm able to catch this error before my program actually runs as a whole. So here, again, is our program. What questions do we have on argc and argv? A question here for, again, the summary of what argc and argv are. In a single sentence argc is the number of inputs to our program at the command line. And in a single sentence argv is the array of strings, the array of inputs to our program at the command line. Other questions too. Another question. Yeah, good question here. So the question is, what counts as being at the command line? And in general, when we say command line, we're also referring to this terminal here. So when I type ./mario and include any options outside of this, like 8, or my own name, or so on, that's at the command line. And in lecture, we saw this other program called cowsay that lets me actually specify what kind of animal. I want to say some kind of text. I could say, give me a dragon that says roar, like this. Let me zoom out so you can see it. Here is that dragon. So notice here in one single command I ran cowsay, but gave it some input. Dash f dragon means configure the animal I show to be a dragon. And then roar is the other input that says what should the dragon be saying here down below? All right, other questions too? All right, so seeing no additional questions here. I think we'll go ahead and call this section a wrap. Thank you all so much for coming and joining us here. We'll see you all next week.