Lesson 1: Hello, World!
It's time to dive in and look at some actual C source code. I present to you the classic Hello World program:
1 #include <stdio.h>
2 int main() {
3 printf("Hello, World!\n");
4 return 0;
5 }
With your new, amazing text editor of choice, type the above and save the file as hello.c. I have purposefully left the line numbers in the code to make it more difficult for you to copy and paste the examples. Hee. I have always found that typing out examples myself helps me remember them better. Besides, typing code is kind of like playing piano: the more you practice, the better you get. Consider this an exercise to bring you and your text editor closer together.
So, What is This?
Quite simply, this is a program. For the moment, don't worry about exactly how it works. It is a file on your computer that contains only text. Specifically, it is a C program. It is a series of lines of source code that act as input to the compiler. The compiler then transforms the source code into machine code.
Why? It may not seem like it, but computers are extremely simple. They cannot understand source code directly. It is too high-level. Expecting a computer to directly execute source code is like trying to train your dog to drive a car by calmly sitting him down and explaining how to do it. It just isn't going to work. Your dog only understands a small set of extremely simple instructions. Sit. Stay. Fetch. Drive. (Lol, I'm just kidding about that last one. Can you imagine? A driving dog!) But you can understand this high-level language. At least you will be able to if you continue to read these posts. It is the compiler's one and only task to translate this high-level, human-readable language into something your computer can understand: a long series of very simple, binary code instructions.
Now that you understand what source code is a little better, you can imagine programming in C as telling the compiler what to translate so your computer does what you want.
Let's Try It!
Fire up your console (terminal), and navigate to the directory (folder) in which you saved your hello.c file. (Type cd /path/to/file. For example, if you're on Mac OS X and you saved the file to your Desktop, type cd /Users/<username>/Desktop.) Once you're there, type gcc hello.c. If it worked, you won't see any output in your console! Awesome! (If you see anything, it means you probably typed something incorrectly, or the file is not actually where you think it is on your computer.)
So what happened? The compiler took your hello.c file and turned it into an executable file named a.out. This is something your computer can understand. Let's run it! Type ./a.out in your console. You should see a single line of output:
Hello, World!
Then your console should be ready for the next command. Cool.
Before we get into how this program works, let's note that a.out is a pretty terrible name for an executable. Let's recompile the program to give us a differently-named file. Type gcc -o hello hello.c. This command tells the compiler to create an executable named hello. Run it by typing ./hello. You should see the exact same result as when you ran ./a.out. Rad.
How Does It Work?
Let's not start at the beginning. Instead, we'll start at the middle. If you haven't guessed by now, line 3 is where the real action is:
printf("Hello, World!\n");
printf is an example of a function call. For now, don't worry about what that means, but keep the term in the back of your head. In the above, we are passing it a single argument, the string "Hello, World!\n". These terms are all important, but let's focus on just one for now: a string is a sequence of characters. That's pretty much it. In this example, our string is a literal, meaning it is written directly into the source code. We'll talk about non-literal strings in the coming lessons. The printf function takes this string, and then prints it out. You do not need to worry about exactly how it works (for now).
But what is that weird "\n", you ask? Excellent question, my highly intelligent and beautiful imaginary pupil! "\n" is a special character. The "\" in front of the "n" tells the compiler to interpret the "\" plus the character immediately after as a single character. Specifically, "\n" is the newline character. When your computer sees this character, rather than printing something visible, it starts a new line. In our example, this has the courteous effect of returning us to the command prompt. Try removing the "\n", then recompile with gcc -o hello hello.c and execute ./hello. Just look at what chaos and havoc you have wrought.
Last but not least, there is a semicolon after the call to printf. In C, all statements end with a semicolon. Get used to knowing exactly where to find it on your keyboard. It's how the compiler knows the statement is complete. With more practice, the distinction between what is a statement and what is not will become more clear.
Line 1
#include <stdio.h>
Now you know a little bit about how the printf function works. As you can probably imagine, this will be quite useful in the coming years of your life as a 1337 h4x0r. But let's take a look at all of that other stuff in the code. After all, there are 5 lines, not just 1. We didn't type it all because it was fun. In line 1, #include is a compiler directive. This tells the compiler to do something before it begins compiling the the rest of the code. This part of the compilation process is often called the preprocessor. Line 1 of our program tells the compiler to use the specified file name, <stdio.h>, and to continue compilation as if the entire contents of that file were directly copied and pasted into the original source code. The file stdio.h also contains source code: it is primarily a long list of function declarations, one of which is our good friend, printf. We'll talk more about function declarations in the coming lessons, but for now, just know that if you write printf somewhere in your code without #include <stdio.h>, the compiler isn't sure what the hell you're talking about.
In fact, let's try it: remove line 1 from hello.c, and recompile. You should see something like the following:
hello.c: In function ‘main’:
hello.c:2: warning: incompatible implicit declaration of built-in function ‘printf’
This is a compiler warning. The compiler still made you an executable file, but it's trying to tell you that there's something you should really look into. In this particular case, it's telling you that you're trying to use printf without declaring it first (more on this in the coming lessons). Let's go back and re-add the line you just took out, and recompile. Now everything is peachy. In fact, let's go one step further. Recompile with this command: gcc -Wall -o hello hello.c. You should see no output. This tells you that everything is super peachy, as the -Wall flag tells gcc to give you every possible warning in your code. From now on, we will always compile using the -Wall flag and your goal will be to produce code that gives you absolutely no warnings.
Line 2
int main() {
In technical terms, this is a function definition for the main function. For now, all you need to know is that this is the entry-point into our program. This tells your operating system where to start when you run your program. All of your C programs will have a function definition for main. With the following we're getting a little bit ahead of ourselves, but this function definition tells us that main takes no arguments and returns an int.
Line 3
We've seen it, unless you're not paying attention.
Line 4
return 0;
This is a return statement. Again, more on this in the coming lessons, but in the context of whatever function you're defining, this specifies what output value the function will produce, if any. Here, we're returning an output value to the operating system. Specifically, a return value of 0 from main tells the operating system, "Everything went well. We're all done here."
Line 5
}
This closes the function definition of main. Without it, our program would contain a syntax error. Basically, this means that there is a problem with our source code that prevents the compiler from correctly reading it. If your code contains a syntax error, the compiler will not give you an executable file. Let's try it and see what happens. But first, let's get rid of your current hello executable. Type rm hello. Just to make sure it's gone, use the ls command if you're comfortable with that (you're going to have to learn these UNIX commands at some point), or simply try to execute ./hello. You should see something like "No such file or directory". Now, edit your hello.c file, and remove line 5. Recompile (gcc -Wall -o hello hello.c), and you should see this:
hello.c: In function ‘main’:
hello.c:4: error: expected declaration or statement at end of input
This is a compiler error. As you might have guessed, these can be fairly cryptic. Learning to understand these will eventually become second-nature. For now, this one is basically the compiler saying, "Aww turds. I got all the way to the end of the input, and something isn't right. Bail." Now type ls hello.
ls: hello: No such file or directory
Whoops. There's no executable file. This means we really have to fix the source code. Go back, replace the "}", and recompile. Once again, everything is right with the world, and typing ls hello in our console shows us this:
hello
This means we once again have an executable file. Yay!
Until Next Time
That's all for now! Until the next lesson, try playing around with hello.c. Add some newline characters to the string passed to printf. Add more calls to printf. Remove the semicolon after the call to printf to see what happens when you forget one. Remove the return statement. Try all sorts of crazy crap. Next time, we're actually going to do something cool, so get some practice time in with your text editor and gcc!