: An attempt to make computer machines run better

home | better linux | games | software | tutorials | reference | web log |

Tutorials: index | C | risc-v 32 assembly | risc-v 64 assembly | webassembly | x86_64 assembly | C 1 (old) | C 2 (old) | C 3 (old) | C 4 (old) | low-level graphics |


C Tutorial

The things you should learn

Tutorial 2: Solving problems

 My first tutorial was fairly dry and focused mostly on the compilation process without providing much information about how to actually program. This tutorial will fill that gap.

Where do we start?
 You should have a compiler installed on your computer and know how to use it. If you do not, then I recommend GCC (or MinGW if using Windows), which is free and can be run from the command line (which is preferable for this tutorial). Clang/LLVM is also another option, it is also free. MSVC can also be invoked from the command line, but I prefer GCC or Clang instead.

Can we start writing code?
 Ok. C code is stored in a plain text file. To illustrate this, I recommend that you start out with just a plain text editor (avoid IDE's) at this point. If you are using Windows, then notepad is always available. If you are using Linux, Then you might have a text editor such as Leafpad or gEdit available, or if you are familiar with the Linux command line, you could also use nano, vi, or sandy.
 Start by just creating a blank file. Save it in some directory on your computer. However, before you actually save it, you should remember that if you are on Windows, you are probably used to text files using the extension ".txt", in this case, we do not want to use ".txt", instead, we want to save our source code file with the extension ".c". So if you name your file something like "tut_2", then your file's full file name should be "tut_2.c" and not "tut_2.txt". The reason for this is not because ".c" is a magic extension, it's essentially just used to help us differentiate between a text file that contains text and a text file that contains c code. If you wanted to, it is possible to use the ".txt" extension instead.

 Before we start writing a program, we should know what exactly we are trying to accomplish. Usually, the first program you write just prints out the string "Hello World", but we already covered that in Tutorial 1. So, instead of that, we are going to do something better. Let's write a program that generates a random number and asks the user to guess what it is, giving hints if the user guesses wrong.
 While this is more fun than "Hello World", you may still be disappointed that we will still be using the command line for this one. Don't worry though, you will be doing graphical programming soon enough.
This exercise will teach many of the basic C concepts you need to know for larger projects.

Getting started and defining functions
 Start out by defining your main function. We won't write any include directives yet since we haven't actually decided what functions we will be using so we don't know what header functions define those functions. Although, we could guess that we will probably be using the common, standard ones, for this exercise, let's not make any assumptions just yet.
In C, all function definitions follow the same form. Your main function is no exception, it is just a normal function, just like any other, it is NOT a magic function, it just happens to be the function that libc calls once it has everything set up.
 Function definitions begin with the return type. For main, it should be int (integer), because that is what libc expects it to return. For other functions, it can be almost anything you want. We will talk more about limitations later. After the return type comes some whitespace (space, tab, or new-line) which functions as a sort of separator, and then the name of the function (main). Finally, you have to specify what arguments will be passed to the function. This is accomplished with a comma separated list of variable types and variable names, all within parenthesis. The main function will have two arguments passed to it, the first is an int, and the second is a 2D array of characters. So your code defining main should look like this:
int main(int argc, char **argv)
 You can see in this example that the first argument is an integer and I have chosen the name "argc" (argument count) for the variable. The second argument is named "argv", and is of the type char** (2D array of characters). You can call your variables whatever you want, but these are the names that are usually used for the arguments to main.

Generating a Random Number
 As you may have guessed, this program will need to generate a random number. This is actually not as easy as it might sound. Computers can not just make up a number, they need to have some algorithm to follow to get a number. So usually instead of random numbers, computers use "psuedo-random numbers", which are not really random numbers, but to a human being they seem to be random.
 There are many classes of psuedo-random number generators, some are trivial, some are really hard to determine what number will be next (cryptographic psuedo-random number generators), some try to be fast, some try to be slow, some try to be even, most try to do more than one thing. In our case, we don't need anything fancy, just something that looks random to the user. So we can use the random number generator that is part of the C standard. This random number generator is part of the standard C library, and defined in the header file, "stdlib.h".
Now that we know this, we can start writing some code. If you can remember in the last tutorial we learned about the C preprocessor "#include" directive. We can use the #include directive to include the contents of stdlib.h into our source. This will allow the C compiler to understand our calls to the random number generator functions. This line should look like this:
 #include <stdlib.h>
 The function to actually retrieve a random number is defined as:
int rand()
This might look odd to you if you understand function definitions but not how the random generator works. The definition shows that it returns and integer, but takes zero arguments. So, without passing any arguments, how can we tell the random generator what range of numbers we want? The answer to this question will be covered later in this tutorial.

Integer Math, Constants, Types, and Storage
 Now if we ask the user to guess a random number without any range, it will take the user quite a long time. So we need to define some kind of range. For this example, we will use only numbers between 0 and 100.
 We already mentioned that the rand() function doesn't allow us to specify a range when we call it, so how can we get a number within the range we want?
 There are actually a few methods to accomplish this. The first and easiest method involves something you probably learned in elementary school and haven't used since: Integer Math. If you don't know what I mean, let's look at an example: 3 / 2 = 1.5, right? This is true only sometimes, it depends on what kind of numbers the 3 and the 2 are. If they are both integers, then C will do integer division, and the result will be 1 instead of 1.5. If that doesn't make sense to you, remember back to elementary school, before you learned fractions, when you first learned about division. Back then, if you divided 3 into 2, your answer would have been 1, with a remainder of 1. This is integer division.
 Now that we understand integer division, consider this: in the equation x/y (assuming integer division), what is the maximum possible remainder? What is the minimum possible remainder?
 In case you haven't figured it out, the maximum possible remainder would be y-1 and the minimum possible remainder would be 0, or in other words, the range is from 0 to y-1. This is exactly what we want to do, and this is a very common method to get rand() into the range that we want.
 So, in order to implement this in our code, we need to know how to get a remainder. To do this, we use the "modulo" operation, using the modulo operator, "%". For example, to get the remainder of 3 / 2, we would write 3%2 in our code.
 Using this knowledge, generating a random between 0 and 100 is easy, all we need to do is generate a random number and take the remainder of the result over 100. This can be done in one line:
 This works for our purpose, but it does have some issues. First of all, using this method it becomes more likely that the result will be a smaller number in most cases. However, if the random generator can generate big enough randoms before the modulo operation, then the increased likelihood of smaller numbers is very slight. However, in some cases this is still unacceptable since it reduces randomness. To compensate for this, we can use another method to transform our random number into a random number within a specific range.
 This second method uses regular floating-point division. It also requires us to know the actual range of the rand() function before we transform the result into the range we want. I don't know what the exact number is, and it is possible for different systems to use a different range, so it is impossible to know on all systems. However, the header defines what is called a "constant", which is like a variable, except it is handled by the C preprocessor instead of the C compiler. As a result of this, constants can not be changed after the application is compiled, they are constant. Constants usually use a name that is written in all capital letters so that programmers can differentiate between constants and variables more easily. The constant that tells us the range of rand() is called RAND_MAX.
 It is important to remember that constants are handled by the preprocessor and not the compiler, a constant is not a variable. Before compilation, the preprocessor will read your code, find the constants and actually replace those constants with the number they represent. So if RAND_MAX is defined as 255, then trying to change RAND_MAX to equal 100 would be the same as writing 255=100, which simply doesn't make sense.
 Now that we know the range of rand() is 0 to RAND_MAX, we can convert the range into the range that we want. If we use floating point math, we can divide the generated random by RAND_MAX, which will result in a decimal number between 0 and 1. After that, all we need to do it multiply by 100 to get a number between 0 and 100.
 However, we still didn't learn how to actually do floating-point math. There is no special way to do floating point math in C, the compiler will just automatically do floating-point math whenever at least one of the variables is a floating-point number. rand() returns an integer, and RAND_MAX is an integer as well, so rand()/RAND_MAX will always equal 0 or 1, never anything in between. In order to do floating-point division, we need to convert one of these integers into a floating-point number. One easy way to do this is to create a new floating-point variable and store the result of rand() in that variable before dividing by RAND_MAX. We learned how to declare integer variables already, using "int". To declare a floating-point variable, we can use "float" instead. So declare a variable called r as a floating-point variable as follows:
float r = rand();
 Then, r can be divided by RAND_MAX, and the compiler will do floating- point division instead since r is a floating-point variable and the result will be between 0 and 1. It is important that we also store this value in a floating- point variable as well, otherwise the result will be automatically rounded to an integer. However, once we have multiplied by 100, we probably want to round to an integer again, it would be very difficult for the user to guess a floating- point number between 0 and 100 exactly. We can do this by creating another integer variable and storing the result there.
A full algorithm to convert the range of rand() into 0 to 100 would look like this:
 float r = rand();
 r = r / RAND_MAX;
 r = r * 100;
 int result = r;
This can be done in one line without intermediate storage, but it would need to use explicit casts, which we haven't learned yet.
It should also be noted that there is a slight difference between the two methods described. The first method will yeild a number between 0 and 99 inclusive, while the second method could generate a 100 (but its a 1/101 chance).

Getting Input
 We have generated a random number, now we need the user to guess what it might be.
  So we will need to get input from the user somehow. There are many ways of doing this, but on the command line it is fairly easy. If you remember last lesson we learned about printf(), we can use a similar function to get input, called scanf().
 scanf() works like printf() in that it takes a format argument and a list of variables for each marker in the format. However, scanf has some differences. For instance, since scanf will be saving data to the variables, we cannot give variables to scanf(), we need to give scanf() pointers to the variables instead. On top of that, scanf() also accepts a few formats that printf() doesn't, which will be useful later for reading long strings, but for now we only need to read an integer.
 Let's look at and example. We want to read an integer from the command line and store it in a variable. We can give this variable some meaningful name, such as input. Command line input is line-buffered, which is really annoying in my opinion, but it does occasionally make things easier. What this means for us is that we will not recieve anything until the user presses enter. So our scanf() call could look like this:
 int input;
Notice that I passed &input instead of just input. That is because &input is the pointer to input, meaning that &input is the memory address of input. If we just passed input then scanf() would know the current value of input, but not where to store the value that it retrieved from the user.

Conditional Statements
 Next, we need to test if the user entered the correct number, and if not, if the user's guess was too high or too low. To do this, C has what is called an if statement. The if statement basically allows you to do something in some cases, and something different or nothing in other cases. For instance, we can tell the user that the answer is wrong if the input is not equal to the random number, and if it is equal, than we can tell the user that it is right.
 However, this alone is going to make for an almost impossible game. We need to allow the user to guess again if the answer is wrong, and continue guessing until the correct answer is found. To do this, we need to use some kind of loop. In C (and most languages), loops are like the if statement, except instead of doing something in one case and something else in another, they do something again and again and again until the condition changes. For instance, we can keep asking for more guesses until the user finds the right answer.
 There are various types of loops in C, three common types are the while loop, the do-while loop, and the for loop. Of these, I personally use the for loop most, and I can't think of a time I ever used a do-while loop for real-world work. However, the first loop we should learn about is the while loop, it is simple and understanding it will make understanding the other types of loops easier.
 Think about how you would use "while" as a command in English, for instance, "While it is hot, wear a coat". If you were to listen to that advise, you would continually wear a coat if it is hot out, and when it becomes cold, you will stop wearing a coat. This is exactly how the while loop works. If the condition is true (it is hot) then you will do the action (wear a coat) continually until the condition (it is hot) becomes false (it is not hot). In our case, we want to say something like "While the answer is different than the generated number, guess again".
 Both conditional statements and loops require a "condition" that must be either true or false. In C, both true and false have a value, false being 0 and true being anything besides 0, usually 1. This concept is very important to understand. In human languages, true and false are concepts and not numbers, but in C, true and false are numbers. This means that mathematical expressions will either be true or false, which is exactly what your conditions will be in your loops and conditional statments: expressions. In fact, the correct term to use for the condition is "expression".
 This can be a difficult concept to understand at first. In a human language, you would say "if x is equal to 10", while in C you would say "if (x==10)", these look nearly the same, but are processed quite differently. You may notice that in C, we will use == instead of just = when we are writing expressions. That is because in C, == is a mathematical operator, just like + or - or *. Also, remember that we are talking about == and not =. If I were to ask you what is 3+2, you could easily answer assuming you possess even elementary math skills, but in C, I could just as easily ask what is 3==2? In human language this is a strange question, but to a computer, it is just as natural. I have even given you enough information to solve it already. Since I already told you that false is equal to 0 and true is equal to 1, and 3 is not equal to 2, then you can figure out that 3==2 is equal to 0.
 Now, of course, it would be silly to actually write "3==2" in your code, since we know that 3==2 will always be false, instead your expressions will likely use variables instead. For instance, i==8 will be 1 if i is 8 and 0 if i is anything else. Now if we made a mistake and accidentally wrote "i=8", then no matter what i was set to before, it would now be equal to 8, which is not what we wanted to do.
 In addition to the == operator, there are also inequality operators, such as < and >.
 Now that we know how to use expressions, let's learn how to actually write conditional statements. The if statement is written in the following form:
 if (expression)
Where expression is the expression to be evaluated. The parenthesis around the expression are required, however, this makes the if statement look somewhat like a function, do not get confused, if is not a function, it is a statement. The space between the word "if" and the parenthesis is not strictly required, but I always add that to differentiate statements from functions. Also notice the usage of curly brackets. In the last tutorial we saw how curly brackets mark the beginning and end of a function, but they have other uses too. More generally, curly brackets mark a block of code. They act sort of like a partition. In this case, the curly brackets indicate what code will be executed if the expression evaluates to true. If the expression evaluates to false, then the entire block of code will be skipped entirely.
 You may also have noticed that I did not place a semi-colon (;) after the if statement. That is not a mistake. This is very important. The if statement does not actually require curly brackets to be placed after it, however, if are no curly brackets, the if statement will execute only the next line of code if the expression is true, and skip only one line if it is false. If you accidentally place a semi-colon after the if statement, you are ending that line of code. That means if the expression is true, it will do nothing and then continue, if the expression is false, it will skip nothing and continue. This means that your if statement will essentially have no effect. So make sure you are very careful about writing if statements.
 while loops are written just like if statements, except with the word, "while" instead of "if". while works almost exactly like if, except that if the expression evaluates to true, then the code block is executed and the it starts over, evaluating the expression again. A while loop takes the following form:
 while (expression)

Inequalities and Comparison
 Remember that I told you == is an operator that evaluates to true (1) if both sides are equal and false (0) if they are not? Remember that I also told you that there are other operators as well for inequalities? For instance, > would evaluate to true if the left side is greater than the right side, and 0 if the right side is greater or if they are equal. There is another important operator which is !. The ! operator in C means "not". If you are familiar at all with binary logic, then you can easily understand !. If not, I will explain. What ! does is convert any true (1) values to false (0) and false to true. So, !0 equals 1, and !1 equals 0.
 In addition to the ! (but related), there is another operated called != which means "not equal". This operator evaluates to true if both sides are different and false if they are equal. It is the exact opposite of ==.
 These might seem silly, but if you consider the loop we will need to write for our program, consider the condition which will cause the guessing to continue. We want to continue guessing (restart the loop) while the user has not yet guessed correctly. e.g. while (guess!=random).

Putting it all together
 Now finally if you made it here, you have enough knowledge to complete the entire program. If you feel up to it, try to write it yourself without looking at the completed code below. Otherwise, look at the code below and take note of how it utilizes the concepts that we learned in this tutorial.

Completed Program Source
#include >stdio.h<
#include >stdlib.h<

int main(int argc,char **argv)
 int guess;
 int random;

 random = rand() % 100;
 scanf("%d", &guess);
 while (guess != random)
  if (guess > random)
   printf("Too high!\n");
  if (guess < random)
   printf("Too low!\n");
  scanf("%d", &guess);

 return 0;

 Now let's do a quick quiz to see how much you remember.
Answer the following questions:

1. What is 14/3?
2. What type of file is used to store C code?
3. What does a ; do?
4. What is "14 == 14"?
5. What defines a "block" of code?
6. How are constants processed?
7. What is "15 < 50"
8. What does % mean and what does it do?
9. What is 9/2?
10. What is "=" used for?

If you compiled and ran the program we wrote in this tutorial, you may have noticed that it is far from perfect. Not to worry though, next time we will work on fixing it up while learning more concepts!