1 00:00:00,000 --> 00:00:02,500 [Section 5 - More Comfortable] 2 00:00:02,500 --> 00:00:04,690 [Rob Bowden - Harvard University] 3 00:00:04,690 --> 00:00:07,250 [This is CS50. - CS50.TV] 4 00:00:08,990 --> 00:00:14,250 >> Like I said in my email, there are a lot of things you can use 5 00:00:14,250 --> 00:00:17,060 other than the appliance to actually do the problem sets. 6 00:00:17,060 --> 00:00:19,910 We recommend you do it in the appliance just because then we can more easily help you 7 00:00:19,910 --> 00:00:22,070 and we know how everything is going to work. 8 00:00:22,070 --> 00:00:26,950 But as one example of where you can do things if, say, you don't have access 9 00:00:26,950 --> 00:00:31,570 to an appliance or you want to work in the Science Center basement-- 10 00:00:31,570 --> 00:00:33,090 which actually they have the appliance too-- 11 00:00:33,090 --> 00:00:35,150 if you want to work anywhere. 12 00:00:35,150 --> 00:00:42,370 One example is have you seen/heard of SSH? 13 00:00:44,380 --> 00:00:47,780 SSH is basically just like connect to something. 14 00:00:47,780 --> 00:00:51,340 Actually, right now I'm SSHed into the appliance. 15 00:00:51,340 --> 00:00:54,290 I never work directly in the appliance. 16 00:00:55,930 --> 00:01:01,060 Here is the appliance, and if you look down here you see this IP address. 17 00:01:01,060 --> 00:01:03,650 I never work in the appliance itself; 18 00:01:03,650 --> 00:01:08,840 I always come over to an iTerm2 window/terminal window. 19 00:01:08,840 --> 00:01:15,910 You can SSH to that IP address, ssh jharvard@192.168.129.128. 20 00:01:15,910 --> 00:01:20,390 I remember that number very easily because it's such a nice pattern. 21 00:01:20,390 --> 00:01:24,920 But that will ask me for my password, and now I'm in the appliance. 22 00:01:24,920 --> 00:01:33,060 Basically, at this point, if you opened up a terminal inside of the appliance itself, 23 00:01:33,060 --> 00:01:36,350 this interface, however you would use it, is exactly the same 24 00:01:36,350 --> 00:01:40,010 as the interface I'm using over here but now you're SSHed. 25 00:01:42,240 --> 00:01:44,920 You don't have to SSH to the appliance. 26 00:01:44,920 --> 00:01:52,360 One example of another place you could SSH to is I'm pretty sure you have by default-- 27 00:01:52,360 --> 00:01:55,020 Oh. Bigger. 28 00:01:55,020 --> 00:02:01,130 All of you should have by default FAS accounts on the FAS servers. 29 00:02:01,130 --> 00:02:06,840 For me, I would SSH to rbowden@nice.fas.harvard.edu. 30 00:02:06,840 --> 00:02:11,610 It's going to ask you that the first time, and you say yes. 31 00:02:11,610 --> 00:02:15,840 My password is just going to be my FAS password. 32 00:02:15,840 --> 00:02:22,650 And so now, I'm SSHed to the nice servers, and I can do anything I want on here. 33 00:02:22,650 --> 00:02:28,560 A lot of classes you might take, like 124, are going to have you upload stuff to here 34 00:02:28,560 --> 00:02:30,950 to actually submit your problem sets. 35 00:02:30,950 --> 00:02:34,100 But say you don't have access to your appliance. 36 00:02:34,100 --> 00:02:37,910 Then you can do things, like on here it will say-- 37 00:02:37,910 --> 00:02:42,160 This is just our section of questions. 38 00:02:42,160 --> 00:02:45,070 It will ask you to do this in the appliance. 39 00:02:45,070 --> 00:02:47,790 Instead I'll just do it on the server. 40 00:02:47,790 --> 00:02:50,560 I'm going to unzip that. 41 00:02:50,560 --> 00:02:55,670 The problem is going to be that you're used to using something like gedit 42 00:02:55,670 --> 00:02:58,160 or whatever inside of the appliance. 43 00:02:58,160 --> 00:03:01,830 You're not going to have that on the FAS server. 44 00:03:01,830 --> 00:03:04,110 It's all just going to be this textual interface. 45 00:03:04,110 --> 00:03:09,180 So you could either one, try to learn a text editor that they do have. 46 00:03:09,180 --> 00:03:12,130 They have Nano. 47 00:03:12,130 --> 00:03:14,990 Nano is usually pretty easy to use. 48 00:03:14,990 --> 00:03:19,470 You can use your arrows and type normally. 49 00:03:19,470 --> 00:03:21,250 So that's not hard. 50 00:03:21,250 --> 00:03:24,720 If you want to get really fancy you can use Emacs, 51 00:03:24,720 --> 00:03:29,850 which I probably shouldn't have opened because I don't even know how to close Emacs. 52 00:03:29,850 --> 00:03:32,760 Control X, Control C? Yeah. 53 00:03:32,760 --> 00:03:35,310 Or you can use Vim, which is what I use. 54 00:03:35,310 --> 00:03:37,800 And so those are your options. 55 00:03:37,800 --> 00:03:43,830 If you don't want to do that, you can also, if you look at manual.cs50.net-- 56 00:03:43,830 --> 00:03:45,410 Oh. 57 00:03:45,410 --> 00:03:49,920 On a PC, you can SSH using PuTTY, 58 00:03:49,920 --> 00:03:51,940 which you're going to have to download separately. 59 00:03:51,940 --> 00:03:55,460 On a Mac, you can just by default use Terminal or you can download iTerm2, 60 00:03:55,460 --> 00:03:58,490 which is like a nice, fancy Terminal. 61 00:03:58,490 --> 00:04:03,780 If you go to manual.cs50.net you'll see a link to Notepad++, 62 00:04:03,780 --> 00:04:07,120 which is what you can use on a PC. 63 00:04:07,120 --> 00:04:13,340 It lets you SFTP from Notepad++, which is basically SSH. 64 00:04:13,340 --> 00:04:17,750 What this will let you do is edit your files locally, 65 00:04:17,750 --> 00:04:20,670 and then whenever you want to save them, it will save to nice.fas, 66 00:04:20,670 --> 00:04:23,670 where you can then run them. 67 00:04:23,670 --> 00:04:26,880 And the equivalent on a Mac is going to be TextWrangler. 68 00:04:26,880 --> 00:04:28,760 So it lets you do the same thing. 69 00:04:28,760 --> 00:04:32,800 It lets you edit files locally and save them to nice.fas, 70 00:04:32,800 --> 00:04:35,730 where you can then run them. 71 00:04:35,730 --> 00:04:40,400 So if you're ever stuck without an appliance, you have these options 72 00:04:40,400 --> 00:04:44,230 to still do your problem sets. 73 00:04:44,230 --> 00:04:48,250 The one problem is going to be that you're not going to have the CS50 library 74 00:04:48,250 --> 00:04:51,580 because nice.fas does not by default have that. 75 00:04:51,580 --> 00:04:55,970 You can either download the CS50 library-- 76 00:04:55,970 --> 00:04:58,470 I don't think I need that at this point. 77 00:04:58,470 --> 00:05:03,270 You can either download the CS50 library and copy it over to nice.fas, 78 00:05:03,270 --> 00:05:07,450 or I think at this point we don't use it anymore anyway. 79 00:05:07,450 --> 00:05:12,720 Or if we do, you can for the time being replace it with 80 00:05:12,720 --> 00:05:18,480 the implementations of the functions in the CS50 library anyway. 81 00:05:18,480 --> 00:05:21,370 So that shouldn't be that much of a restriction. 82 00:05:21,370 --> 00:05:23,710 And that's that. 83 00:05:26,460 --> 00:05:29,820 >> I'll go back to the appliance now; we'll do everything in the appliance. 84 00:05:29,820 --> 00:05:37,510 Looking at our section of questions, at the beginning, like I said in my email, 85 00:05:37,510 --> 00:05:43,620 we have to talk about the one short you were supposed to watch. 86 00:05:43,620 --> 00:05:51,980 We have the Redirecting & Pipes and these three questions. 87 00:05:51,980 --> 00:05:56,070 >> To which stream do functions like printf write by default? 88 00:05:56,070 --> 00:05:59,130 So stream. What is a stream? 89 00:06:06,520 --> 00:06:15,100 A stream is basically like it's just some-- 90 00:06:15,100 --> 00:06:21,450 It's not even a source of 1s and 0s. 91 00:06:21,450 --> 00:06:24,920 The stream it's asking for here is standard out. 92 00:06:24,920 --> 00:06:27,250 And so standard out is a stream that when you write to it, 93 00:06:27,250 --> 00:06:30,940 it appears on the screen. 94 00:06:30,940 --> 00:06:36,860 Standard out, by stream, it means you just write 1s and 0s to it, 95 00:06:36,860 --> 00:06:40,220 and the other end of standard out just reads from that stream. 96 00:06:40,220 --> 00:06:43,540 It's just a string of 1s and 0s. 97 00:06:43,540 --> 00:06:45,570 You can write to streams or you can read from streams 98 00:06:45,570 --> 00:06:47,950 depending on what the stream actually is. 99 00:06:47,950 --> 00:06:52,800 The other two default streams are standard in and standard error. 100 00:06:52,800 --> 00:06:57,540 Standard in is whenever you do GetString, it's waiting for you to input stuff. 101 00:06:57,540 --> 00:07:01,570 So it waiting for you, it's actually waiting on standard in, 102 00:07:01,570 --> 00:07:04,880 which is really what you get when you type at the keyboard. 103 00:07:04,880 --> 00:07:07,530 You're typing into standard in. 104 00:07:07,530 --> 00:07:10,050 Standard error is basically equivalent to standard out, 105 00:07:10,050 --> 00:07:13,280 but it's specialized in that when you print to standard error, 106 00:07:13,280 --> 00:07:16,770 you're supposed to only print error messages to that 107 00:07:16,770 --> 00:07:20,200 so you can differentiate between regular messages printed to the screen 108 00:07:20,200 --> 00:07:24,560 versus error messages depending on whether they went to standard out or standard error. 109 00:07:24,560 --> 00:07:28,660 Files too. 110 00:07:28,660 --> 00:07:32,440 Standard out, standard in, and standard error are just special streams, 111 00:07:32,440 --> 00:07:36,810 but really any file, when you open a file, it becomes a stream of bytes 112 00:07:36,810 --> 00:07:40,740 where you can just read from that stream. 113 00:07:40,740 --> 00:07:47,770 You, for the most part, can just think of a file as a stream of bytes. 114 00:07:47,770 --> 00:07:51,190 So what streams do they write to by default? Standard out. 115 00:07:51,190 --> 00:07:56,980 >> What's the difference between > and >>? 116 00:07:58,140 --> 00:08:03,710 Did anyone watch the video beforehand? Okay. 117 00:08:03,710 --> 00:08:10,960 > is going to be how you redirect into files, 118 00:08:10,960 --> 00:08:15,240 and >> is also going to redirect output into files, 119 00:08:15,240 --> 00:08:17,820 but it's instead going to append to the file. 120 00:08:17,820 --> 00:08:23,430 For example, let's say I happen to have dict right here, 121 00:08:23,430 --> 00:08:27,020 and the only stuff inside of dict is cat, cat, dog, fish, dog. 122 00:08:27,020 --> 00:08:31,530 One command that you have at the command line is cat, 123 00:08:31,530 --> 00:08:34,539 which is just going to print what's in a file. 124 00:08:34,539 --> 00:08:40,679 So when I say cat dict, it's going to print cat, cat, dog, fish, dog. That's all cat does. 125 00:08:40,679 --> 00:08:46,280 That means that it printed to standard out cat, cat, dog, fish, dog. 126 00:08:46,280 --> 00:08:53,240 If I instead want to redirect that to a file, I can use > and redirect it to whatever the file is. 127 00:08:53,240 --> 00:08:56,460 I'll call the file file. 128 00:08:56,460 --> 00:09:00,320 So now if I ls, I'll see I have a new file called file. 129 00:09:00,320 --> 00:09:05,700 And if I open it up, it's going to have exactly what cat put at the command line. 130 00:09:05,700 --> 00:09:11,040 So now if I do that again, then it's going to redirect the output into file, 131 00:09:11,040 --> 00:09:13,930 and I'm going to have the same exact thing. 132 00:09:13,930 --> 00:09:17,910 So technically, it completely overrode what we had. 133 00:09:17,910 --> 00:09:22,970 And we'll see if I change dict, I took out dog. 134 00:09:22,970 --> 00:09:29,980 Now if we cat dict into file again, we're going to have the new version with dog removed. 135 00:09:29,980 --> 00:09:32,400 So it completely overrides it. 136 00:09:32,400 --> 00:09:36,640 Instead, if we use >>, it's going to append file. 137 00:09:36,640 --> 00:09:40,860 Now, opening file, we see we have just the same thing printed twice 138 00:09:40,860 --> 00:09:44,920 because it was there once, then we appended to the original. 139 00:09:44,920 --> 00:09:48,130 So that's what > and >> do. 140 00:09:48,130 --> 00:09:50,580 Does the next one ask-- It does not ask about it. 141 00:09:50,580 --> 00:09:59,050 >> The other one that we have is <, which if > redirects standard out, 142 00:09:59,050 --> 00:10:01,970 < is going to be redirect standard in. 143 00:10:01,970 --> 00:10:12,050 Let's see if we have an example. 144 00:10:14,750 --> 00:10:16,930 I can write one real quick. 145 00:10:17,870 --> 00:10:25,700 Let's take any file, hello.c. 146 00:10:56,060 --> 00:10:59,070 Relatively straightforward file. 147 00:10:59,070 --> 00:11:03,570 I'm just getting a string and then printing "Hello" whatever the string I just entered was. 148 00:11:03,570 --> 00:11:07,990 So make hello and then ./hello. 149 00:11:07,990 --> 00:11:10,720 Now it's prompting me to enter something, 150 00:11:10,720 --> 00:11:15,070 which means it's waiting on things to be entered into standard in. 151 00:11:15,070 --> 00:11:20,450 So enter whatever I want into standard in. We're just going to say Hello, Rob! 152 00:11:20,450 --> 00:11:23,310 Then it's printing to standard out Hello, Rob! 153 00:11:23,310 --> 00:11:28,860 If I do ./hello and then redirect, 154 00:11:30,740 --> 00:11:34,310 for now you can only redirect from a file. 155 00:11:34,310 --> 00:11:41,720 So if I put in some file, txt, and I put Rob, 156 00:11:41,720 --> 00:11:52,300 if I run hello and then redirect the file txt into ./hello, it's going to say Hello, Rob! immediately. 157 00:11:52,300 --> 00:11:57,160 When it first gets to GetString and it's waiting on standard in, 158 00:11:57,160 --> 00:12:01,730 standard in is no longer waiting on the keyboard for data to get entered. 159 00:12:01,730 --> 00:12:05,980 Instead, we have redirected standard in to read from the file txt. 160 00:12:05,980 --> 00:12:10,290 And so it's going to read from the file txt, which is just the line Rob, 161 00:12:10,290 --> 00:12:13,380 and then it's going to print Hello, Rob! 162 00:12:13,380 --> 00:12:18,180 And if I wanted, I could also do ./hello < txt 163 00:12:18,180 --> 00:12:21,500 and then the standard out that it's printing, which is Hello, Rob!, 164 00:12:21,500 --> 00:12:24,700 I can redirect that into its own file. 165 00:12:24,700 --> 00:12:29,790 I'll just call the file hello--no, I won't, because that's the executable--txt2. 166 00:12:29,790 --> 00:12:40,150 Now, txt2 is going to have the output of ./hello < txt, which is going to be Hello, Rob! 167 00:12:41,370 --> 00:12:43,520 >> Questions? 168 00:12:45,900 --> 00:12:49,090 >> Okay. So then here we have pipeline. 169 00:12:49,090 --> 00:12:53,510 Pipes are the last unit of redirection. 170 00:12:53,510 --> 00:12:58,750 >> Oh. I guess one more unit of redirection is if instead of > you do 2>, 171 00:12:58,750 --> 00:13:01,070 that's redirecting standard error. 172 00:13:01,070 --> 00:13:06,280 So if something went to standard error, it would not get put into txt2. 173 00:13:06,280 --> 00:13:12,480 But notice if I do 2>, then it's still printing Hello, Rob! to the command line 174 00:13:12,480 --> 00:13:18,600 because I'm only redirecting standard error, I'm not redirecting standard out. 175 00:13:18,600 --> 00:13:22,210 Standard error and standard out are different. 176 00:13:24,210 --> 00:13:27,080 If you wanted to actually write to standard error, 177 00:13:27,080 --> 00:13:35,080 then I could change this to be fprintf to stderr. 178 00:13:35,080 --> 00:13:37,850 So printf, by default, prints to standard out. 179 00:13:37,850 --> 00:13:41,720 If I want to print to standard error manually, then I have to use fprintf 180 00:13:41,720 --> 00:13:45,010 and specify what I want to print to. 181 00:13:45,010 --> 00:13:49,720 If instead I did fprintf stdout, then that's basically equivalent to printf. 182 00:13:49,720 --> 00:13:55,530 But fprintf to standard error. 183 00:13:57,790 --> 00:14:03,650 So now, if I redirect this into txt2, Hello, Rob! is still getting printed at the command line 184 00:14:03,650 --> 00:14:08,270 since it's getting printed to standard error and I'm only redirecting standard out. 185 00:14:08,270 --> 00:14:16,420 If I now redirect standard error, now it didn't get printed, and txt2 is going to be Hello, Rob! 186 00:14:16,420 --> 00:14:21,910 So now, you can print your actual errors to standard error 187 00:14:21,910 --> 00:14:24,720 and print your regular messages to standard out. 188 00:14:24,720 --> 00:14:31,420 And so when you run your program, you can run it as ./hello this type with the 2> 189 00:14:31,420 --> 00:14:33,800 so that your program is going to run normally, 190 00:14:33,800 --> 00:14:38,400 but any error messages that you get you can check later in your error log, 191 00:14:38,400 --> 00:14:44,500 so errors, and then look later and your errors file will have any errors that happened. 192 00:14:45,200 --> 00:14:47,540 >> Questions? 193 00:14:47,540 --> 00:14:58,070 >> The last one is pipe, which you can think of as taking the standard out from one command 194 00:14:58,070 --> 00:15:01,210 and making it the standard in of the next command. 195 00:15:01,210 --> 00:15:05,570 An example here is echo is a command line thing 196 00:15:05,570 --> 00:15:11,840 that is just going to echo whatever I put as its argument. I won't put quotes. 197 00:15:11,840 --> 00:15:16,150 Echo blah, blah, blah is just going to print blah, blah, blah. 198 00:15:16,150 --> 00:15:20,600 Before, when I said I had to put Rob into a txt file 199 00:15:20,600 --> 00:15:28,830 because I can only redirect txt files, instead,/ if I echo Rob 200 00:15:28,830 --> 00:15:35,520 and then pipe it into ./hello, that will also do the same type of thing. 201 00:15:35,520 --> 00:15:39,160 This is taking the output of this command, echo Rob, 202 00:15:39,160 --> 00:15:43,610 and using it as the input for ./hello. 203 00:15:44,790 --> 00:15:49,560 You can think of it as first redirect echo Rob into a file 204 00:15:49,560 --> 00:15:54,160 and then input into ./hello that file that was just outputted. 205 00:15:54,160 --> 00:15:57,850 But it takes the temporary file out of the picture. 206 00:16:01,890 --> 00:16:04,460 >> Questions on that? 207 00:16:04,460 --> 00:16:07,150 >> The next question is going to involve this. 208 00:16:07,150 --> 00:16:15,310 What pipeline could you use to find the number of unique names in a file called names.txt? 209 00:16:15,310 --> 00:16:24,160 The commands we're going to want to use here are unique, so uniq, and then wc. 210 00:16:24,160 --> 00:16:28,840 You can do man uniq to actually look at what that does, 211 00:16:28,840 --> 00:16:34,840 and it's just going to filter adjacent matching lines from the input. 212 00:16:34,840 --> 00:16:40,690 And man wc is going to print the newline, word, and byte counts for each file. 213 00:16:40,690 --> 00:16:43,760 And the last one we're going to want to use is sort, 214 00:16:43,760 --> 00:16:47,410 which is going to just sort lines of txt file. 215 00:16:47,410 --> 00:16:58,080 If I make some txt file, names.txt, and it's Rob, Tommy, Joseph, Tommy, Joseph, RJ, Rob, 216 00:16:58,080 --> 00:17:03,910 what I want to do here is find the number of unique names in this file. 217 00:17:03,910 --> 00:17:08,750 So what should the answer be? >>[student] 4. >>Yeah. 218 00:17:08,750 --> 00:17:13,780 It should be 4 since Rob, Tommy, Joseph, RJ are the only unique names in this file. 219 00:17:13,780 --> 00:17:20,180 The first step, if I just do word count on names.txt, 220 00:17:20,180 --> 00:17:24,290 this is actually telling me everything. 221 00:17:24,290 --> 00:17:32,560 This is actually printing--let's see, man wc--newlines, words, and byte count. 222 00:17:32,560 --> 00:17:38,270 If I only care about lines, then I can just do wc -l names.txt. 223 00:17:41,730 --> 00:17:44,300 So that's step 1. 224 00:17:44,300 --> 00:17:50,510 But I don't want to wc -l names.txt because names.txt just contains all the names, 225 00:17:50,510 --> 00:17:54,170 and I want to filter out any non-unique ones. 226 00:17:54,170 --> 00:18:01,200 So if I do uniq names.txt, that doesn't quite give me what I want 227 00:18:01,200 --> 00:18:03,760 because the duplicated names are still there. 228 00:18:03,760 --> 00:18:07,690 Why is that? Why is uniq not doing what I want? 229 00:18:07,690 --> 00:18:10,500 [student] The duplicates are not [inaudible] >>Yeah. 230 00:18:10,500 --> 00:18:16,370 Remember the man page for uniq says filter adjacent matching lines. 231 00:18:16,370 --> 00:18:19,680 They're not adjacent, so it won't filter them. 232 00:18:19,680 --> 00:18:31,100 If I sort them first, sort names.txt is going to put all the duplicate lines together. 233 00:18:31,100 --> 00:18:34,450 So now sort names.txt is that. 234 00:18:34,450 --> 00:18:40,550 I'm going to want to use that as the input to uniq, which is | uniq. 235 00:18:40,550 --> 00:18:43,390 That gives me Joseph, RJ, Rob, Tommy, 236 00:18:43,390 --> 00:18:49,260 and I want to use that as the input to wc -l, 237 00:18:49,260 --> 00:18:52,740 which is going to give me 4. 238 00:18:52,740 --> 00:18:56,930 Like it says here, what pipeline could you use? 239 00:18:56,930 --> 00:19:01,390 You can do a lot of things like using a series of commands 240 00:19:01,390 --> 00:19:05,130 where you use the output from one command as the input to the next command. 241 00:19:05,130 --> 00:19:08,780 You can do a lot of things, a lot of clever things. 242 00:19:08,780 --> 00:19:11,440 >> Questions? 243 00:19:12,910 --> 00:19:14,600 Okay. 244 00:19:14,600 --> 00:19:17,880 That's it for pipes and redirection. 245 00:19:18,370 --> 00:19:24,090 >> Now we go on to the actual stuff, the coding stuff. 246 00:19:24,090 --> 00:19:29,100 Inside of this PDF, you'll see this command, 247 00:19:29,100 --> 00:19:32,950 and you'll want to run this command in your appliance. 248 00:19:36,240 --> 00:19:42,250 wget is the command for just getting something from the Internet, basically, 249 00:19:42,250 --> 00:19:45,180 so wget and this URL. 250 00:19:45,180 --> 00:19:49,110 If you went to this URL in your browser, it would download that file. 251 00:19:49,110 --> 00:19:52,510 I just clicked on it, so it downloaded the file for me. 252 00:19:52,510 --> 00:19:55,650 But writing wget of that thing inside of the terminal 253 00:19:55,650 --> 00:19:58,620 is just going to download it into your terminal. 254 00:19:58,620 --> 00:20:02,750 I have section5.zip, and you'll want to unzip section5.zip, 255 00:20:02,750 --> 00:20:06,520 which is going to give you a folder called section5, 256 00:20:06,520 --> 00:20:11,550 which is going to have all of the files we're going to be using today inside of it. 257 00:20:33,380 --> 00:20:37,710 As these programs' file names suggest, they're a bit buggy, 258 00:20:37,710 --> 00:20:40,990 so your mission is to figure out why using gdb. 259 00:20:40,990 --> 00:20:44,560 Does everyone have them downloaded/know how to get them downloaded 260 00:20:44,560 --> 00:20:47,480 into their appliance? Okay. 261 00:20:47,480 --> 00:20:56,400 >> Running ./buggy1, it will say Segmentation fault (core dumped), 262 00:20:56,400 --> 00:21:00,500 which any time you get a segfault, it's a bad thing. 263 00:21:00,500 --> 00:21:03,810 Under what circumstances do you get a segfault? 264 00:21:03,810 --> 00:21:08,210 [student] Dereferencing a null pointer. >>Yeah. So that is one example. 265 00:21:08,210 --> 00:21:11,580 Dereferencing a null pointer you're going to get a segfault. 266 00:21:11,580 --> 00:21:16,720 What a segfault means is you're touching memory you shouldn't be touching. 267 00:21:16,720 --> 00:21:21,350 So dereferencing a null pointer is touching address 0, 268 00:21:21,350 --> 00:21:28,060 and basically, all computers nowadays say that address 0 is memory you shouldn't be touching. 269 00:21:28,060 --> 00:21:31,920 So that's why dereferencing a null pointer results in a segfault. 270 00:21:31,920 --> 00:21:37,210 When you happen to not initialize a pointer, then it has a garbage value, 271 00:21:37,210 --> 00:21:41,520 and so when you try to dereference it, in all likelihood you're touching memory 272 00:21:41,520 --> 00:21:43,540 that's in the middle of nowhere. 273 00:21:43,540 --> 00:21:45,650 If you happen to get lucky and the garbage value 274 00:21:45,650 --> 00:21:48,440 happened to point to somewhere on the stack or something, 275 00:21:48,440 --> 00:21:50,820 then when you dereference that pointer which you haven't initialized, 276 00:21:50,820 --> 00:21:52,730 nothing will go wrong. 277 00:21:52,730 --> 00:21:55,480 But if it's pointing to, say, somewhere between the stack and the heap, 278 00:21:55,480 --> 00:21:59,850 or it's pointing just to somewhere that hasn't been used by your program yet, 279 00:21:59,850 --> 00:22:02,240 then you're touching memory you shouldn't be touching and you segfault. 280 00:22:02,240 --> 00:22:06,370 When you write a recursive function and it recurses too many times 281 00:22:06,370 --> 00:22:08,720 and your stack grows too large and the stack collides into things 282 00:22:08,720 --> 00:22:12,270 that it shouldn't be colliding with, you're touching memory you shouldn't be touching, 283 00:22:12,270 --> 00:22:14,810 so you segfault. 284 00:22:14,810 --> 00:22:17,010 That is what a segfault is. 285 00:22:17,010 --> 00:22:21,810 >> It's also the same reason that if you have a string like-- 286 00:22:21,810 --> 00:22:23,930 let's go back to the previous program. 287 00:22:23,930 --> 00:22:28,530 In hello.c--I'm just going to make something else. 288 00:22:28,530 --> 00:22:33,770 char *s = "hello world!"; 289 00:22:33,770 --> 00:22:42,310 If I use *s = something or s[0] = 'X'; 290 00:22:42,310 --> 00:22:47,290 so make hello, ./hello, why did that segfault? 291 00:22:48,410 --> 00:22:51,250 Why did this segfault? 292 00:22:55,660 --> 00:22:57,890 What would you expect to happen? 293 00:22:57,890 --> 00:23:06,640 If I did printf("%s\n", s); what would you expect to be printed? 294 00:23:06,640 --> 00:23:09,930 [student] X hello. >>Yeah. 295 00:23:09,930 --> 00:23:15,140 The problem is that when you declare a string like this, 296 00:23:15,140 --> 00:23:18,190 s is a pointer that's going to go on the stack, 297 00:23:18,190 --> 00:23:25,880 and what s is pointing to is this string which is contained in read-only memory. 298 00:23:25,880 --> 00:23:30,560 So just by the name, read-only memory, you should get the idea 299 00:23:30,560 --> 00:23:33,010 that if you try to change what's in read-only memory, 300 00:23:33,010 --> 00:23:36,670 you're doing something you shouldn't be doing with memory and you segfault. 301 00:23:36,670 --> 00:23:45,360 This is actually a big difference between char *s and char s[]. 302 00:23:45,360 --> 00:23:48,790 So char s[], now this string is going to be put on the stack, 303 00:23:48,790 --> 00:23:53,960 and the stack is not read-only, which means that this should work perfectly fine. 304 00:23:55,500 --> 00:23:57,370 And it does. 305 00:23:57,370 --> 00:24:06,250 Remember that when I do char *s = "hello world!", s itself is on the stack 306 00:24:06,250 --> 00:24:10,390 but s points to somewhere else, and that somewhere else happens to be read-only. 307 00:24:10,390 --> 00:24:15,640 But char s[] is just something on the stack. 308 00:24:17,560 --> 00:24:21,760 So that's another example of a segfault happening. 309 00:24:21,760 --> 00:24:27,820 >> We saw that ./buggy1 resulted in a segfault. 310 00:24:27,820 --> 00:24:31,810 In theory, you shouldn't look at buggy1.c immediately. 311 00:24:31,810 --> 00:24:35,170 Instead, we'll look at it through gdb. 312 00:24:35,170 --> 00:24:37,750 Notice that when you get Segmentation fault (core dumped), 313 00:24:37,750 --> 00:24:40,850 you get this file over here called core. 314 00:24:40,850 --> 00:24:45,200 If we ls -l, we'll see that core is usually a pretty big file. 315 00:24:45,200 --> 00:24:51,580 This is the number of bytes of the file, so it looks like it's 250-something kilobytes. 316 00:24:51,580 --> 00:24:56,120 The reason for this is that what the core dump actually is 317 00:24:56,120 --> 00:25:01,410 is when your program crashes, the state of memory of your program 318 00:25:01,410 --> 00:25:05,230 just gets copied and pasted into this file. 319 00:25:05,230 --> 00:25:07,270 It gets dumped into that file. 320 00:25:07,270 --> 00:25:13,060 This program, while it was running, happened to have a memory usage of around 250 kilobytes, 321 00:25:13,060 --> 00:25:17,040 and so that's what got dumped into this file. 322 00:25:17,040 --> 00:25:23,630 Now you can look at that file if we do gdb buggy1 core. 323 00:25:23,630 --> 00:25:30,130 We can just do gdb buggy1, and that will just start up gdb regularly, 324 00:25:30,130 --> 00:25:33,800 using buggy1 as its input file. 325 00:25:33,800 --> 00:25:38,260 But if you do gdb buggy1 core, then it's specifically going to start up gdb 326 00:25:38,260 --> 00:25:40,330 by looking at that core file. 327 00:25:40,330 --> 00:25:45,560 And you saying buggy1 means gdb knows that that core file comes from the buggy1 program. 328 00:25:45,560 --> 00:25:49,580 So gdb buggy1 core is going to immediately bring us 329 00:25:49,580 --> 00:25:52,060 to where the program happened to terminate. 330 00:25:57,720 --> 00:26:02,340 We see here Program terminated with signal 11, Segmentation fault. 331 00:26:02,340 --> 00:26:10,110 We happen to see a line of assembly, which probably isn't very helpful. 332 00:26:10,110 --> 00:26:15,360 But if you type bt or backtrace, that's going to be the function 333 00:26:15,360 --> 00:26:19,430 that gives us the list of our current stack frames. 334 00:26:19,430 --> 00:26:23,150 So backtrace. It looks like we only have two stack frames. 335 00:26:23,150 --> 00:26:26,310 The first is our main stack frame, 336 00:26:26,310 --> 00:26:29,810 and the second is the stack frame for this function that we happen to be in, 337 00:26:29,810 --> 00:26:34,440 which looks like we only have the assembly code for. 338 00:26:34,440 --> 00:26:38,050 So let's go back into our main function, 339 00:26:38,050 --> 00:26:42,300 and to do that we can do frame 1, and I think we can also do down, 340 00:26:42,300 --> 00:26:45,160 but I almost never do down--or up. Yeah. 341 00:26:45,160 --> 00:26:50,710 Up and down. Up brings you up one stack frame, down brings you down a stack frame. 342 00:26:50,710 --> 00:26:53,240 I tend to never use that. 343 00:26:53,240 --> 00:26:59,120 I just specifically say frame 1, which is go to the frame labeled 1. 344 00:26:59,120 --> 00:27:01,750 Frame 1 is going to bring us into main stack frame, 345 00:27:01,750 --> 00:27:05,570 and it says right here the line of code we happen to be at. 346 00:27:05,570 --> 00:27:07,950 If we wanted a couple more lines of code, we can say list, 347 00:27:07,950 --> 00:27:11,280 and that's going to give us all the lines of code around it. 348 00:27:11,280 --> 00:27:13,360 The line we segfaulted at was 6: 349 00:27:13,360 --> 00:27:17,360 if (strcmp("CS50 rocks", argv[1]) == 0). 350 00:27:17,360 --> 00:27:24,130 If it isn't obvious yet, you can get it straight from here just by thinking why it segfaulted. 351 00:27:24,130 --> 00:27:28,800 But we can take it one step further and say, "Why would argv[1] segfault?" 352 00:27:28,800 --> 00:27:38,830 Let's print argv[1], and it looks like it's 0x0, which is the null pointer. 353 00:27:38,830 --> 00:27:44,750 We're strcmping CS50 rocks and null, and so that's going to segfault. 354 00:27:44,750 --> 00:27:48,280 And why is argv[1] null? 355 00:27:48,640 --> 00:27:51,280 [student] Because we didn't give it any command-line arguments. 356 00:27:51,280 --> 00:27:53,390 Yeah. We didn't give it any command-line arguments. 357 00:27:53,390 --> 00:27:58,460 So ./buggy1 is only going to have argv[0] be ./buggy1. 358 00:27:58,460 --> 00:28:02,100 It's not going to have an argv[1], so that's going to segfault. 359 00:28:02,100 --> 00:28:07,450 But if, instead, I do just CS50, it's going to say You get a D 360 00:28:07,450 --> 00:28:09,950 because that's what it's supposed to do. 361 00:28:09,950 --> 00:28:15,240 Looking at buggy1.c, it's supposed to print "You get a D"-- 362 00:28:15,240 --> 00:28:20,820 If argv[1] is not "CS50 rocks", "You get a D", else "You get an A!" 363 00:28:20,820 --> 00:28:25,660 So if we want an A, we need this to compare as true, 364 00:28:25,660 --> 00:28:28,710 which means that it compares to 0. 365 00:28:28,710 --> 00:28:31,100 So argv[1] needs to be "CS50 rocks". 366 00:28:31,100 --> 00:28:35,660 If you want to do that on the command line, you need to use \ to escape the space. 367 00:28:35,660 --> 00:28:41,690 So CS50\ rocks and You get an A! 368 00:28:41,690 --> 00:28:44,060 If you don't do the backslash, why does this not work? 369 00:28:44,060 --> 00:28:47,190 [student] It's two different arguments. >>Yeah. 370 00:28:47,190 --> 00:28:52,540 Argv[1] is going to be CS50, and argv[2] is going to be rocks. Okay. 371 00:28:52,540 --> 00:28:56,470 >> Now ./buggy2 is going to segfault again. 372 00:28:56,470 --> 00:29:01,880 Instead of opening it with its core file, we'll just open up buggy2 directly, 373 00:29:01,880 --> 00:29:05,000 so gdb buggy2. 374 00:29:05,000 --> 00:29:09,590 Now if we just run our program, then it's going to say Program received signal SIGSEGV, 375 00:29:09,590 --> 00:29:15,530 which is the segfault signal, and this is where it happened to happen. 376 00:29:15,530 --> 00:29:21,250 Looking at our backtrace, we see that we were in the function oh_no, 377 00:29:21,250 --> 00:29:23,900 which was called by the function dinky, which was called by the function binky, 378 00:29:23,900 --> 00:29:26,460 which was called by main. 379 00:29:26,460 --> 00:29:31,680 We can also see the arguments to these functions. 380 00:29:31,680 --> 00:29:34,680 The argument to dinky and binky was 1. 381 00:29:34,680 --> 00:29:44,390 If we list the function oh_no, we see that oh_no is just doing char** s = NULL; 382 00:29:44,390 --> 00:29:47,410 *s = "BOOM"; 383 00:29:47,410 --> 00:29:50,330 Why would that fail? 384 00:29:54,330 --> 00:29:58,380 [student] You can't dereference the null pointer? >>Yeah. 385 00:29:58,380 --> 00:30:06,090 This is just saying s is NULL, regardless if that happens to be a char**, 386 00:30:06,090 --> 00:30:12,070 which, depending on how you interpret it, it could be a pointer to a pointer to a string 387 00:30:12,070 --> 00:30:15,550 or an array of strings. 388 00:30:15,550 --> 00:30:21,430 It's s is NULL, so *s is dereferencing a null pointer, 389 00:30:21,430 --> 00:30:24,800 and so this is going to crash. 390 00:30:24,800 --> 00:30:27,540 This is one of the quickest ways you can possibly segfault. 391 00:30:27,540 --> 00:30:31,300 It's just declaring a null pointer and immediately segfaulting. 392 00:30:31,300 --> 00:30:34,570 That's what oh_no is doing. 393 00:30:34,570 --> 00:30:43,400 If we go up one frame, then we're going to get into the function that called oh_no. 394 00:30:43,400 --> 00:30:44,830 I need to do that down. 395 00:30:44,830 --> 00:30:48,610 If you don't enter a command and you just hit Enter again, 396 00:30:48,610 --> 00:30:52,350 it will just repeat the previous command that you ran. 397 00:30:52,350 --> 00:30:56,610 We are in frame 1. 398 00:30:56,610 --> 00:31:04,650 Listing this frame, we see here is our function. 399 00:31:04,650 --> 00:31:08,520 You can hit list again, or you can do list 20 and it will list more. 400 00:31:08,520 --> 00:31:13,640 The function dinky says if i is 1, then go to the oh_no function, 401 00:31:13,640 --> 00:31:15,960 else go to the slinky function. 402 00:31:15,960 --> 00:31:18,700 And we know i is 1 because we happen to see up here 403 00:31:18,700 --> 00:31:22,560 that dinky was called with the argument 1. 404 00:31:22,560 --> 00:31:27,560 Or you can just do print i and it will say i is 1. 405 00:31:27,560 --> 00:31:33,770 We are currently in dinky, and if we go up another frame, we know we'll end up in binky. 406 00:31:33,770 --> 00:31:36,600 Up. Now we're in binky. 407 00:31:36,600 --> 00:31:41,340 Listing this function--the list from before half cut me off-- 408 00:31:41,340 --> 00:31:52,670 it started off as if i is 0, then we're going to call it oh_no, else call dinky. 409 00:31:52,670 --> 00:31:57,000 We know i was 1, so it called dinky. 410 00:31:57,000 --> 00:32:05,030 And now we're back in main, and main is just going to be int i = rand() % 3; 411 00:32:05,030 --> 00:32:08,790 That is just going to give you a random number that is either 0, 1, or 2. 412 00:32:08,790 --> 00:32:12,780 It's going to call binky with that number, and it will return 0. 413 00:32:12,780 --> 00:32:16,700 Looking at this, 414 00:32:16,700 --> 00:32:19,880 just walking through the program manually without running it immediately, 415 00:32:19,880 --> 00:32:25,400 you would set a break point at main, which means that when we run the program 416 00:32:25,400 --> 00:32:31,020 your program runs up until it hits a break point. 417 00:32:31,020 --> 00:32:35,450 So running the program, it will run and then it will hit the main function and stop running. 418 00:32:35,450 --> 00:32:44,700 Now we're inside of main, and step or next is going to bring us to the next line of code. 419 00:32:44,700 --> 00:32:47,050 You can do step or next. 420 00:32:47,050 --> 00:32:51,800 Hitting next, now i has been set to rand() % 3, so we can print the value of i, 421 00:32:51,800 --> 00:32:55,280 and it will say i is 1. 422 00:32:55,280 --> 00:32:58,110 Now it does matter whether we use next or step. 423 00:32:58,110 --> 00:33:01,000 I guess it mattered in the previous one, but we would want to use next. 424 00:33:01,000 --> 00:33:06,000 If we use step, we step into the function, which means look at the actual thing 425 00:33:06,000 --> 00:33:07,940 that's happening inside of binky. 426 00:33:07,940 --> 00:33:10,510 If we use next, then it means go over the function 427 00:33:10,510 --> 00:33:14,070 and just go to the next line of code in our main function. 428 00:33:14,070 --> 00:33:17,900 Right here on this line, I was at where it said rand() % 3; 429 00:33:17,900 --> 00:33:21,320 if I did step, it would go into the implementation of rand 430 00:33:21,320 --> 00:33:25,110 and look at what's happening there, and I could step through the rand function. 431 00:33:25,110 --> 00:33:26,920 But I don't care about the rand function. 432 00:33:26,920 --> 00:33:30,190 I just want to go to the next line of code in main, so I use next. 433 00:33:30,190 --> 00:33:35,800 But now I do care about the binky function, so I want to step into that. 434 00:33:35,800 --> 00:33:37,730 Now I'm in binky. 435 00:33:37,730 --> 00:33:42,040 The first line of code is going to say if (i == 0), I take a step, 436 00:33:42,040 --> 00:33:44,930 we see we end up at dinky. 437 00:33:44,930 --> 00:33:51,620 If we list things, we see that it checked is i = 0. 438 00:33:51,620 --> 00:33:55,470 i is not equal to 0, so it went to the else condition, 439 00:33:55,470 --> 00:33:59,540 which is going to call dinky(i). 440 00:33:59,540 --> 00:34:04,030 You might get confused. 441 00:34:04,030 --> 00:34:07,380 If you just look at these lines directly, you might think if (i == 0), 442 00:34:07,380 --> 00:34:10,800 okay, then I took a step and now I'm at dinky(i), 443 00:34:10,800 --> 00:34:14,120 you might think that must mean i = 0 or something. 444 00:34:14,120 --> 00:34:18,980 No. It just means that it knows it can stick directly to the line dinky(i). 445 00:34:18,980 --> 00:34:23,300 Because i is not 0, the next step isn't going to end at the else. 446 00:34:23,300 --> 00:34:26,239 Else isn't a line it's going to stop at. 447 00:34:26,239 --> 00:34:31,570 It's just going to go to the next line it can actually execute, which is dinky(i). 448 00:34:31,570 --> 00:34:36,090 Stepping into dinky(i), we see if (i == 1). 449 00:34:36,090 --> 00:34:42,670 We do know i = 1, so when we step, we know we're going to end up in oh_no 450 00:34:42,670 --> 00:34:46,489 because i = 1 calls the function oh_no, which you can step into, 451 00:34:46,489 --> 00:34:52,969 which is going to set char** s = to NULL and immediately "BOOM". 452 00:34:54,270 --> 00:34:59,690 And then actually looking at the implementation of buggy2, 453 00:34:59,690 --> 00:35:04,590 this, i is just getting a random number--0, 1, or 2--calling binky, 454 00:35:04,590 --> 00:35:10,610 which if i is 0 it calls oh_no, else it calls dinky, which comes up here. 455 00:35:10,610 --> 00:35:18,100 If i is 1, call oh_no, else call slinky, which coming up here, 456 00:35:18,100 --> 00:35:20,460 if i is 2, call oh_no. 457 00:35:20,460 --> 00:35:24,720 I don't even think there is a way-- 458 00:35:24,720 --> 00:35:30,030 Does anyone see a way of making this a program that won't segfault? 459 00:35:30,030 --> 00:35:37,530 Because unless I'm missing something, if i is 0, you'll immediately segfault, 460 00:35:37,530 --> 00:35:41,250 else you go to a function which if i is 1 you segfault, 461 00:35:41,250 --> 00:35:44,540 else you go to a function where if i is 2 you segfault. 462 00:35:44,540 --> 00:35:46,810 So no matter what you do, you segfault. 463 00:35:46,810 --> 00:35:52,380 >> I guess one way of fixing it would be instead of doing char** s = NULL, 464 00:35:52,380 --> 00:35:55,610 you could malloc space for that string. 465 00:35:55,610 --> 00:36:04,230 We could do malloc(sizeof)--sizeof what? 466 00:36:09,910 --> 00:36:15,190 [student] (char) * 5? >>Does this seem right? 467 00:36:15,190 --> 00:36:21,060 I'm assuming this will work if I actually ran it, but it's not what I'm looking for. 468 00:36:24,400 --> 00:36:32,940 Look at the type of s. Let's add int*, so int* x. 469 00:36:32,940 --> 00:36:35,600 I would do malloc(sizeof(int)). 470 00:36:35,600 --> 00:36:40,490 Or if I wanted an array of 5, I would do (sizeof(int) * 5); 471 00:36:40,490 --> 00:36:44,210 What if I have an int**? 472 00:36:46,260 --> 00:36:49,140 What would I malloc? 473 00:36:49,140 --> 00:36:53,510 [student] Size of the pointer. >>Yeah. (sizeof(int *)); 474 00:36:53,510 --> 00:36:56,960 Same thing down here. 475 00:36:56,960 --> 00:37:01,280 I want (sizeof(char *)); 476 00:37:06,170 --> 00:37:12,840 This is going to allocate space for the pointer that points to "BOOM". 477 00:37:12,840 --> 00:37:15,330 I don't need to allocate space for "BOOM" itself 478 00:37:15,330 --> 00:37:17,210 because this is basically equivalent to what I said before 479 00:37:17,210 --> 00:37:20,870 of char *x = "BOOM". 480 00:37:20,870 --> 00:37:27,950 "BOOM" already exists. It happens to exist in the read-only region of memory. 481 00:37:27,950 --> 00:37:35,200 But it already exists, which means this line of code, if s is a char**, 482 00:37:35,200 --> 00:37:43,900 then *s is a char * and you're setting this char * to point to "BOOM". 483 00:37:43,900 --> 00:37:50,040 If I wanted to copy "BOOM" into s, then I would need to allocate space for s. 484 00:37:55,170 --> 00:38:03,900 I'll do *s = malloc(sizeof(char) * 5); 485 00:38:03,900 --> 00:38:06,210 Why 5? 486 00:38:06,210 --> 00:38:10,860 Why not 4? It looks like "BOOM" is 4 characters. >>[student] The null character. 487 00:38:10,860 --> 00:38:14,580 Yeah. All of your strings are going to need the null character. 488 00:38:14,580 --> 00:38:23,590 Now I can do something like strcat-- What is the function for copying a string? 489 00:38:23,590 --> 00:38:28,520 [student] cpy? >>strcpy. 490 00:38:28,520 --> 00:38:32,700 man strcpy. 491 00:38:36,120 --> 00:38:39,590 So strcpy or strncpy. 492 00:38:39,590 --> 00:38:43,410 strncpy is a bit safer since you can specify exactly how many characters, 493 00:38:43,410 --> 00:38:46,190 but here it doesn't matter because we know. 494 00:38:46,190 --> 00:38:50,340 So strcpy and look in the arguments. 495 00:38:50,340 --> 00:38:53,100 The first argument is our destination. 496 00:38:53,100 --> 00:38:56,770 The second argument is our source. 497 00:38:56,770 --> 00:39:10,310 We're going to copy into our destination *s the pointer "BOOM". 498 00:39:10,310 --> 00:39:19,820 Why might you want to do this with a strcpy instead of just what we had before 499 00:39:19,820 --> 00:39:22,800 of *s = "BOOM"? 500 00:39:22,800 --> 00:39:28,630 There is a reason you might want to do this, but what is that reason? 501 00:39:28,630 --> 00:39:31,940 [student] If you want to change something in "BOOM". >>Yeah. 502 00:39:31,940 --> 00:39:37,950 Now I can do something like s[0] = 'X'; 503 00:39:37,950 --> 00:39:48,190 because s points to the heap and that space on the heap that s is pointing to 504 00:39:48,190 --> 00:39:52,320 is a pointer to more space on the heap, which is storing "BOOM". 505 00:39:52,320 --> 00:39:55,150 So this copy of "BOOM" is being stored in the heap. 506 00:39:55,150 --> 00:39:58,780 There are technically two copies of "BOOM" in our program. 507 00:39:58,780 --> 00:40:03,500 There's the first one that's just given by this "BOOM" string constant, 508 00:40:03,500 --> 00:40:09,250 and the second copy of "BOOM", strcpy created the copy of "BOOM". 509 00:40:09,250 --> 00:40:13,100 But the copy of "BOOM" is being stored on the heap, and the heap you're free to change. 510 00:40:13,100 --> 00:40:17,250 The heap is not read-only, so that means that s[0] 511 00:40:17,250 --> 00:40:20,500 is going to let you change the value of "BOOM". 512 00:40:20,500 --> 00:40:23,130 It's going to let you change those characters. 513 00:40:23,130 --> 00:40:26,640 >> Questions? 514 00:40:27,740 --> 00:40:29,290 Okay. 515 00:40:29,290 --> 00:40:35,500 >> Moving on to buggy3, let's gdb buggy3. 516 00:40:35,500 --> 00:40:39,840 We just run it and we see we get a segfault. 517 00:40:39,840 --> 00:40:46,550 If we backtrace, there are only two functions. 518 00:40:46,550 --> 00:40:52,970 If we go up into our main function, we see that we segfaulted at this line. 519 00:40:52,970 --> 00:41:00,180 So just looking at this line, for (int line = 0; fgets this stuff does not equal NULL; 520 00:41:00,180 --> 00:41:03,770 line++). 521 00:41:03,770 --> 00:41:08,010 Our previous frame was called _IO_fgets. 522 00:41:08,010 --> 00:41:10,720 You'll see that a lot with built-in C functions, 523 00:41:10,720 --> 00:41:15,350 that when you get the segfault, there will be really cryptic function names 524 00:41:15,350 --> 00:41:18,090 like this _IO_fgets. 525 00:41:18,090 --> 00:41:21,770 But that's going to relate to this fgets call. 526 00:41:21,770 --> 00:41:25,850 Somewhere inside here, we are segfaulting. 527 00:41:25,850 --> 00:41:30,340 If we look at the arguments to fgets, we can print buffer. 528 00:41:30,340 --> 00:41:41,180 Let's print as a--Oh, no. 529 00:41:48,980 --> 00:41:51,900 Print isn't going to work exactly as I want it to. 530 00:41:55,460 --> 00:41:58,000 Let's look at the actual program. 531 00:42:02,200 --> 00:42:09,640 Buffer is a character array. It's a character array of 128 characters. 532 00:42:09,640 --> 00:42:14,980 So when I say print buffer, it's going to print those 128 characters, 533 00:42:14,980 --> 00:42:18,300 which I guess is what is expected. 534 00:42:18,300 --> 00:42:21,390 What I was looking for is print the address of buffer, 535 00:42:21,390 --> 00:42:23,680 but that doesn't really tell me much. 536 00:42:23,680 --> 00:42:30,770 So when I happen to say up here x buffer, it shows me 0xbffff090, 537 00:42:30,770 --> 00:42:38,690 which, if you remember from earlier or some point, Oxbffff tends to be a stack-ish region. 538 00:42:38,690 --> 00:42:46,020 The stack tends to start somewhere just under 0xc000. 539 00:42:46,020 --> 00:42:51,890 Just by seeing this address, I know that buffer is happening on the stack. 540 00:42:51,890 --> 00:43:04,500 Restarting my program, run, up, buffer we saw was this sequence of characters 541 00:43:04,500 --> 00:43:06,530 that are pretty much meaningless. 542 00:43:06,530 --> 00:43:12,270 Then printing file, what does file look like? 543 00:43:15,120 --> 00:43:17,310 [student] Null. >>Yeah. 544 00:43:17,310 --> 00:43:22,610 File is an of type FILE*, so it is a pointer, 545 00:43:22,610 --> 00:43:26,610 and the value of that pointer is null. 546 00:43:26,610 --> 00:43:33,240 So fgets is going to try to read from that pointer in an indirect way, 547 00:43:33,240 --> 00:43:37,320 but in order to access that pointer, it has to dereference it. 548 00:43:37,320 --> 00:43:40,550 Or, in order to access what it should be pointing to, it dereferences it. 549 00:43:40,550 --> 00:43:43,810 So it's dereferencing a null pointer and it segfaults. 550 00:43:46,600 --> 00:43:48,730 I could have restarted it there. 551 00:43:48,730 --> 00:43:52,170 If we break at our main point and run, 552 00:43:52,170 --> 00:43:57,320 the first line of code is char* filename = "nonexistent.txt"; 553 00:43:57,320 --> 00:44:00,870 That should give a pretty big hint as to why this program fails. 554 00:44:00,870 --> 00:44:06,080 Typing next brings me to the next line, where I open this file, 555 00:44:06,080 --> 00:44:11,140 and then I immediately get into our line, where once I hit next, it's going to segfault. 556 00:44:11,140 --> 00:44:16,880 Does anyone want to throw out a reason why we might be segfaulting? 557 00:44:16,880 --> 00:44:19,130 [student] File doesn't exist. >>Yeah. 558 00:44:19,130 --> 00:44:22,250 This is supposed to be a hint 559 00:44:22,250 --> 00:44:29,570 that whenever you're opening a file you need to check that the file actually exists. 560 00:44:29,570 --> 00:44:31,510 So here, "nonexistent.txt"; 561 00:44:31,510 --> 00:44:34,700 When we fopen filename for reading, we then need to say 562 00:44:34,700 --> 00:44:45,870 if (file == NULL) and say printf("File does not exist!" 563 00:44:45,870 --> 00:44:56,340 or--better yet--filename); return 1; 564 00:44:56,340 --> 00:45:00,300 So now we check to see if it's NULL 565 00:45:00,300 --> 00:45:03,930 before actually continuing and trying to read from that file. 566 00:45:03,930 --> 00:45:08,800 We can remake it just to see that that works. 567 00:45:11,020 --> 00:45:14,970 I intended to include a new line. 568 00:45:21,090 --> 00:45:25,290 So now nonexistent.txt does not exist. 569 00:45:26,890 --> 00:45:30,040 You should always check for this sort of thing. 570 00:45:30,040 --> 00:45:33,870 You should always check to see if fopen returns NULL. 571 00:45:33,870 --> 00:45:38,170 You should always check to make sure that malloc doesn't return NULL, 572 00:45:38,170 --> 00:45:41,410 or else you segfault. 573 00:45:42,200 --> 00:45:45,930 >> Now buggy4.c. 574 00:45:49,190 --> 00:45:58,440 Running. I'm guessing this is waiting for input or possibly infinite looping. 575 00:45:58,440 --> 00:46:01,870 Yes, it's infinite looping. 576 00:46:01,870 --> 00:46:05,560 So buggy4. It looks like we're infinite looping. 577 00:46:05,560 --> 00:46:12,590 We can break at main, run our program. 578 00:46:12,590 --> 00:46:20,180 In gdb, as long as the abbreviation you use is unambiguous 579 00:46:20,180 --> 00:46:23,420 or special abbreviations that they provide for you, 580 00:46:23,420 --> 00:46:29,020 then you can use n to use next instead of having to type out next all the way. 581 00:46:29,020 --> 00:46:33,730 And now that I've hit n once, I can just hit Enter to keep going next 582 00:46:33,730 --> 00:46:36,640 instead of having to hit n Enter, n Enter, n Enter. 583 00:46:36,640 --> 00:46:44,630 It looks like I'm in some kind of for loop that's setting array[i] to 0. 584 00:46:44,630 --> 00:46:50,510 It looks like I am never breaking out of this for loop. 585 00:46:50,510 --> 00:46:54,780 If I print i, so i is 2, then I'll go next. 586 00:46:54,780 --> 00:46:59,250 I'll print i, i is 3, then I'll go next. 587 00:46:59,250 --> 00:47:05,360 I'll print i and i is 3. Next, print i, i is 4. 588 00:47:05,360 --> 00:47:14,520 Actually, print sizeof(array), so size of array is 20. 589 00:47:16,310 --> 00:47:32,870 But it looks like there's some special gdb command for going until something happens. 590 00:47:32,870 --> 00:47:37,620 It's like setting a condition on the value of the variable. But I don't remember what it is. 591 00:47:37,620 --> 00:47:44,100 So if we keep going-- 592 00:47:44,100 --> 00:47:47,120 What were you saying? What did you bring up? 593 00:47:47,120 --> 00:47:50,500 [student] Does display i add-- >>Yeah. So display i can help. 594 00:47:50,500 --> 00:47:54,530 If we just display i, it will put up here what the value of i is 595 00:47:54,530 --> 00:47:56,470 so I don't have to print it out each time. 596 00:47:56,470 --> 00:48:02,930 If we just keep going next, we see 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5. 597 00:48:02,930 --> 00:48:08,530 Something is going terribly wrong, and i is being reset to 0. 598 00:48:13,330 --> 00:48:22,220 Looking at buggy4.c, we see all that happens is int array[5]; 599 00:48:22,220 --> 00:48:26,200 for (i = 0; i <= sizeof(array); i++) 600 00:48:26,200 --> 00:48:28,550 array[i] = 0; 601 00:48:28,550 --> 00:48:31,390 What do we see that's wrong here? 602 00:48:31,390 --> 00:48:39,480 As a hint, when I was doing the gdb buggy4--let's break main, run-- 603 00:48:39,480 --> 00:48:45,980 I did print sizeof(array) just to see what the condition is where I should finally break out. 604 00:48:47,690 --> 00:48:51,100 Where am I? Did I run? 605 00:48:51,100 --> 00:48:54,280 I didn't declare yet. 606 00:48:54,280 --> 00:48:58,680 So print sizeof(array) and that's 20, 607 00:48:58,680 --> 00:49:06,690 which is expected since my array is of size 5 and it's of 5 integers, 608 00:49:06,690 --> 00:49:12,410 so the entire thing should be 5 * sizeof(int) bytes, where sizeof(int) tends to be 4. 609 00:49:12,410 --> 00:49:14,780 So sizeof(array) is 20. 610 00:49:14,780 --> 00:49:17,420 What should this be? 611 00:49:17,420 --> 00:49:21,720 [student] Divided by sizeof(int). >>Yeah, / sizeof(int). 612 00:49:21,720 --> 00:49:30,630 It looks like there's still a problem here. I think this should just be < 613 00:49:30,630 --> 00:49:36,960 since it's pretty much always < and never <=. 614 00:49:36,960 --> 00:49:44,860 Now let's think about why this was actually broken. 615 00:49:44,860 --> 00:49:53,370 Does anyone have guesses why was i reset to 0 through each iteration of the loop? 616 00:50:01,300 --> 00:50:09,350 The only thing inside of here that's happening is that array[i] is being set to 0. 617 00:50:09,350 --> 00:50:15,350 So somehow, this line of code is causing our int i to be set to 0. 618 00:50:16,730 --> 00:50:23,130 [student] Could it be because it's overriding the memory of this part of i 619 00:50:23,130 --> 00:50:27,970 when it thinks it's the next element of array? >>[Bowden] Yes. 620 00:50:27,970 --> 00:50:33,880 When we're going beyond the end of our array, 621 00:50:33,880 --> 00:50:39,870 somehow that space that we're overriding is overriding the value of i. 622 00:50:39,870 --> 00:50:48,030 And so if we look into buggy4, break main, run, 623 00:50:48,030 --> 00:50:53,120 let's print the address of i. 624 00:50:53,120 --> 00:50:57,280 It looks like it's bffff124. 625 00:50:57,280 --> 00:51:03,930 Now let's print the address of array[0]. 110. 626 00:51:03,930 --> 00:51:06,290 What about [1]? 114. 627 00:51:06,290 --> 00:51:07,920 [2], 118. 628 00:51:07,920 --> 00:51:14,530 11c, 120. array[5] is bfff124. 629 00:51:14,530 --> 00:51:26,990 So array[5] has the same address as i, which means that array[5] is i. 630 00:51:26,990 --> 00:51:30,720 If they have the same address, they are the same thing. 631 00:51:30,720 --> 00:51:38,410 So when we set array[5] to 0, we are setting i to 0. 632 00:51:38,410 --> 00:51:46,070 And if you think about this in terms of the stack, 633 00:51:46,070 --> 00:51:55,590 int i is declared first, which means i gets some space on the stack. 634 00:51:55,590 --> 00:52:04,730 Then array[5] is allocated, so then 20 bytes are allocated on the stack. 635 00:52:04,730 --> 00:52:08,400 So i gets allocated first, then these 20 bytes get allocated. 636 00:52:08,400 --> 00:52:11,400 So i happens right before the array, 637 00:52:11,400 --> 00:52:19,230 and because of the way, like I said last week, where technically the stack grows down, 638 00:52:19,230 --> 00:52:28,520 when you index into an array, we are guaranteed that the 0th position in the array 639 00:52:28,520 --> 00:52:31,970 always happens before the first position in the array. 640 00:52:31,970 --> 00:52:35,900 This is kind of how I drew it last week. 641 00:52:35,900 --> 00:52:42,210 Notice that at the bottom we have address 0 and at the top we have address Max. 642 00:52:42,210 --> 00:52:44,880 The stack is always growing down. 643 00:52:48,100 --> 00:52:53,500 Let's say we allocate i. 644 00:52:53,500 --> 00:52:59,680 We allocate integer i, which means let's just say up here integer i gets allocated. 645 00:52:59,680 --> 00:53:06,420 Then we allocate our array of 5 integers, which means that underneath that, 646 00:53:06,420 --> 00:53:11,230 since the stack is growing down, those 5 integers get allocated. 647 00:53:11,230 --> 00:53:15,900 But because of how arrays work, we're guaranteed that the first position in the array 648 00:53:15,900 --> 00:53:22,260 always has an address less than the second thing in the array. 649 00:53:22,260 --> 00:53:28,270 So array position 0 always has to happen first in memory, 650 00:53:28,270 --> 00:53:30,700 whereas array position 1 has to happen after that 651 00:53:30,700 --> 00:53:33,310 and array position 2 has to happen after that, 652 00:53:33,310 --> 00:53:37,900 which means that array position 0 would happen somewhere down here, 653 00:53:37,900 --> 00:53:40,690 array position 1 would happen above that 654 00:53:40,690 --> 00:53:45,530 because moving up means higher addresses since the maximum address is up here. 655 00:53:45,530 --> 00:53:50,490 So array[0] down here, array[1] up here, array[2] up here, array[3] up here. 656 00:53:50,490 --> 00:53:55,620 Notice how before we allocated integer i all the way up here, 657 00:53:55,620 --> 00:54:01,040 as we move further and further into our array, we are getting closer and closer to our integer i. 658 00:54:01,040 --> 00:54:07,640 It just so happens that array[5], which is one position beyond our array, 659 00:54:07,640 --> 00:54:13,010 is exactly where integer i happened to be allocated. 660 00:54:13,010 --> 00:54:16,920 So that's the point where we happen to be hitting the space on the stack 661 00:54:16,920 --> 00:54:21,680 that was allocated for integer i, and we're setting that to 0. 662 00:54:21,680 --> 00:54:26,160 >> That's how that works. Questions? Yeah. 663 00:54:26,160 --> 00:54:30,710 [student] Never mind. Okay. 664 00:54:30,710 --> 00:54:33,090 [student] How do you avoid these sort of errors? 665 00:54:33,090 --> 00:54:41,190 These sort of errors? Don't use C as your programming language. 666 00:54:41,190 --> 00:54:45,840 Use a language that has array bounds checking. 667 00:54:45,840 --> 00:54:55,900 As long as you're careful, you just need to avoid going past the bounds of your array. 668 00:54:55,900 --> 00:54:58,300 [student] So here when we went past the bounds of your array-- 669 00:54:58,300 --> 00:55:01,840 [Bowden] That's where things start going wrong. >>[student] Oh, okay. 670 00:55:01,840 --> 00:55:05,730 As long as you stay within the memory allocated for your array, you're fine. 671 00:55:05,730 --> 00:55:12,400 But C does no error checking. If I do array[1000], it will gladly just modify whatever happens-- 672 00:55:12,400 --> 00:55:16,500 It goes to the beginning of the array, then it goes 1000 positions after and sets it to 0. 673 00:55:16,500 --> 00:55:20,000 It doesn't do any checking that oh, this doesn't actually have 1000 things in it. 674 00:55:20,000 --> 00:55:22,750 1000 is way beyond what I should be changing, 675 00:55:22,750 --> 00:55:26,940 whereas Java or something you'll get array out of bounds index 676 00:55:26,940 --> 00:55:29,820 or index out of bounds exception. 677 00:55:29,820 --> 00:55:33,950 That's why a lot of higher level languages have these things 678 00:55:33,950 --> 00:55:37,340 where if you go beyond the bounds of the array, you fail 679 00:55:37,340 --> 00:55:40,070 so that you can't change things from underneath you 680 00:55:40,070 --> 00:55:42,590 and then things go much worse than just getting an exception 681 00:55:42,590 --> 00:55:44,940 saying that you went beyond the end of the array. 682 00:55:44,940 --> 00:55:50,970 [student] And so should we have just changed the <= to just >[Bowden] Yeah. 683 00:55:50,970 --> 00:55:54,800 It should be < sizeof(array) / sizeof(int); 684 00:55:54,800 --> 00:55:59,560 since sizeof(array) is 20, but we only want 5. >>[student] Right. 685 00:55:59,560 --> 00:56:04,060 More questions? Okay. 686 00:56:04,060 --> 00:56:07,380 >> [student] I have a question. >>Yeah. 687 00:56:07,380 --> 00:56:16,440 [student] What is the actual array variable? 688 00:56:16,440 --> 00:56:20,000 [Bowden] Like what is array? 689 00:56:20,000 --> 00:56:24,930 Array itself is a symbol. 690 00:56:24,930 --> 00:56:31,490 It is just the address of the start of the 20 bytes that we are referencing. 691 00:56:31,490 --> 00:56:38,070 You can think of it as a pointer, but it is a constant pointer. 692 00:56:38,070 --> 00:56:44,140 As soon as things get compiled, the variable array does not exist anymore. 693 00:56:44,140 --> 00:56:48,210 [student] So how does it find the size of array? 694 00:56:48,210 --> 00:56:54,130 Size of array refers to the size of that block that that symbol refers to. 695 00:56:54,130 --> 00:57:01,240 When I do something like printf("%p\n", array); 696 00:57:01,240 --> 00:57:05,140 let's run it. 697 00:57:12,960 --> 00:57:15,530 What did I just do wrong? 698 00:57:15,530 --> 00:57:19,220 Array 'array' declared here. 699 00:57:20,820 --> 00:57:23,200 Oh, up here. 700 00:57:23,200 --> 00:57:31,250 Clang is clever, and it happens to notice that I declared the array as 5 elements 701 00:57:31,250 --> 00:57:34,540 but I'm indexing into position 1000. 702 00:57:34,540 --> 00:57:38,450 It can do that because these are just constants. 703 00:57:38,450 --> 00:57:43,370 It can only go so far in noticing that I'm going beyond the bounds of the array. 704 00:57:43,370 --> 00:57:46,880 But notice before when we had i be incorrect, 705 00:57:46,880 --> 00:57:51,040 it can't possibly determine how many values i could take on, 706 00:57:51,040 --> 00:57:55,540 so it can't determine that i was going beyond the end of the array. 707 00:57:55,540 --> 00:57:59,430 That's just Clang being clever. 708 00:57:59,430 --> 00:58:03,340 >> But now make buggy4. So what else am I doing wrong? 709 00:58:03,340 --> 00:58:05,970 Implicitly declaring library function 'printf'. 710 00:58:05,970 --> 00:58:14,960 I'm going to want to #include . 711 00:58:14,960 --> 00:58:18,710 Okay. Now running buggy4. 712 00:58:18,710 --> 00:58:24,840 Printing the value of the array like I did here, printing it as a pointer 713 00:58:24,840 --> 00:58:30,060 prints something that looks like this--bfb8805c--which is some address 714 00:58:30,060 --> 00:58:33,450 that's in the stack-ish region. 715 00:58:33,450 --> 00:58:41,820 Array itself is like a pointer, but it isn't an actual pointer, 716 00:58:41,820 --> 00:58:45,410 since a regular pointer we can change. 717 00:58:45,410 --> 00:58:54,700 Array is just some constant. The 20 blocks of memory start at address 0xbfb8805c. 718 00:58:54,700 --> 00:59:09,020 So bfb8805c through this address +20--or I guess -20-- 719 00:59:09,020 --> 00:59:17,400 is all of the memory allocated for this array. 720 00:59:17,400 --> 00:59:20,350 Array, the variable itself is not stored anywhere. 721 00:59:20,350 --> 00:59:27,660 When you're compiling, the compiler--hand wave at it-- 722 00:59:27,660 --> 00:59:33,060 but the compiler will just use where it knows array to be. 723 00:59:33,060 --> 00:59:36,090 It knows where that array starts, 724 00:59:36,090 --> 00:59:40,910 and so it can always just do things in terms of offsets from that beginning. 725 00:59:40,910 --> 00:59:43,960 It doesn't need a variable itself to represent array. 726 00:59:43,960 --> 00:59:53,730 But when I do something like int *p = array; now p is a pointer which points to that array, 727 00:59:53,730 --> 00:59:57,830 and now p actually does exist on the stack. 728 00:59:57,830 --> 01:00:01,950 I'm free to change p. I can do p = malloc. 729 01:00:01,950 --> 01:00:06,500 So it originally pointed to array; now it points to some space on the heap. 730 01:00:06,500 --> 01:00:09,620 I cannot do array = malloc. 731 01:00:09,620 --> 01:00:13,710 If Clang is clever, it will yell at me right off the bat. 732 01:00:17,000 --> 01:00:21,430 Actually, I'm pretty sure gcc would do this too. 733 01:00:21,430 --> 01:00:25,010 So array type 'int [5]' is not assignable. 734 01:00:25,010 --> 01:00:28,040 You cannot assign something to an array type 735 01:00:28,040 --> 01:00:30,500 because array is just a constant. 736 01:00:30,500 --> 01:00:34,760 It is a symbol which references those 20 bytes. I can't change it. 737 01:00:34,760 --> 01:00:37,690 >> [student] And where is the size of the array stored? 738 01:00:37,690 --> 01:00:40,670 [Bowden] It's not stored anywhere. It's when it's compiling. 739 01:00:40,670 --> 01:00:46,310 So where is the size of array stored? 740 01:00:46,310 --> 01:00:51,870 You can only use sizeof(array) inside of the function that the array is declared itself. 741 01:00:51,870 --> 01:01:03,150 So if I do some function, foo, and I do (int array[]) 742 01:01:03,150 --> 01:01:10,450 printf("%d\n", sizeof(array)); 743 01:01:10,450 --> 01:01:21,330 and then down here I call foo(array); 744 01:01:21,330 --> 01:01:24,840 inside of this function--let's run it. 745 01:01:34,200 --> 01:01:36,840 This is Clang being clever again. 746 01:01:36,840 --> 01:01:43,890 It's telling me that sizeof on array function parameter 747 01:01:43,890 --> 01:01:46,690 will return size of 'int *'. 748 01:01:46,690 --> 01:01:55,150 This would be an error if it's not what I wanted to happen. 749 01:01:55,150 --> 01:01:58,960 Let's actually turn off Werror. 750 01:02:14,950 --> 01:02:17,590 Warning. Warnings are fine. 751 01:02:17,590 --> 01:02:19,960 It will still compile as long as it has a warning. 752 01:02:19,960 --> 01:02:22,910 ./a.out is going to print 4. 753 01:02:22,910 --> 01:02:28,650 The warning that was generated is a clear indicator of what went wrong. 754 01:02:28,650 --> 01:02:34,120 This int array is just going to print sizeof(int *). 755 01:02:34,120 --> 01:02:39,790 Even if I put array[5] in here, it's still just going to print sizeof(int *). 756 01:02:39,790 --> 01:02:47,440 So as soon as you pass it into a function, the distinction between arrays and pointers 757 01:02:47,440 --> 01:02:49,670 is nonexistent. 758 01:02:49,670 --> 01:02:52,640 This happens to be an array that was declared on the stack, 759 01:02:52,640 --> 01:02:58,300 but as soon as we pass that value, that 0xbf blah, blah, blah into this function, 760 01:02:58,300 --> 01:03:03,350 then this pointer points to that array on the stack. 761 01:03:03,350 --> 01:03:08,310 So that means that sizeof only applies in the function that the array was declared, 762 01:03:08,310 --> 01:03:11,230 which means that when you are compiling this function, 763 01:03:11,230 --> 01:03:17,330 when Clang goes through this function, it sees array is an int array of size 5. 764 01:03:17,330 --> 01:03:20,640 So then it sees sizeof(array). Well, that's 20. 765 01:03:20,640 --> 01:03:26,440 That's actually how sizeof basically works for almost all cases. 766 01:03:26,440 --> 01:03:31,150 Sizeof is not a function; it's an operator. 767 01:03:31,150 --> 01:03:33,570 You don't call the sizeof function. 768 01:03:33,570 --> 01:03:38,280 Sizeof(int), the compiler will just translate that to 4. 769 01:03:41,480 --> 01:03:43,700 Got it? Okay. 770 01:03:43,700 --> 01:03:47,520 >> [student] So what is the difference between sizeof(array) in main and in foo? 771 01:03:47,520 --> 01:03:52,840 This is because we're saying sizeof(array), which is of type int *, 772 01:03:52,840 --> 01:03:57,120 whereas the array down here is not of type int *, it's an int array. 773 01:03:57,120 --> 01:04:04,540 >> [student] So if you had the parameter in array[] instead of int * array, 774 01:04:04,540 --> 01:04:09,230 would that mean that you could still change array because now it's a pointer? 775 01:04:09,230 --> 01:04:14,250 [Bowden] Like this? >>[student] Yeah. Can you change array within the function now? 776 01:04:14,250 --> 01:04:18,420 [Bowden] You could change array in both cases. 777 01:04:18,420 --> 01:04:23,130 In both of these cases you are free to say array[4] = 0. 778 01:04:23,130 --> 01:04:26,590 [student] But can you make array point to something else? 779 01:04:26,590 --> 01:04:30,230 [Bowden] Oh. Yeah. In either case-- >>[student] Yeah. 780 01:04:30,230 --> 01:04:38,410 [Bowden] The distinction between array[] and an int * array, there is none. 781 01:04:38,410 --> 01:04:42,570 You can also get some multidimensional array in here 782 01:04:42,570 --> 01:04:47,050 for some convenient syntax, but it's still just a pointer. 783 01:04:47,050 --> 01:04:56,400 This means that I am free to do array = malloc(sizeof(int)); and now point somewhere else. 784 01:04:56,400 --> 01:04:59,610 But just like how this works forever and always, 785 01:04:59,610 --> 01:05:03,210 changing this array by making it point to something else 786 01:05:03,210 --> 01:05:07,570 does not change this array down here because it's a copy of the argument, 787 01:05:07,570 --> 01:05:10,780 it's not a pointer to that argument. 788 01:05:10,780 --> 01:05:16,070 And actually, just as more indication that it's exactly the same-- 789 01:05:16,070 --> 01:05:21,100 we already saw what printing array prints-- 790 01:05:21,100 --> 01:05:31,410 what if we print the address of the array or the address of the address of the array 791 01:05:31,410 --> 01:05:36,290 to either of those? 792 01:05:41,770 --> 01:05:45,220 Let's ignore this one. 793 01:05:48,140 --> 01:05:51,660 Okay. This is fine. It's now running ./a.out. 794 01:05:51,660 --> 01:06:00,220 Printing array, then printing the address of the array, are the same thing. 795 01:06:00,220 --> 01:06:02,870 Array just doesn't exist. 796 01:06:02,870 --> 01:06:08,190 It knows when you're printing array, you're printing the symbol that refers to those 20 bytes. 797 01:06:08,190 --> 01:06:11,940 Printing the address of the array, well, array doesn't exist. 798 01:06:11,940 --> 01:06:17,200 It doesn't have an address, so it just prints the address of those 20 bytes. 799 01:06:20,820 --> 01:06:28,150 As soon as you compile down, like in your compiled buggy4 ./a.out, 800 01:06:28,150 --> 01:06:30,340 array is nonexistent. 801 01:06:30,340 --> 01:06:33,640 Pointers exist. Arrays do not. 802 01:06:34,300 --> 01:06:38,060 The blocks of memory representing the array still exist, 803 01:06:38,060 --> 01:06:43,270 but the variable array and variables of that type do not exist. 804 01:06:46,260 --> 01:06:50,270 Those are like the main differences between arrays and pointers 805 01:06:50,270 --> 01:06:55,590 are as soon as you make function calls, there is no difference. 806 01:06:55,590 --> 01:07:00,460 But inside of the function that the array itself is declared, sizeof works differently 807 01:07:00,460 --> 01:07:05,190 since you're printing the size of the blocks instead of the size of the type, 808 01:07:05,190 --> 01:07:08,950 and you can't change it because it's a symbol. 809 01:07:08,950 --> 01:07:14,370 Printing the thing and the address of the thing prints the same thing. 810 01:07:14,370 --> 01:07:18,480 And that's pretty much it. 811 01:07:18,480 --> 01:07:20,820 [student] Could you say that one more time? 812 01:07:21,170 --> 01:07:24,170 I might have missed something. 813 01:07:24,170 --> 01:07:29,260 Printing array and address of array prints the same thing, 814 01:07:29,260 --> 01:07:33,180 whereas if you print a pointer versus the address of the pointer, 815 01:07:33,180 --> 01:07:36,010 the one thing prints the address of what you're pointing to, 816 01:07:36,010 --> 01:07:40,360 the other prints the address of the pointer on the stack. 817 01:07:40,360 --> 01:07:47,040 You can change a pointer; you can't change an array symbol. 818 01:07:47,740 --> 01:07:53,270 And sizeof pointer is going to print the size of that pointer type. 819 01:07:53,270 --> 01:07:57,470 So int* p sizeof(p) is going to print 4, 820 01:07:57,470 --> 01:08:04,110 but int array[5] print sizeof(array) is going to print 20. 821 01:08:04,110 --> 01:08:07,480 [student] So int array[5] will print 20? >>Yes. 822 01:08:07,480 --> 01:08:13,300 That's why inside of buggy4 when it used to be sizeof(array) 823 01:08:13,300 --> 01:08:16,660 this was doing i < 20, which is not what we wanted. 824 01:08:16,660 --> 01:08:20,880 We want i < 5. >>[student] Okay. 825 01:08:20,880 --> 01:08:25,569 [Bowden] And then as soon as you start passing in the functions, 826 01:08:25,569 --> 01:08:34,340 if we did int* p = array; 827 01:08:34,340 --> 01:08:39,779 inside of this function, we can basically use p and array in exactly the same ways, 828 01:08:39,779 --> 01:08:43,710 except for the sizeof problem and the changing problem. 829 01:08:43,710 --> 01:08:49,810 But p[0] = 1; is the same as saying array[0] = 1; 830 01:08:49,810 --> 01:08:55,600 And as soon as we say foo(array); or foo(p); 831 01:08:55,600 --> 01:08:59,760 inside of the foo function, this is the same call twice. 832 01:08:59,760 --> 01:09:03,350 There is no difference between these two calls. 833 01:09:07,029 --> 01:09:11,080 >> Everyone good on that? Okay. 834 01:09:14,620 --> 01:09:17,950 We have 10 minutes. 835 01:09:17,950 --> 01:09:28,319 >> We'll try to get through this Hacker Typer program, 836 01:09:28,319 --> 01:09:32,350 this website, which came out last year or something. 837 01:09:34,149 --> 01:09:41,100 It's just supposed to be like you type randomly and it prints out-- 838 01:09:41,100 --> 01:09:46,729 Whatever file it happens to have loaded is what it looks like you're typing. 839 01:09:46,729 --> 01:09:52,069 It looks like some kind of operating system code. 840 01:09:53,760 --> 01:09:56,890 That's what we want to implement. 841 01:10:08,560 --> 01:10:11,690 You should have a binary executable named hacker_typer 842 01:10:11,690 --> 01:10:14,350 that takes in a single argument, the file to "hacker type." 843 01:10:14,350 --> 01:10:16,480 Running the executable should clear the screen 844 01:10:16,480 --> 01:10:20,850 and then print out one character from the passed-in file each time the user presses a key. 845 01:10:20,850 --> 01:10:24,990 So whatever key you press, it should throw away and instead print a character from the file 846 01:10:24,990 --> 01:10:27,810 that is the argument. 847 01:10:29,880 --> 01:10:34,350 I'll pretty much tell you what the things we're going to need to know are. 848 01:10:34,350 --> 01:10:36,440 But we want to check out the termios library. 849 01:10:36,440 --> 01:10:44,840 I have never used this library in my entire life, so it has very minimal purposes. 850 01:10:44,840 --> 01:10:48,610 But this is going to be the library we can use to throw away the character you hit 851 01:10:48,610 --> 01:10:52,390 when you are typing into standard in. 852 01:10:56,970 --> 01:11:05,840 So hacker_typer.c, and we're going to want to #include . 853 01:11:05,840 --> 01:11:12,870 Looking at the man page for termios--I'm guessing it's terminal OS or something-- 854 01:11:12,870 --> 01:11:16,240 I don't know how to read it. 855 01:11:16,240 --> 01:11:21,040 Looking at this, it says to include these 2 files, so we'll do that. 856 01:11:37,620 --> 01:11:46,820 >> First thing first, we want to take in a single argument, which is the file we should open. 857 01:11:46,820 --> 01:11:52,420 So what do I want to do? How do I check to see I have a single argument? 858 01:11:52,420 --> 01:11:56,480 [student] If argc equals it. >>[Bowden] Yeah. 859 01:11:56,480 --> 01:12:21,250 So if (argc != 2) printf("usage: %s [file to open]"). 860 01:12:21,250 --> 01:12:32,750 So now if I run this without providing a second argument--oh, I need the new line-- 861 01:12:32,750 --> 01:12:36,240 you'll see it says usage: ./hacker_typer, 862 01:12:36,240 --> 01:12:39,770 and then the second argument should be the file I want to open. 863 01:12:58,430 --> 01:13:01,260 Now what do I do? 864 01:13:01,260 --> 01:13:08,490 I want to read from this file. How do I read from a file? 865 01:13:08,490 --> 01:13:11,920 [student] You open it first. >>Yeah. 866 01:13:11,920 --> 01:13:15,010 So fopen. What does fopen look like? 867 01:13:15,010 --> 01:13:22,980 [student] Filename. >>[Bowden] Filename is going to be argv[1]. 868 01:13:22,980 --> 01:13:26,110 [student] And then what you want to do with it, so the-- >>[Bowden] Yeah. 869 01:13:26,110 --> 01:13:28,740 So if you didn't remember, you could just do man fopen, 870 01:13:28,740 --> 01:13:32,960 where it's going to be a const char *path where path is filename, 871 01:13:32,960 --> 01:13:34,970 const char *mode. 872 01:13:34,970 --> 01:13:38,660 If you happen to not remember what mode is, then you can look for mode. 873 01:13:38,660 --> 01:13:44,660 Inside of man pages, the slash character is what you can use to search for things. 874 01:13:44,660 --> 01:13:49,790 So I type /mode to search for mode. 875 01:13:49,790 --> 01:13:57,130 n and N are what you can use to cycle through the search matches. 876 01:13:57,130 --> 01:13:59,800 Here it says the argument mode points to a string 877 01:13:59,800 --> 01:14:01,930 beginning with one of the following sequences. 878 01:14:01,930 --> 01:14:06,480 So r, Open text file for reading. That's what we want to do. 879 01:14:08,930 --> 01:14:13,210 For reading, and I want to store that. 880 01:14:13,210 --> 01:14:18,720 The thing is going to be a FILE*. Now what do I want to do? 881 01:14:18,720 --> 01:14:21,200 Give me a second. 882 01:14:28,140 --> 01:14:30,430 Okay. Now what do I want to do? 883 01:14:30,430 --> 01:14:32,940 [student] Check if it's NULL. >>[Bowden] Yeah. 884 01:14:32,940 --> 01:14:38,690 Any time you open a file, make sure that you're successfully able to open it. 885 01:14:58,930 --> 01:15:10,460 >> Now I want to do that termios stuff where I want to first read my current settings 886 01:15:10,460 --> 01:15:14,050 and save those into something, then I want to change my settings 887 01:15:14,050 --> 01:15:19,420 to throw away any character that I type, 888 01:15:19,420 --> 01:15:22,520 and then I want to update those settings. 889 01:15:22,520 --> 01:15:27,250 And then at the end of the program, I want to change back to my original settings. 890 01:15:27,250 --> 01:15:32,080 So the struct is going to be of type termios, and I'm going to want two of those. 891 01:15:32,080 --> 01:15:35,600 The first one is going to be my current_settings, 892 01:15:35,600 --> 01:15:42,010 and then they're going to be my hacker_settings. 893 01:15:42,010 --> 01:15:48,070 First, I'm going to want to save my current settings, 894 01:15:48,070 --> 01:15:53,790 then I'm going to want to update hacker_settings, 895 01:15:53,790 --> 01:16:01,570 and then way at the end of my program, I want to revert to current settings. 896 01:16:01,570 --> 01:16:08,660 So saving current settings, the way that works, we man termios. 897 01:16:08,660 --> 01:16:15,810 We see that we have this int tcsetattr, int tcgetattr. 898 01:16:15,810 --> 01:16:22,960 I pass in a termios struct by its pointer. 899 01:16:22,960 --> 01:16:30,640 The way this will look is--I've already forgotten what the function was called. 900 01:16:30,640 --> 01:16:34,930 Copy and paste it. 901 01:16:39,150 --> 01:16:45,500 So tcgetattr, then I want to pass in the struct that I'm saving the information in, 902 01:16:45,500 --> 01:16:49,650 which is going to be current_settings, 903 01:16:49,650 --> 01:16:59,120 and the first argument is the file descriptor for the thing I want to save the attributes of. 904 01:16:59,120 --> 01:17:04,360 What the file descriptor is is like any time you open a file, it gets a file descriptor. 905 01:17:04,360 --> 01:17:14,560 When I fopen argv[1], it gets a file descriptor which you are referencing 906 01:17:14,560 --> 01:17:16,730 whenever you want to read or write to it. 907 01:17:16,730 --> 01:17:19,220 That's not the file descriptor I want to use here. 908 01:17:19,220 --> 01:17:21,940 There are three file descriptors you have by default, 909 01:17:21,940 --> 01:17:24,310 which are standard in, standard out, and standard error. 910 01:17:24,310 --> 01:17:29,960 By default, I think it's standard in is 0, standard out is 1, and standard error is 2. 911 01:17:29,960 --> 01:17:33,980 So what do I want to change the settings of? 912 01:17:33,980 --> 01:17:37,370 I want to change the settings of whenever I hit a character, 913 01:17:37,370 --> 01:17:41,590 I want it to throw that character away instead of printing it to the screen. 914 01:17:41,590 --> 01:17:45,960 What stream--standard in, standard out, or standard error-- 915 01:17:45,960 --> 01:17:52,050 responds to things when I type at the keyboard? >>[student] Standard in. >>Yeah. 916 01:17:52,050 --> 01:17:56,450 So I can either do 0 or I can do stdin. 917 01:17:56,450 --> 01:17:59,380 I'm getting the current_settings of standard in. 918 01:17:59,380 --> 01:18:01,720 >> Now I want to update those settings, 919 01:18:01,720 --> 01:18:07,200 so first I'll copy into hacker_settings what my current_settings are. 920 01:18:07,200 --> 01:18:10,430 And how structs work is it will just copy. 921 01:18:10,430 --> 01:18:14,510 This copies all of the fields, as you would expect. 922 01:18:14,510 --> 01:18:17,410 >> Now I want to update some of the fields. 923 01:18:17,410 --> 01:18:21,670 Looking at termios, you would have to read through a lot of this 924 01:18:21,670 --> 01:18:24,110 just to see what you would want to look for, 925 01:18:24,110 --> 01:18:28,210 but the flags you're going to want to look for are echo, 926 01:18:28,210 --> 01:18:33,110 so ECHO Echo input characters. 927 01:18:33,110 --> 01:18:37,710 First I want to set--I've already forgotten what the fields are. 928 01:18:45,040 --> 01:18:47,900 This is what the struct looks like. 929 01:18:47,900 --> 01:18:51,060 So input modes I think we want to change. 930 01:18:51,060 --> 01:18:54,210 We'll look at the solution to make sure that's what we want to change. 931 01:19:04,060 --> 01:19:12,610 We want to change lflag in order to prevent needing to look through all these. 932 01:19:12,610 --> 01:19:14,670 We want to change local modes. 933 01:19:14,670 --> 01:19:17,710 You would have to read through this entire thing to understand where everything belongs 934 01:19:17,710 --> 01:19:19,320 that we want to change. 935 01:19:19,320 --> 01:19:24,120 But it's inside of local modes where we're going to want to change that. 936 01:19:27,080 --> 01:19:33,110 So hacker_settings.cc_lmode is what it's called. 937 01:19:39,630 --> 01:19:43,020 c_lflag. 938 01:19:49,060 --> 01:19:52,280 This is where we get into bitwise operators. 939 01:19:52,280 --> 01:19:54,860 We're kind of out of time, but we'll go through it real quick. 940 01:19:54,860 --> 01:19:56,600 This is where we get into bitwise operators, 941 01:19:56,600 --> 01:19:59,950 where I think I said one time long ago that whenever you start dealing with flags, 942 01:19:59,950 --> 01:20:03,370 you're going to be using bitwise operator a lot. 943 01:20:03,370 --> 01:20:08,240 Each bit in the flag corresponds to some sort of behavior. 944 01:20:08,240 --> 01:20:14,090 So here, this flag has a bunch of different things, where all of them mean something different. 945 01:20:14,090 --> 01:20:18,690 But what I want to do is just turn off the bit which corresponds to ECHO. 946 01:20:18,690 --> 01:20:25,440 So to turn that off I do &= ¬ECHO. 947 01:20:25,440 --> 01:20:30,110 Actually, I think it's like tECHO or something. I'm just going to check again. 948 01:20:30,110 --> 01:20:34,050 I can termios it. It's just ECHO. 949 01:20:34,050 --> 01:20:38,440 ECHO is going to be a single bit. 950 01:20:38,440 --> 01:20:44,230 ¬ECHO is going to mean all bits are set to 1, which means all flags are set to true 951 01:20:44,230 --> 01:20:47,140 except for the ECHO bit. 952 01:20:47,140 --> 01:20:53,830 By ending my local flags with this, it means all flags that are currently set to true 953 01:20:53,830 --> 01:20:56,520 will still be set to true. 954 01:20:56,520 --> 01:21:03,240 If my ECHO flag is set to true, then this is necessarily set to false on the ECHO flag. 955 01:21:03,240 --> 01:21:07,170 So this line of code just turns off the ECHO flag. 956 01:21:07,170 --> 01:21:16,270 The other lines of code, I'll just copy them in the interest of time and then explain them. 957 01:21:27,810 --> 01:21:30,180 In the solution, he said 0. 958 01:21:30,180 --> 01:21:33,880 It's probably better to explicitly say stdin. 959 01:21:33,880 --> 01:21:42,100 >> Notice that I'm also doing ECHO | ICANON here. 960 01:21:42,100 --> 01:21:46,650 ICANON refers to something separate, which means canonical mode. 961 01:21:46,650 --> 01:21:50,280 What canonical mode means is usually when you're typing out the command line, 962 01:21:50,280 --> 01:21:54,670 standard in does not process anything until you hit newline. 963 01:21:54,670 --> 01:21:58,230 So when you do GetString, you type a bunch of things, then you hit newline. 964 01:21:58,230 --> 01:22:00,590 That's when it's sent to standard in. 965 01:22:00,590 --> 01:22:02,680 That's the default. 966 01:22:02,680 --> 01:22:05,830 When I turn off canonical mode, now every single character you press 967 01:22:05,830 --> 01:22:10,910 is what gets processed, which is usually kind of bad because it's slow to process these things, 968 01:22:10,910 --> 01:22:14,330 which is why it's good to buffer it into entire lines. 969 01:22:14,330 --> 01:22:16,810 But I want each character to be processed 970 01:22:16,810 --> 01:22:18,810 since I don't want it to wait for me to hit newline 971 01:22:18,810 --> 01:22:21,280 before it processes all the characters I've been typing. 972 01:22:21,280 --> 01:22:24,760 This turns off canonical mode. 973 01:22:24,760 --> 01:22:31,320 This stuff just means when it actually processes characters. 974 01:22:31,320 --> 01:22:35,830 This means process them immediately; as soon as I am typing them, process them. 975 01:22:35,830 --> 01:22:42,510 And this is the function which is updating my settings for standard in, 976 01:22:42,510 --> 01:22:45,480 and TCSA means do it right now. 977 01:22:45,480 --> 01:22:50,310 The other options are wait until everything that is currently on the stream is processed. 978 01:22:50,310 --> 01:22:52,030 That doesn't really matter. 979 01:22:52,030 --> 01:22:56,920 Just right now change my settings to be whatever is currently in hacker_typer_settings. 980 01:22:56,920 --> 01:23:02,210 I guess I called it hacker_settings, so let's change that. 981 01:23:09,610 --> 01:23:13,500 Change everything to hacker_settings. 982 01:23:13,500 --> 01:23:16,870 >> Now at the end of our program we're going to want to revert 983 01:23:16,870 --> 01:23:20,210 to what is currently inside of normal_settings, 984 01:23:20,210 --> 01:23:26,560 which is going to just look like &normal_settings. 985 01:23:26,560 --> 01:23:30,650 Notice I haven't changed any of my normal_settings since originally getting it. 986 01:23:30,650 --> 01:23:34,520 Then to just change them back, I pass them back at the end. 987 01:23:34,520 --> 01:23:38,390 This was the update. Okay. 988 01:23:38,390 --> 01:23:43,900 >> Now inside of here I'll just explain the code in the interest of time. 989 01:23:43,900 --> 01:23:46,350 It's not that much code. 990 01:23:50,770 --> 01:24:03,750 We see we read a character from the file. We called it f. 991 01:24:03,750 --> 01:24:07,850 Now you can man fgetc, but how fgetc is going to work 992 01:24:07,850 --> 01:24:11,910 is just it's going to return the character that you just read or EOF, 993 01:24:11,910 --> 01:24:15,680 which corresponds to the end of the file or some error happening. 994 01:24:15,680 --> 01:24:19,900 We are looping, continuing to read a single character from the file, 995 01:24:19,900 --> 01:24:22,420 until we've run out of characters to read. 996 01:24:22,420 --> 01:24:26,650 And while we're doing that, we wait on a single character from standard in. 997 01:24:26,650 --> 01:24:29,090 Every single time you type something at the command line, 998 01:24:29,090 --> 01:24:32,820 that's reading in a character from standard in. 999 01:24:32,820 --> 01:24:38,330 Then putchar is just going to put the char we read up here from the file to standard out. 1000 01:24:38,330 --> 01:24:42,890 You can man putchar, but it's just putting to standard out, it's printing that character. 1001 01:24:42,890 --> 01:24:51,600 You could also just do printf("%c", c); Same idea. 1002 01:24:53,330 --> 01:24:56,670 That's going to do the bulk of our work. 1003 01:24:56,670 --> 01:25:00,300 >> The last thing we're going to want to do is just fclose our file. 1004 01:25:00,300 --> 01:25:03,310 If you don't fclose, that's a memory leak. 1005 01:25:03,310 --> 01:25:06,680 We want to fclose the file we originally opened, and I think that's it. 1006 01:25:06,680 --> 01:25:13,810 If we make that, I already got problems. 1007 01:25:13,810 --> 01:25:17,260 Let's see. 1008 01:25:17,260 --> 01:25:19,960 What did it complain about? 1009 01:25:19,960 --> 01:25:30,220 Expected 'int' but argument is of type 'struct _IO_FILE*'. 1010 01:25:36,850 --> 01:25:39,370 We'll see if that works. 1011 01:25:45,210 --> 01:25:53,540 Only allowed in C99. Augh. Okay, make hacker_typer. 1012 01:25:53,540 --> 01:25:57,760 Now we get more useful descriptions. 1013 01:25:57,760 --> 01:25:59,900 So use of undeclared identifier 'normal_settings'. 1014 01:25:59,900 --> 01:26:04,170 I didn't call it normal_settings. I called it current_settings. 1015 01:26:04,170 --> 01:26:12,090 So let's change all of that. 1016 01:26:17,920 --> 01:26:21,710 Now passing argument. 1017 01:26:26,290 --> 01:26:29,500 I'll make this 0 for now. 1018 01:26:29,500 --> 01:26:36,720 Okay. ./hacker_typer cp.c. 1019 01:26:36,720 --> 01:26:39,590 I also didn't clear the screen at the beginning. 1020 01:26:39,590 --> 01:26:42,960 But you can look back to the last problem set to see how you clear the screen. 1021 01:26:42,960 --> 01:26:45,160 It's just printing some characters 1022 01:26:45,160 --> 01:26:47,210 while this is doing what I want to do. 1023 01:26:47,210 --> 01:26:48,900 Okay. 1024 01:26:48,900 --> 01:26:55,280 And thinking about why this needed to be 0 instead of stdin, 1025 01:26:55,280 --> 01:27:00,560 which should be #define 0, 1026 01:27:00,560 --> 01:27:03,890 this is complaining that-- 1027 01:27:13,150 --> 01:27:19,360 Before when I said that there's file descriptors but then you also have your FILE*, 1028 01:27:19,360 --> 01:27:23,210 a file descriptor is just a single integer, 1029 01:27:23,210 --> 01:27:26,970 whereas a FILE* has a whole bunch of stuff associated with it. 1030 01:27:26,970 --> 01:27:30,380 The reason we need to say 0 instead of stdin 1031 01:27:30,380 --> 01:27:37,480 is that stdin is a FILE* which points to the thing that is referencing file descriptor 0. 1032 01:27:37,480 --> 01:27:45,070 So even up here when I do fopen(argv[1], I'm getting a FILE* back. 1033 01:27:45,070 --> 01:27:51,180 But somewhere in that FILE* is a thing corresponding to the file descriptor for that file. 1034 01:27:51,180 --> 01:27:57,430 If you look at the man page for open, so I think you'll have to do man 3 open--nope-- 1035 01:27:57,430 --> 01:27:59,380 man 2 open--yeah. 1036 01:27:59,380 --> 01:28:06,250 If you look at the page for open, open is like a lower-level fopen, 1037 01:28:06,250 --> 01:28:09,350 and it's returning the actual file descriptor. 1038 01:28:09,350 --> 01:28:12,050 fopen does a bunch of stuff on top of open, 1039 01:28:12,050 --> 01:28:17,640 which instead of returning just that file descriptor returns a whole FILE* pointer 1040 01:28:17,640 --> 01:28:20,590 inside of which is our little file descriptor. 1041 01:28:20,590 --> 01:28:25,020 So standard in refers to the FILE* thing, 1042 01:28:25,020 --> 01:28:29,120 whereas 0 refers to just the file descriptor standard in itself. 1043 01:28:29,120 --> 01:28:32,160 >> Questions? 1044 01:28:32,160 --> 01:28:35,930 [laughs] Blew through that. 1045 01:28:35,930 --> 01:28:39,140 All right. We're done. [laughs] 1046 01:28:39,140 --> 01:28:42,000 >> [CS50.TV]