
Lesson Description
The "Hello World using printf" Lesson is part of the full, C Fundamentals course featured in this preview video. Here's what you'd learn in this lesson:
Richard refactors the "hello world" example using printf instead of write. The printf function is a wrapper around write, adding better ergonomics since the length of the string doesn't need to be calculated. The main function also returns 0, indicating the program was successfully completed.
Transcript from the "Hello World using printf" Lesson
[00:00:00]
>> Richard Feldman: Now, I wanna note that you can use this assembly, don't worry, you're not supposed to be able to read that. To sort of compare and just give you an idea of what this overhead looks like in terms of the hardware. So here is the C assembly code that we just saw.
[00:00:12]
For C++ Hello, World, here's what it is. It's like, okay, there's a little bit more, but not a whole lot more. And then, if you look at something like Go, it's this. And also, if you look closely in inside of what's actually going on in here, there's a whole bunch of jumps and stuff like that.
[00:00:27]
So it's actually jumping to a bunch of other stuff that's doing even more work. So really, if you wanted to see the equivalent of how much actual hardware instructions are going into Go's Hello, World versus a C or a C++, you actually need multiple more screens of this.
[00:00:42]
Because it's doing so much to set up the garbage collector and the virtual machine and all that kinda stuff. So, yeah, this is where the overhead comes from. C's performance is not because it's doing something extra, it's just because it's doing less, at least in terms of what it compiles to.
[00:00:57]
All of these C successor languages that we talked about in the previous section, like Rust, Zig, Jai, Odin, Carbon, they're all trying to be over on this side of things. They're like, really, compared to C, we wanna have some more ergonomic features, but we really, really wanna minimize the extra overhead.
[00:01:12]
A lot of these languages were okay with saying, yeah, as C++ is here, it's okay if there's a little bit of extra overhead, as long as it's not really noticeable in the benchmarks. C is absolute minimum, but they're okay with that as long as the ergonomics get significantly better.
[00:01:27]
So that's kind of what you're getting if you're looking into one of these C alternatives. Okay, so, going back to this, as we've seen, this generating the absolute minimum number of hardware instructions, if anything does benchmark faster than this implementation of Hello, World. You compare this to a C++ version or whatever else, and you find that somebody else has written one that that is running faster, this tells you that your benchmark is wrong.
[00:01:52]
Because again, you cannot minimize the number of instructions the hardware does anymore, it literally is unbeatably fast. Normally that's not something you can say. Normally you say, well, look, if you thought this was theoretically faster but the other one benchmarked faster, then obviously the other one's faster. But of course, there's always the possibility that you messed up your benchmarks.
[00:02:08]
This is one of the very rare cases where you can confidently say, if something else seems to run faster than this, that means the benchmark is wrong somehow. Okay, so to make this a little bit more ergonomic, sorry, idiomatic, I did talk about, saying a void here, meaning main is not returning anything.
[00:02:24]
It is a little bit more common, or I guess, idiomatic to actually have main return an integer and explicitly return the exit code that you want the program to do when it finishes running. So when you run main, like 0, just by convention, 0 generally means the program succeeded, everything went fine.
[00:02:42]
Non zero generally means something went wrong, and then there's not really a ton of consistency in terms of what the non zero ones mean in terms of what they're trying to convey. In terms of what actually went wrong. But I mentioned this in large part because when we get to the exercises, we're gonna do it in this style, because the C compiler will actually give you a warning if you do it in this style.
[00:03:01]
But for learning purposes, I wanted to start with that cuz it's a little bit simpler. The other thing that we can do to improve the ergonomics of this is, instead of using this write function, we can use a wrapper around write called printf. And printf, as we will see later, the main reason that people use it, so this is print formatted, the f is for formatted.
[00:03:20]
Again, normally we would probably see some camel case or some snake case [LAUGH] in these names, but this is C, 1972, they just went print, just stick an f on the end. It's the formatted version of print, really minimizing your teletype ink. And basically, this just takes the string.
[00:03:35]
And as previously mentioned, this will do things like automatically calculating the length of the string and automatically writing to standard out. So you don't have to say one for standard out and 13 for the length of the string, printf will just take care of that for you. Also, as we will see later in the course, printf has additional features that let you do some basic string interpolation type things.
[00:03:52]
So if you wanted to like print a string in the middle of this without having to concatenate it together ahead of time, printf also lets you do that. You can also print numbers in here and it'll convert them to strings for you, things like that. So we're gonna use printf quite a bit more throughout the course of the course.
[00:04:05]
But in terms of getting as close to the middle as possible, printf is just a convenience wrapper around write. Instead of coming from unisted, it comes from stdio, which is for standard input output. So usually, when you see a C Hello World example, they will show you this.
[00:04:22]
This is almost exactly what you will see for C Hello World. It's like, int main, return 0, printf. But I did wanna go over, this is the actual closest to the metal version you can get without getting into operating system land. Okay, yeah, the F is for formatted, and formatted meaning, it supports the things like, if you wanna interpolate a string in there or a number or something like that.
[00:04:46]
So here's an example of using that interpolation feature, the actual formatted part. So we can define a local variable here, say, int num = 42. So as with our main function declaration, we are defining a local variable here by first saying the type, and then saying the name of the thing, and then equals, and then the actual value.
[00:05:06]
Worth noting that main is a special name. So you can name your functions whatever you want. You can write, of course, in the course of this course, we're gonna write helper functions and stuff like that. But if you specifically name it, M-A-I-N, that is special in the C compiler world.
[00:05:20]
That is basically the function that C looks for as the entry point for your program. So this is what's gonna run when your program runs. Obviously, in Node.js or something like that, you don't need to do this, you can just start writing your code. It's fine, but that's not the way it's done in C.
[00:05:35]
In C, you actually have to wrap it in a function named main. All the other functions can be named whatever you want, but main is special. So, int num = 42. And then, basically, in order to substitute num into here, where we wanna say the number is followed by the actual number, printf takes multiple arguments if you want.
[00:05:53]
So you can just keep giving it as many different arguments as you want. That's one of the things that C supports. It's called var args, for varying number of args. And essentially, what you do here is you use a format specifier to say, here is the type of the thing that I want to interpolate in here.
[00:06:09]
So %d, d basically means digit, or digits. So it's essentially like, yeah, give me an integer over here and I will print its digits out. As we will see, there are different format specifiers for different types. So %s is if you gave it a string. So this is not quite as convenient as usual, like dollar sign, curly braces, string interpolation style.
[00:06:33]
But again, 1972. So, we mentioned this a little bit earlier, but why is it called print? And why is there no underscore before f? And again, it's because PDP-11 teletypes. It was physically printing back when C was originally created in 1972. All right, so, let's sort of summarize all the things we talked about in part 1.
[00:06:54]
So this is Hello, World in C. We start off by just talking about the lowest level, closest to the metal version of that that you can get. What does the actual hardware see? And we went through the assembly language of the actual instructions that the the CPU would be seeing.
[00:07:09]
We talked about the performance implications of this, namely that it is unbeatable fast. You cannot write a Hello, World that runs faster than this, because we were executing the minimal number of instructions that we could possibly do on the hardware. And then finally, we talked about some ways to very slightly improve the ergonomics, like using printf instead of write.
[00:07:24]
So we don't have to specify the standard out integer and the length of the string. And then also, being a little bit more idiomatic with using a return like int rather than void.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops