CS50 x 2024 Notes C -12

But let's go ahead now and solve some actual problems.

And it's no coincidence that we keep showing or alluding to Super Mario Brothers in some form, an older game from the Nintendo Entertainment System, that allows you ultimately to have this two-dimensional world, where Mario moves up and down and side scrolls from left to right. But you'll see we can distill even some aspects of " Mario " into some fairly representative programming problems.

And in fact, let me propose that we consider this screen from the original Super Mario Brothers. So there's these four blocks in the sky, each with a question mark. And if you click on one of these or if Mario jumps up underneath each of these question marks, he gets like a coin or something else that pops out.

Let's distill this, though, into its essence and consider in C, we make not a blue sky yet, not a green grassy hill, and so forth, but how can we just make four question marks in a row, because I dare say that we do have the building blocks via which to do this.

Well, the simplest way might be to go over here and run code of mario.c. And then in mario.c, let's include some stdio.h, so we have printf. Let's do int main( void ), as we keep doing. And inside of main, let's keep it super simple - - 1, 2, 3, 4, backslash n. Doesn't get much simpler than that. This is not going to be the prettiest of games.

But if I make mario.c, ./mario, I get my four question marks in the sky. All right, so it's not all that interesting. But this is clearly a candidate for what type of programming feature. So some kind of loop, right ? So print the thing out iteratively instead.

So let me do that. Instead of just printing this out all at once, let go ahead and remove this and do for int i gets 0; i less than 4; i++. And then in here, let me go ahead and print out just one question mark instead. And now let's run this. So, make mario.c to recompile it, ./mario. And does anyone not want me to hit Enter yet ? Yeah, it's going to print out a new line every time.

So notice it's four question marks, but there each on its own line.

All right, well, let me fix this. It's obviously because of the backslash n. So let me remove that. Let me rerun make mario, ./mario. And it's better in one way but worse in another. So wait, but now the dollar sign is doing that thing. Where it's on the same line, which just looks stupid, if nothing else so how can I fix that ? Yeah, so logically, we don't have that many building blocks today. It's a lot of new syntax, but it's not that many new ideas.

Let's just use printf to print out literally one and only one of these backslash n's, but outside of the loop, so it happens after all four of those have been printed. All right, let me do make mario again, ./mario. And ok, now we're back in business. So sort of silly syntactical details, but if you reduce the problem to its essence, it should hopefully, logically become clear over time.

And let me propose, for the sake of discussion, that this big wall here is like a 3-by-3 grid of bricks. So it's not just a single brick. It's like 3-by-3, or nine total. Now things get interesting.

And let me go back to mario.c. I could take the easy road out and just say, all right, well.

Let's printf, how about 1, 2, 3, backslash n, close quote. And then, ok, let me just copy/paste. And I'm using hashes instead of the actual bricks. Let me now go ahead and " make mario " again, ./mario. And it doesn't quite look like a square. But that's just because the hashes are a little taller than they are wide. But it is correct, but not well designed. So here, too, what would be better designed than just hardcoding, typing literally all of these hashes ? Interesting, two loops. And why two loops instead of one ? So one for vertical, one for horizontal. And even though these predate most of us, like old-school typewriters you might know or might recall that if you feed a piece of paper into it, you can print like line, then it scrolls, line, then it scrolls, line, then it scrolls. This is kind of how the terminal windows works, too. You can print rows and columns, but you have to print one row at a time, one row at a time, one row at a time. It's not easy, but it is possible to go backwards and go up and down. But just going row by row by row is more typical. So how can I do this ? Well, I could use at least one and maybe even indeed two loops. And this is where we're just now composing different ideas from today and even last week.

So let me go ahead and say, for int i gets 0; i less than 3 for a 3-by-3 grid - - i++. And now let me cheat slightly, let me print out just three of these here, and that's it. So I'm kind of cheating. I'm printing out rows dynamically, but I'm still printing three colums all in one breath.

But let's see what happens. Make mario, ./mario, and it does work. But what if you said, no, I want 4-by-4 or 5-by-5, or 6-by-6 ? No, I have to change the 3 to a 6, and I have to add another three hashes here. Things get messy if we don't do this mathematically. So let me now do this instead. Why don't I go ahead and print out every row at a time. But for each row, let me use another loop to decide, like rat-a-tat-tat from left to right, how many do I want to print. So to do this, I could do another for loop.

I could call this variable something different, j is pretty common. We start at i, we go to j. If you go past k, maybe l, you're probably doing something wrong. You don't want nested, nested, nested loops, but two is ok, j equals 0; j is less than 3; j++. And then here, I can print out a single one of these and no new line. I don't want to screw up like I did before. So I'll just do one, let me go ahead and do make mario now, ./mario. But when I hit Enter, this is not correct yet, what's it going to look like ?

A single line of nine hashes, I think, because I never used a single backslash n. So that looks wrong. So between what line number should I insort a printf of backslash n ?

Between 10 and 11. So I'm going to go in here. I'm going to add printf, quote, unquote, " backslash n ", semicolon. Let me go back and recompile mario - - ./mario. And crossing fingers - -voila, perfect. I printed out now a 3-by-3. Now, it's correct. It's not, if we want to be really nitpicky, maybe still not the best design. Where am I perhaps repeating myself ?

Yeah, I mean, it's not a huge deal. But now I have two, people would call these magic numbers, " magic " in the sense of where did that come from ? You just randomly put it in the middle of your code. And you also put the same thing here. Now I have to make sure I don't screw up and make one change but not the other. So it turns out we can factor these act.

I can actually do something like this, int n equals 3. And then I can just change this to n and this to n which is marginally better because now I only have to change n in one place if I want to make this thing bigger or smaller. It's still going to work the same. So make mario, ./mario. There's our 3-by-3.

But if I want to make a 5-by-5, let me change the n to 5, rerun make mario, ./mario. And now it's a bigger grid, 5-by-5. But this is a little fragile. And it turns out there's another trick we should introduce.

It turns out that C supports what are called constants, whereby if you have a variable that you want to exist because it's useful but you don't to accidentally change it, or if you're working with a partner in class or a colleague to accidentally change that value with their own code,

you can go into your code and tell C this is actually a constant integer, a const, so to speak. And this will just prevent you or someone else from doing something stupid by accidentally changing it elsewhere. The code is still going to work the same but you won't be accidentally able to change it very easily to something else. And honestly, what we've now done, too, is set ourselves up to make this more dynamic.

Let me go up here, and let me add the CS50 library so that we have access to get_int because now we could do something fancy like ask the get_int function for the size of this brick wall. And then we can use n dynamically.

So for instance, let me increase the size of my terminal, make mario, ./mario, size 3. Gives me a 3-by-3. ./mario, size 5, gives me a 5-by-5.

./mario, how about 50, gives me a crazy big one, but it's all dynamic. And now I don't have to even change the code. It just now works.

As an aside, if you're wondering how I type so darn fast.

Sometimes it's just because I'm hitting the up arrow. It turns out that Linux will remember, if you configure it this way, all of your previous commands.

So if you hit up,

up,

up,

up. I can go through the past couple of hours of commands that I've typed which is useful sometimes - - not for hours of commands but the past few - - just to save yourself some keystrokes.

And another trick in a terminal window is to do this. If I do ./ma and I get bored and I don't want to type out " rio ". I can also just hit Tab, and it will autocomplete based on the characters that do match.

But let's do this. It's kind of broken, arguably, if I do this how about " cat " ? That prevents me from doing something stupid because get_int only accepts integers. But it will accept 0, which does nothing. It will accept -1, which does nothing. And that's not bad. It's not doing something weird. But it would be nice to catch that and force the user to give us a positive integer instead, so we at least see something on the screen.

So let me go back into my code and let me propose that now we have the CS50 library, why don't we do something like this ? I'm going to get rid of the constant just in case the user needs to type it again.

And what if I do this ? While n is less than 1 - - so if it's 0, -1, -2 or whatever, let's go ahead and again ask the user for an int, and ask them for the size again. And therefore, only once n is not less than 1 will this loop break out and will proceed with the rest of code. So now let me try this.

Make mario, ./mario, 0 didn't like that, -1 didn't like that, -2 didn't like that, 3 it did like that. So using a loop now, I can ensure that the human is providing me with input that I actually want.

So this is correct. But I dare say 6 through 10 could be done better. Why is this poorly designed instinctively ? What's the repetition, to be clear, what lines ? 6 and 9. Ok, so they're literally the same and that's generally not a good thing.

And maybe I could change this one to remind the user like, hey, that's not a positive number. So you might want to customize the message. But just having copy/paste here for the most part is not a good thing. So it turns out - - and there's just one feature of C we wanted to introduce you to today - - it turns out there's one other way that would actually help us eliminate this redundancy of using get_int twice and particularly asking literally the same question - - size twice in duplicate.

So I'm actually going to go into my code here, and I'm going to delete the loop as we've written it thus far.

And instead of using a while loop, I'm going to introduce instead something that we typically call a do while loop, which is a little bit different. Indeed, we begin with the keyword " do " and then inside of the curly braces, what I'm going to do here is that thing I might want to do once and more times thereafter. So for instance, I'm going to say n equals get_int quote, unquote, " size ". And then at the bottom of this block of code then I'm going to use the keyword " while " as Boolean expression. And here, I'm going to ask the question, do this while n is less than 1. But there's one fix I still need to do here because notice on the line 8. I actually haven't given n a type. I haven't declared n yet.

But it would not be correct to declare n here inside of that do block. But why might that be ? Why would it not be a good thing to declare n inside of these curly braces ? Yeah, so recall that this is an issue of scope. Recall that the scope of a variable is generally confined to the most recently opened curly braces in which that variable is declared. And so if I declare this variable on line 8. I'm not going to be able to use it on line 10. But there is a fix, even though it might look a little strange.

I'm going to go above my do block here. And before I go into this loop, I'm actually going to declare n to be an integer, but semicolon end of thought. I'm not going to bother giving it a value, because I know logically I'm going to end up giving it a value anyway now on line 9. And so what's different about this version of the code is that the do while loop ensures that we prompt the user for input at least once. And then while that input is not what we expect, for instance, less than 1, then it's going to execute again, again, again. And indeed, the semantics are just that. Do the following while this Boolean expression is true.

So if I go ahead now and rerun make mario, compiles ok - - ./mario. And now I'll go ahead and input something that's not correct like 0. But I'm prompted again. I'll put something like -1. and I'm prompted again. But if I go ahead and input, for instance, 10 now, because that's a positive integer, I indeed get a 10-by-10 grid of bricks.

相关推荐
jimy11 小时前
gnu89和c99中的inline关键字的关系和差异
c语言
techdashen1 小时前
深入 Rust enum 的内存世界
开发语言·后端·rust
yuhuofei20212 小时前
【Python入门】Python与PyCharm的安装
开发语言·python·pycharm
吴声子夜歌2 小时前
Java——类加载机制
java·开发语言·python
杨校2 小时前
杨校老师课堂之C++的位运算应用专项训练
开发语言·c++
笨蛋不要掉眼泪2 小时前
Java并发编程:线程的创建和运行
java·开发语言·jvm
九伯都2 小时前
java编写 agent 入门案例
java·开发语言
代码中介商2 小时前
C++ STL 容器完全指南(三):deque、list 与 map 深度详解
开发语言·c++
xqqxqxxq2 小时前
Java 线程池(一)
java·开发语言