© German Gonzalez-Morris and Ivor Horton 2020
G. Gonzalez-Morris, I. HortonBeginning Chttps://doi.org/10.1007/978-1-4842-5976-4_2

2. First Steps in Programming

German Gonzalez-Morris1  and Ivor Horton2
(1)
Santiago, Chile
(2)
STRATFORD UPON AVON, UK
 

By now you’re probably eager to create programs that allow your computer to really interact with the outside world. You don’t just want programs that work as glorified typewriters, displaying fixed information that you included in the program code, and indeed there’s a whole world of programming that goes beyond that. Ideally, you want to be able to enter data from the keyboard and have the program squirrel it away somewhere. This would make the program much more versatile. Your pcrogram would be able to access and manipulate these data, and it would be able to work with different data values each time you execute it. This idea of entering different information each time you run a program is what makes programming useful. A place to store an item of data that can vary in a program is not altogether surprisingly called a variable, and this is what this chapter covers.

In this chapter, you’ll learn
  • How memory is used and what variables are

  • How you calculate in C

  • What different types of variables there are and what you use them for

  • What casting is and when you need to use it

  • How to write a program that calculates the height of a tree—any tree

Memory in Your Computer

I’ll explain first how the computer stores the data that’s processed in your program. To understand this, you need to know a little bit about memory in your computer, so before you start your first program, let’s take a quick tour of your computer’s memory.

The instructions that make up your program, and the data that it acts upon, have to be stored somewhere that’s instantly accessible while your computer is executing that program. When your program is running, the program instructions and data are stored in the main memory or the random access memory (RAM) of the machine. RAM is volatile storage. When you switch off your PC, the contents of RAM are lost. Your PC has permanent storage in the form of one or more disk drives (or solid-state drive, SSD). Anything you want to keep when a program finishes executing needs to be printed or written to disk, because when the program ends, the results stored in RAM will be lost.

You can think of RAM as an ordered sequence of boxes. Each of these boxes is in one of two states: either the box is full when it represents 1 or the box is empty when it represents 0. Therefore, each box represents one binary digit, either 0 or 1. The computer sometimes thinks of these in terms of true and false: 1 is true and 0 is false. Each of these boxes is called a bit, which is a contraction of binary digit.

Note

If you can’t remember or have never learned about binary numbers and you want to find out a little bit more, there is more detail in Appendix A. However, you needn’t worry about these details if they don’t appeal to you. The important point here is that the computer can only deal with 1s and 0s—it can’t deal with decimal numbers directly. All the data that your program works with, including the program instructions themselves, will consist of binary numbers inside your PC.

For convenience, the bits in memory are grouped into sets of eight, and each set of eight bits is called a byte . To allow you to refer to the contents of a particular byte, each byte has been labeled with a number, starting from 0 for the first byte, 1 for the second byte, and so on, up to whatever number of bytes you have in your computer’s memory. This label for a byte is called its address. Thus, the address of each byte is unique. Just as a street address identifies a particular house, the address of a byte uniquely references that byte in your computer’s memory.

To summarize, memory consists of a large number of bits that are in groups of eight (called bytes), and each byte has a unique address. Byte addresses start from 0. A bit can only be either 1 or 0. This is illustrated in Figure 2-1.
../images/311070_6_En_2_Chapter/311070_6_En_2_Fig1_HTML.png
Figure 2-1.

Bytes in memory

The amount of memory your computer has is expressed in terms of so many kilobytes, megabytes, or gigabytes and, in the case of large disk drives/SSD, terabytes. Here’s what those words mean:
  • 1 kilobyte (or 1 KB) is 1,024 bytes.

  • 1 megabyte (or 1 MB) is 1,024 kilobytes, which is 1,048,576 bytes.

  • 1 gigabyte (or 1 GB) is 1,024 megabytes, which is 1,073,741,824 bytes.

  • 1 terabyte (or 1 TB) is 1,024 gigabytes, which is 1,099,511,627,776 bytes.

If you have a gigabyte of RAM in your PC, byte addresses will be from 0 to 1,073,741,823 inclusive. You might be wondering why we don’t work with simpler, more rounded numbers, such as a thousand or a million or a billion. The reason is this: there are 1,024 numbers from 0 to 1,023, and 1,023 happens to be 10 bits that are all 1 in binary—11 1111 1111, which is a very convenient binary value. So while 1,000 is a very convenient decimal value, it’s actually rather inconvenient in a binary machine—it’s 11 1110 1000, which is not exactly neat and tidy. The kilobyte (1,024 bytes) is therefore defined in a manner that’s convenient for your computer, rather than for you. Similarly, for a megabyte, you need 20 bits, and for a gigabyte, you need 30 bits.

Confusion can arise with hard disk drive (HDD) or solid-state drive (SSD) capacities. Disk drive manufacturers often refer to a HDD/SSD as having a capacity of 256 gigabytes or 1 terabyte, when they really mean 256 billion bytes and 1 trillion bytes. Of course, 256 billion bytes is only 231 gigabytes, and 1 trillion bytes is only 911 gigabytes, so a manufacturer’s specification of the capacity of a HDD/SSD looks more impressive than it really is. Now that you know a bit about bytes, let’s see how you use memory in your programs to store data.

It is essential to understand how the C memory is structured. This is mainly made by heap, stack, global constant, and code. However, the most important are heap and stack. They belong to RAM (memory); therefore, they’re volatile and frequently change at runtime.

Local variables of a function are created in the stack; meanwhile, the heap is where pointers are manually handled by your program (i.e., malloc/free). The scope of stack is the function that is being executed.

The stack is designed to have smaller variables than heap; the philosophy is speed for volatile variables.

On the other hand, the heap is to handle larger variables (dynamically allocated space); of course, there is a speed trade-off accessing objects from each segment. They are manually handled by the developer (implicitly, it may occur a memory leak). The variables in the heap are global and can be accessed through pointers (which will be seen in later chapters).

The stack is a LIFO (last-in-first-out) structure; this is very useful for recursive function (which we’ll see later in the next chapter). Every time a variable is declared, it will exist on the top of that segment (yes, it is a stack-LIFO structure, using push-pop functions).
../images/311070_6_En_2_Chapter/311070_6_En_2_Fig2_HTML.png
Figure 2-1a.

Memory representation

What Is a Variable?

A variable in a program is a specific piece of memory that consists of one or more contiguous bytes, typically 1, 2, 4, 8, or 16 bytes. Every variable in a program has a name, which will correspond to the memory address for the variable. You use the variable name to store a data value in memory or retrieve the data that the memory contains.

Let’s start with a program that outputs your salary. It will use the printf() function we learned about in Chapter 1. Assuming your salary is $10,000 per month, you can already write that program very easily:
// Program 2.1 What is a Variable?
#include <stdio.h>
int main(void)
{
  printf("My salary is $10000");
  return 0;
}

I’m sure you don’t need any more explanation about how this works; it’s almost identical to the programs you saw in Chapter 1. So how can we modify this program to allow you to customize the message depending on a value stored in memory? There are several other ways of doing this. A more useful approach uses a variable.

You could allocate a piece of memory that you could name salary, say, and store the value 10000 in it. When you want to display your salary, you could use the variable name, salary, and the value that’s stored in it (10000) would be output. Wherever you use a variable name in a program, the computer accesses the value that’s stored there. You can access a variable however many times you need to in your program. When your salary changes, you can simply change the value stored in the variable salary, and the program will work with the new value. Of course, all the values will be stored in salary as binary numbers.

You can have as many variables as you like in a program. The value that each variable contains, at any point during the execution of that program, is determined by the instructions contained in your program. The value of a variable isn’t fixed, and you can change it whenever you need to throughout a program.

Note

I said that a variable can be one or more bytes, so you may be wondering how the computer knows how many bytes it is. You’ll see later in the next section that every variable has a type that specifies the kind of data the variable can store. The type of a variable determines how many bytes are allocated for it.

Naming Variables

The name you give to a variable, conveniently referred to as a variable name , can be defined with some flexibility. A variable name is a sequence of one or more uppercase or lowercase letters, digits, and underscore characters (_) that begin with a letter (incidentally, the underscore character counts as a letter). Examples of legal variable names are as follows:
Radius  diameter  Auntie_May  Knotted_Wool  D678

A variable name must not begin with a digit, so 8_Ball and 6_pack aren’t legal names. A variable name must not include characters other than letters, underscores, and digits, so Hash! and Mary-Lou aren’t allowed as names. This last example is a common mistake, but Mary_Lou would be quite acceptable. Because spaces aren’t allowed in a name, Mary Lou would be interpreted as two variable names, Mary and Lou. Variables starting with one or two underscore characters are often used in the header files, so don’t use the underscore as the first character in your variable names; otherwise, you run the risk of your name clashing with the name of a variable used in the standard library. For example, names such as _this and _that are best avoided. Another very important point to remember about variable names is that they are case sensitive. Thus, the names Democrat and democrat are distinct.

Although you can call variables whatever you want within the preceding constraints, it’s worth calling them something that gives you a clue as to what they contain. Assigning the name x to a variable that stores a salary isn’t very helpful. It would be far better to call it salary and leave no doubt as to what it is. In the end, the variable's name must be a clear representation of its meaning.

Caution

The maximum number of characters that you can have in a variable name will depend on your compiler. A minimum of 31 characters must be supported by a compiler that conforms to the C standard, so you can always use names up to this length without any problems. I suggest that you don’t make your variable names longer than this anyway. Very long names become tedious to type and make the code hard to follow. Some compilers will truncate names that are too long.

Variables That Store Integers

There are several different types of variables, and each type of variable is used for storing a particular kind of data. There are several types that store integers, types that store nonintegral numerical values, and types that store characters. Where there are several types to store a particular kind of data, such as integers, the difference between the types is in the amount of memory they occupy and the range of values they can hold. I will introduce variables that you use to store integers first.

An integer is any whole number without a decimal point. Here are some examples:
123     10,999,000,000     20,000     88     1

You will recognize these values as integers, but what I’ve written here isn’t quite correct so far as your program is concerned. You can’t include commas in an integer, so the second value would actually be written in a program as 10999000000 and the third value would be 20000.

Here are some examples of numbers that are not integers:
1.234     999.9     2.0     –0.0005     3.14159265

Normally, 2.0 would be described as an integer because it’s a whole number, but as far as your computer is concerned, it isn’t because it is written with a decimal point. If you want an integer, you must write it as 2 with no decimal point. Integers are always written in C without a decimal point; if there’s a decimal point, it isn’t an integer—it’s a floating-point value, which I’ll get to later. Before I discuss integer variables in more detail (and believe me, there’s a lot more detail!), let’s look at a simple variable in action in a program, just so you can get a feel for how they’re used.

Try it Out: Using a Variable
Let’s return to a program to output your salary. You can rewrite the previous program to use a variable of type int, which is an integer type:
//  Program 2.2 Using a variable
#include <stdio.h>
int main(void)
{
  int salary;                               // Declare a variable called salary
  salary = 10000;                           // Store 10000 in salary
  printf("My salary is %d. ", salary);
  return 0;
}
Type in this example and compile, link, and execute it. You’ll get the following output:
My salary is 10000.

How It Works

The first three lines are essentially the same as in all the previous programs. Let’s look at the new stuff. The statement that identifies the memory that you’re using to store your salary is the following:
  int salary;                     // Declare a variable called salary

This statement is called a variable declaration because it declares the name of the variable. The name, in this case, is salary.

Caution Notice that the variable declaration ends with a semicolon. If you omit the semicolon, your program will generate an error when you try to compile it.

The variable declaration also specifies the type of data that the variable will store. You’ve used the keyword int to specify that the variable, salary, will be used to store an integer value of type int. The keyword int precedes the name of the variable. This is just one of several different types you can use to store integers.

Note Remember, keywords are words that are reserved in C because they have a special meaning. You must not use keywords as names for variables or other entities in your code. If you do, your compiler will produce error messages.

The declaration for the variable, salary, is also a definition because it causes some memory to be allocated to hold an integer value, which you can access using the name salary.

Note A declaration introduces a variable name, and a definition causes memory to be allocated for it. The reason for this distinction will become apparent later in the book.

Of course, you have not specified what the value of salary should be yet, so at this point it will contain a junk value—whatever was left behind from when this bit of memory was used last.

The next statement is
  salary = 10000;                 // Store 10000 in salary

This is a simple arithmetic assignment statement. It takes the value to the right of the equal sign and stores it in the variable on the left of the equal sign. Here you’re declaring that the variable salary will have the value 10000. You’re storing the value on the right (10000) in the variable on the left (salary). The = symbol is called the assignment operator because it assigns the value on the right to the variable on the left.

You then have the familiar printf() statement , but it’s a little different from how you’ve seen it in action before:
  printf("My salary is %d. ", salary);
There are now two arguments inside the parentheses, separated by a comma. An argument is a value that’s passed to a function. In this program statement, the two arguments to the printf() function are as follows:
  • The first argument is a control string , so called because it controls how the output specified by the following argument or arguments is to be presented. This is the character string between the double quotes. It is also referred to as a format string because it specifies the format of the data that are output.

  • The second argument is the name of the variable, salary. The control string in the first argument determines how the value of salary will be displayed.

The control string is fairly similar to the previous example, in that it contains some text to be displayed. However, if you look carefully, you’ll see %d embedded in it. This is called a conversion specifier for the value of the variable. Conversion specifiers determine how variable values are displayed on the screen. In other words, they specify the form to which an original binary value is to be converted before it is displayed. In this case, you’ve used a d, which is a decimal specifier that applies to integer values. It just means that the second argument, salary, will be represented and output as a decimal (base 10) number.

Note

Conversion specifiers always start with a % character so that the printf() function can recognize them. Because a % in a control string always indicates the start of a conversion specifier, you must use the escape sequence %% when you want to output a % character.

Try It Out: Using More Variables
Let’s try a slightly longer example:
// Program 2.3 Using more variables
#include <stdio.h>
int main(void)
{
  int brothers;                             // Declare a variable called brothers
  int brides;                               // and a variable called brides
  brothers = 7;                             // Store 7 in the variable brothers
  brides = 7;                               // Store 7 in the variable brides
  // Display some output
  printf("%d brides for %d brothers ", brides, brothers);
  return 0;
}
If you run this program, you should get the following output:
7 brides for 7 brothers

How It Works

This program works in a similar way to the previous example. You first declare two variables, brides and brothers, with these statements:
  int brothers;                             // Declare a variable called brothers
  int brides;                               // and a variable called brides
You specify both variables as type int so they both can only store integer values. Notice that they have been declared in separate statements. Because they are both of the same type, you could have saved a line of code and declared them together like this:
int brothers, brides;
When you declare several variables of a given type in one statement, the variable names following the data type are separated by commas, and the whole line ends with a semicolon. This can be a convenient format, but the downside is that it isn’t so obvious what each variable is for; because if they appear on a single line, you can’t add individual comments to describe each variable so easily. Always comment on the code to have more precise details of what the code is supposed to do. However, you could write this single statement spread over two lines:
  int brothers,                             // Declare a variable called brothers
      brides;                               // and a variable called brides

By spreading the statement out over two lines, you’re able to put the comments back in. Comments are ignored by the compiler so it’s the exact equivalent of the original statement without the comments. You can spread C statements over as many lines as you want. The semicolon determines the end of the statement, not the end of the line.

Of course, you might as well write the preceding statement as two statements, and in general it is a better practice to define each variable in a separate statement. Variable declarations often appear at the beginning of the executable code for a function, but you are not obliged to do so. You typically put declarations for variables that you intend to use in a block of code that is between a pair of braces immediately following the opening brace, {.

The next two statements assign the same value, 7, to each variable:
  brothers = 7;                             // Store 7 in the variable brothers
  brides = 7;                               // Store 7 in the variable brides

Note that the statements that declared these variables precede these statements. If one or the other of the declarations were missing or appeared later in the code, the program wouldn’t compile. A variable does not exist in your code before its declaration. You must always declare a variable before you use it.

The next statement calls the printf() function . The first argument is a control string that will display a line of text. This string also contains specifications for how the values of subsequent arguments will be interpreted and displayed within the text. The %d conversion specifiers within the control string will be replaced by the values currently stored in the variables that appear as the second and third arguments to the printf() function call—in this case, brides and brothers:
  printf("%d brides for %d brothers ", brides, brothers);
The conversion specifier s are replaced in order by the values of the variables that appear as the second and subsequent arguments to the printf() function, so the value of brides corresponds to the first specifier, and the value of brothers corresponds to the second. This would be more obvious if you changed the statements that set the values of the variables as follows:
  brothers = 8;                             // Store 8 in the variable brothers
  brides = 4;                               // Store 4 in the variable brides
In this somewhat dubious scenario, the printf() statement would show clearly which variable corresponds to which conversion specifier, because the output would be the following:
4 brides for 8 brothers
You can demonstrate that variables are case sensitive by changing the printf() statement so that one of the variable names starts with a capital letter, as follows:
// Program 2.3A Using more variables
#include <stdio.h>
int main(void)
{
  int brothers;                             // Declare a variable called brothers
  int brides;                               // and a variable called brides
  brothers = 7;                             // Store 7 in the variable brothers
  brides = 7;                               // Store 7 in the variable brides
  // Display some output
  printf("%d brides for %d brothers ", Brides, brothers);
  return 0;
}

You’ll get an error message when you try to compile this. The compiler interprets the names brides and Brides as different, so it doesn’t understand what Brides refers to because you have not declared it. This is a common error. As I’ve said before, punctuation and spelling mistakes are the main causes of trivial errors. You must always declare a variable before you use it; otherwise, the compiler will not recognize it and will flag the statement as an error.

Using Variables

You now know how to name and declare your variables, but so far this hasn’t been much more useful than what you learned in Chapter 1. Let’s try another program in which you’ll use the values in the variables before you produce some output.

Try It Out: Doing a Simple Calculation
This program does a simple calculation using the values of the variables:
// Program 2.4 Simple calculations
#include <stdio.h>
int main(void)
{
  int total_pets;
  int cats;
  int dogs;
  int ponies;
  int others;
  // Set the number of each kind of pet
  cats = 2;
  dogs = 1;
  ponies = 1;
  others = 46;
  // Calculate the total number of pets
  total_pets = cats + dogs + ponies + others;
  printf("We have %d pets in total ", total_pets);   // Output the result
  return 0;
}
This example produces this output:
We have 50 pets in total

How It Works

As in the previous examples, all the statements between the braces are indented by the same amount. This makes it clear that all these statements belong together. You should always organize your programs the way you see here: indent a group of statements that lie between an opening and closing brace by the same amount. It makes your programs much easier to read.

You first define five variables of type int:
  int total_pets;
  int cats;
  int dogs;
  int ponies;
  int others;

Because these variables will be used to store a count of a number of animals, they are definitely going to be whole numbers. As you can see, they’re all declared as type int.

The variables are given specific values in these four assignment statements:
  cats = 2;
  dogs = 1;
  ponies = 1;
  others = 46;
At this point, the variable total_pets doesn’t have an explicit value set. It will get its value as a result of the calculation using the other variables:
  total_pets = cats + dogs + ponies + others;

In this arithmetic statement, you calculate the sum of all your pets on the right of the assignment operator by adding the values of each of the variables together. This total value is then stored in the variable total_pets that appears on the left of the assignment operator. The new value replaces any old value that was stored in the variable total_pets.

The printf() statement presents the result of the calculation by displaying the value of total_pets:
  printf("We have %d pets in total ", total_pets);

Try changing the numbers of some of the types of animals, or maybe add some more of your own. Remember to declare them, initialize their value, and include them in the total_pets statement.

Initializing Variables

In the previous example, you declared each variable with a statement such as this:
  int cats;                         // The number of cats as pets
You set the value of the variable cats using this statement:
  cats = 2;
This sets the value of cats to 2. So what was the value before this statement was executed? Well, it could be anything. The first statement creates the variable called cats , but its value will be whatever was left in memory from the last program that used this memory. The assignment statement that appeared later sets the value to 2, but it would be much better to initialize the variable when you declare it. You can do this with the following statement:
  int cats = 2;

This statement declares cats as type int and sets its initial value to 2. Initializing variables as you declare them is very good practice. It avoids any doubt about what the initial values are, and if the program doesn’t work as it should, it can help you track down the errors. Avoid leaving spurious values for variables when you create them, which reduces the chances of your computer crashing when things do go wrong. Inadvertently working with junk values can cause all kinds of problems. From now on, you’ll always initialize variables in the examples, even if it’s just to 0.

The previous program is the first one that really did something. It is very simple—just adding a few numbers—but it is a significant step forward. It is an elementary example of using an arithmetic statement to perform a calculation. Now let’s look at some more sophisticated calculations that you can do.

Basic Arithmetic Operations

An arithmetic statement is of the following form:
  variable_name = arithmetic_expression;

The arithmetic expression on the right of the = operator specifies a calculation using values stored in variables or explicit numbers that are combined using arithmetic operators such as addition (+), subtraction (–), multiplication (*), and division (/). There are also other operators you can use in an arithmetic expression, as you’ll see.

In the previous example, the arithmetic statement was the following:
  total_pets = cats + dogs + ponies + others;

The effect of this statement is to calculate the value of the arithmetic expression to the right of the = and store that value in the variable specified on the left.

The = symbol in C defines an action. It doesn’t specify that the two sides are equal, as it does in mathematics. It specifies that the value that results from evaluating the expression on the right is to be stored in the variable on the left. This means that you could have the following statement:
  total_pets = total_pets + 2;
This would be ridiculous as a mathematical equation, but in programming, it’s fine. Let’s look at it in context. Imagine you’d rewritten the last part of the program to include the preceding statement. Here’s a fragment of the program as it would appear with the statement added:
  total_pets = cats + dogs + ponies + others;
  total_pets = total_pets + 2;
  printf("The total number of pets is: %d", total_pets);

After executing the first statement here, total_pets will contain the value 50. Then, in the second line, you extract the value of total_pets, add 2 to that value, and store the results back in the variable total_pets. The final total that will be displayed is therefore 52.

Note

In an assignment operation, the expression on the right of the = sign is evaluated first, and the result is then stored in the variable on the left. The new value replaces the value that was previously contained in the variable to the left of the assignment operator. The variable on the left of the assignment is called an lvalue , because it is a location that can store a value. The value that results from executing the expression on the right of the assignment is called an rvalue because it is a value that results from evaluating the expression and is not an lvalue.

An arithmetic expression is any expression that results in a numerical value. The following are arithmetic expressions:
3      1 + 2      total_pets      cats + dogs - ponies     -data
Evaluating any of these expressions produces a single numeric value. Note that just a variable name is an expression that produces a value, which is the value that the variable contains. The last example has a value that is the negation of data, so if data contains -5, the value of the expression -data is 5. Of course, the value of data is still -5. In a moment, we’ll take a closer look at how an expression is made up, and we’ll look into the rules governing its evaluation. First, though, you can try some working examples using the basic arithmetic operators. Table 2-1 shows these operators.
Table 2-1.

Basic Arithmetic Operators

Operator

Action

+

Addition

-

Subtraction

*

Multiplication

/

Division

%

Modulus

The items of data that an operator applies to are generally referred to as operands . All these operators produce an integer result when both operands are integers. You may not have come across the modulus operator before. It calculates the remainder after dividing the value of the expression on the left of the operator by the value of the expression on the right. For this reason, it’s sometimes referred to as the remainder operator . The expression 12 % 5 produces 2, because 12 divided by 5 leaves a remainder of 2. We’ll look at this in more detail in the next section. All these operators work as you’d expect, with the exception of division, which is slightly nonintuitive when applied to integers, as you’ll see. Let’s try some more arithmetic operations.

Note

The values that an operator is applied to are called operands. An operator that requires two operands, such as %, is called a binary operator . An operator that applies to a single value is called a unary operator . Thus, - is a binary operator in the expression a - b and a unary operator in the expression -data.

Try It Out: Subtraction and Multiplication
Let’s look at a food-based program that demonstrates subtraction and multiplication:
// Program 2.5 Calculations with cookies
#include <stdio.h>
int main(void)
{
  int cookies = 5;
  int cookie_calories = 125;                // Calories per cookie
  int total_eaten = 0;                      // Total cookies eaten
  int eaten = 2;                            // Number to be eaten
  cookies = cookies - eaten;                // Subtract number eaten from cookies
  total_eaten = total_eaten + eaten;
  printf(" I have eaten %d cookies.  There are %d cookies left",
                                                             eaten, cookies);
  eaten = 3;                                // New value for cookies eaten
  cookies = cookies - eaten;                // Subtract number eaten from cookies
  total_eaten = total_eaten + eaten;
  printf(" I have eaten %d more.  Now there are %d cookies left ", eaten, cookies);
  printf(" Total energy consumed is %d calories. ", total_eaten*cookie_calories);
  return 0;
}
This program produces the following output:
I have eaten 2 cookies.  There are 3 cookies left
I have eaten 3 more.  Now there are 0 cookies left
Total energy consumed is 625 calories.

How It Works

You first declare and initialize three variables of type int:
  int cookies = 5;
  int cookie_calories = 125;                // Calories per cookie
  int total_eaten = 0;                      // Total cookies eaten

You’ll use the total_eaten variable to accumulate the total number of cookies eaten as the program progresses, so you initialize it to 0.

Next, you declare and initialize a variable that holds the number of cookies to be eaten:
  int eaten = 2;                            // Number to be eaten
You use the subtraction operator to subtract eaten from the value of cookies:
  cookies = cookies - eaten;                // Subtract number eaten from cookies

The result of the subtraction is stored back in the variable cookies, so the value of cookies will now be 3.

Because you’ve eaten some cookies, you increment the count of the total that you’ve eaten by the value of eaten:
  total_eaten = total_eaten + eaten;

You add the current value of eaten, which is 2, to the current value of total_eaten, which is 0. The result is stored back in the variable total_eaten.

The printf() statement displays the number of cookies that have been eaten and that are left:
  printf(" I have eaten %d cookies.  There are %d cookies left",
                                                                eaten, cookies);
I chose to put the part of the statement that follows the first comma on a new line. You can spread statements out like this to make them easier to read or fit within a given width on the screen. Note that you cannot split the string that is the first argument in this way. An explicit newline character isn’t allowed in the middle of a string. When you need to split a string over two or more lines, each segment of the string on a line must have its own pair of double quotes delimiting it. For example, you could write the previous statement as follows:
  printf(" I have eaten %d cookies. "
          " There are %d cookies left",
          eaten, cookies);

Where there are two or more strings immediately following one another like this, the compiler will join them to form a single string.

You display the values stored in eaten and cookies using the conversion specifier, %d, for integer values. The value of eaten will replace the first %d in the output string, and the value of cookies will replace the second. The string is displayed starting on a new line because of the at the beginning.

The next statement sets the variable eaten to a new value:
  eaten = 3;                                // New value for cookies to be eaten
The new value replaces the previous value stored in eaten, which was 2. You then go through the same sequence of operations as you did before:
  cookies = cookies - eaten;                // Subtract number eaten from cookies
  total_eaten = total_eaten + eaten;
  printf(" I have eaten %d more.  Now there are %d cookies left ", eaten, cookies);
Finally, before executing the return statement that ends the program, you calculate and output the number of calories corresponding to the number of cookies eaten:
  printf(" Total energy consumed is %d calories. ", total_eaten*cookie_calories);

Here the second argument to the printf() function is an arithmetic expression rather than just a variable. The compiler will arrange for the result of the expression total_eaten*cookie_calories to be stored in a temporary variable, and that value will be passed as the second argument to the printf() function. You can always use an expression for an argument to a function as long as it evaluates to a result of the required type.

Easy, isn’t it? Let’s take a look at an example using division and the modulus operator.

Try It Out: Division and the Modulus Operator
Suppose you have a jar of 45 cookies and a group of seven children. You’ll divide the cookies equally among the children and work out how many each child has. Then you’ll work out how many cookies are left over:
// Program 2.6 Cookies and kids
#include <stdio.h>
int main(void)
{
  int cookies = 45;                         // Number of cookies in the jar
  int children = 7;                         // Number of children
  int cookies_per_child = 0;                // Number of cookies per child
  int cookies_left_over = 0;                // Number of cookies left over
  // Calculate how many cookies each child gets when they are divided up
  cookies_per_child = cookies/children;     // Number of cookies per child
  printf("You have %d children and %d cookies ", children, cookies);
  printf("Give each child %d cookies. ", cookies_per_child);
  // Calculate how many cookies are left over
  cookies_left_over = cookies%children;
  printf("There are %d cookies left over. ", cookies_left_over);
  return 0;
}
When you run this program, you’ll get this output:
You have 7 children and 45 cookies
Give each child 6 cookies.
There are 3 cookies left over.

How It Works

I’ll go through this program step by step. Four integer variables, cookies, children, cookies_per_child, and cookies_left_over, are declared and initialized with the following statements:
  int cookies = 45;                         // Number of cookies in the jar
  int children = 7;                         // Number of children
  int cookies_per_child = 0;                // Number of cookies per child
  int cookies_left_over = 0;                // Number of cookies left over
The number of cookies is divided by the number of children by using the division operator / to produce the number of cookies given to each child:
  cookies_per_child = cookies/children;     // Number of cookies per child
The next two statements output what is happening, including the value stored in cookies_per_child:
  printf("You have %d children and %d cookies ", children, cookies);
  printf("Give each child %d cookies. ", cookies_per_child);
You can see from the output that cookies_per_child has the value 6. This is because the division operator always produces an integer result when the operands are integers. The result of dividing 45 by 7 is 6, with a remainder of 3. You calculate the remainder in the next statement by using the modulus operator:
  cookies_left_over = cookies%children;

The expression to the right of the assignment operator calculates the remainder that results when the value of cookies is divided by the value of children.

Finally, you output the remainder in the last statement:
  printf("There are %d cookies left over. ", cookies_left_over);

More on Division with Integers

Let’s look at the result of using the division and modulus operators where one of the operands is negative. With division, the result will always be negative if the operands have different signs. Thus, the expression –45/7 produces the same result as the expression 45/–7, which is –6. If the operands in a division are of the same sign, either positive or negative, the result is always positive. Thus, 45/7 produces the same result as –45/–7, which is 6.

With the modulus operator, the sign of the result is always the same as the sign of the left operand whether or not the operands have different signs. Thus, 45 % –7 results in the value 3, whereas –45 % 7 results in the value –3; the expression -45 % -7 also evaluates to -3.

Unary Operators

For example, the multiplication sign is a binary operator because it has two operands, and the effect is to multiply one operand value by the other. However, there are some operators that are unary, meaning that they only need one operand. I’ll present more examples later, but for now we’ll just take a look at the single most common unary operator.

Unary Minus Operator

The operators that we’ve dealt with so far have been binary operators that require two operands. There are also unary operators in C that apply to a single operand. The unary minus operator is one example. It produces a positive result when applied to a negative operand and a negative result when the operand is positive. You might not immediately realize when you would use this, but think about keeping track of your bank account. Say you have $200 in the bank. You record what happens to this money in a book with two columns, one for money that you pay out and another for money that you receive. One column is your expenditure, and the other is your revenue.

You decide to buy a CD for $50 and a book for $25. If all goes well, when you compare the initial value in the bank and subtract the expenditure ($75), you should end up with what’s left. Table 2-2 shows how these entries could typically be recorded.
Table 2-2.

Recording Revenues and Expenditures

Entry

Revenue

Expenditure

Bank balance

Check received

$200

 

$200

CD

 

$50

$150

Book

 

$25

$125

Closing balance

$200

$75

$125

If these numbers were stored in variables, you could enter both the revenue and expenditure as positive values, which would allow you to calculate the sum of each as positive totals. You then only make an expenditure value negative when you want to calculate how much is left in your account. You could do this by simply placing a minus sign (–) in front of the variable name.

To output the amount you had spent as a negative value, you could write the following:
int expenditure = 75;
printf("Your balance has changed by %d.", -expenditure);
This would result in the following output:
Your balance has changed by -75.

The minus sign will remind you that you’ve spent this money rather than gained it. Of course, the expression -expenditure doesn’t change the value stored in expenditure—it’s still 75. The value of the expression is –75.

The unary minus operator in the expression -expenditure specifies an action, the result of which is the value of expenditure with its sign inverted: negative becomes positive and positive becomes negative. This is subtly different from when you use the minus operator when you write a negative number such as –75 or –1.25. In this case, the minus doesn’t result in an action, and no instructions need to be executed when your program is running. The minus sign is part of the constant and determines that it is negative.

Variables and Memory

So far you’ve only looked at integer variables without considering how much space they take up in memory. Each time you declare a variable of a given type, the compiler allocates sufficient space in memory to store values of that particular type of variable. Every variable of a particular type will always occupy the same amount of memory—the same number of bytes. Variables of different types may require different amounts of memory to be allocated.

We saw at the beginning of this chapter how your computer’s memory is organized into bytes. Each variable will occupy some number of bytes in memory, so how many bytes are needed to store an integer? Well, it depends on how big the integer value is. A single byte can store an integer value from –128 to +127. This would be enough for some of the values that we’ve seen so far, but what if you want to store a count of the average number of stitches in a pair of knee-length socks? One byte would not be anywhere near enough. On the other hand, if you want to record the number of hamburgers a person can eat in two minutes, a single byte is likely to be enough, and allocating more bytes for this purpose would be wasting memory. Not only do you have variables of different types in C that store numbers of different types, one of which happens to be integers; you also have several varieties of integer variables to provide for different ranges of integers to be stored.

Signed Integer Types

We have five basic flavors of variables that we can declare that can store signed integer values, so positive and negative values can be stored (I’ll get to unsigned integer values in the next section). Each type is specified by a different keyword or combination of keywords, as shown in Table 2-3.
Table 2-3.

Type Names for Integer Variable Types

Type name

Number of bytes

signed char

1

short

2

int

4

long

4

long long

8

Here are some declarations for variables of these types:
short shoe_size;
int house_number;
long long star_count;

The type names short, long, and long long can be written as short int, long int, and long long int, and they can optionally have the keyword signed in front. However, these types are almost always written in their abbreviated forms as shown in Table 2-3. Type int can also be written as signed int, but you won’t see this often either. Table 2-3 reflects the typical number of bytes for each type. The amount of memory occupied by variables of these types, and therefore the range of values you can store, depends on the particular compiler you’re using. It’s easy to find out what the limits are for your compiler because they are defined in the limits.h header file, and I’ll show you how to do this later in the chapter .

Unsigned Integer Types

Some kinds of data are always positive, the number of pebbles on a beach, for example. In such cases, you don’t need to provide for negative values. For each type that stores signed integers, there is a corresponding type that stores unsigned integers, and the unsigned type occupies the same amount of memory as the signed type. Each unsigned type name is essentially the signed type name prefixed with the keyword unsigned . Table 2-4 shows the basic set of unsigned integer types that you can use.
Table 2-4.

Type Names for Unsigned Integer Types

Type name

Number of bytes

unsigned char

1

unsigned short or unsigned short int

2

unsigned int

4

unsigned long or unsigned long int

4

unsigned long long or unsigned long long int

8

With a given number of bits, the number of different values that can be represented is fixed. A 32-bit integer variable can represent any of 4,294,967,296 different values. Thus, using an unsigned type doesn’t provide more values than the corresponding signed type, but it does allow numbers to be represented that are twice the magnitude.

Here are examples of unsigned integer variable declarations:
unsigned int count;
unsigned long population;
Note

Variables of different types that occupy the same number of bytes are still different. Type long and type int may occupy the same amount of memory, but they are still different types.

Specifying Integer Constants

Because you can have different types of integer variables, you might expect to have different kinds of integer constants, and you do. If you just write the integer value 100, for example, this will be of type int. If you want to make sure it is type long, you must append an uppercase or lowercase letter L to the numeric value. So 100 as a long value is written as 100L. Although it’s perfectly legal to use it, a lowercase letter l is best avoided because it’s easily confused with the digit 1.

To declare and initialize the variable Big_Number, you could write this:
long Big_Number = 1287600L;
You write negative integer constants with a minus sign, for example:
int decrease = -4;
long  below_sea_level = -100000L;
You specify integer constants to be of type long long by appending two Ls:
long long really_big_number = 123456789LL;
To specify a constant to be of an unsigned type, you append a U, as in these examples:
unsigned int count = 100U;
unsigned long value = 999999999UL;
To store integers with the largest magnitude, you could define a variable like this:
 unsigned long long metersPerLightYear = 9460730472580800ULL;

The ULL specifies that the initial value is type unsigned long long.

Hexadecimal Constants

You can write integer values in hexadecimal form—that is, to base 16. The digits in a hexadecimal number are the equivalent of decimal values 0–15, and they’re represented by 09 and A–F (or a–f). Because there needs to be a way to distinguish between 9910 and 9916, hexadecimal numbers are written with the prefix 0x or 0X. You would therefore write 9916 in your program as 0x99 or as 0X99. Hexadecimal constants can also have a suffix. Here are some examples of hexadecimal constants:
0xFFFF     0xdead     0xfade     0xFade     0x123456EE     0xafL     0xFABABULL

The last example is of type unsigned long long, and the second to last example is of type long.

Hexadecimal constants are most often used to specify bit patterns, because each hexadecimal digit corresponds to 4 bits. Two hexadecimal digits specify a byte. The bitwise operators that you’ll learn about in Chapter 3 are usually used with hexadecimal constants that define masks. If you’re unfamiliar with hexadecimal numbers, you can find a detailed discussion of them in Appendix A.

Octal Constants

An octal value is a number to base 8. Each octal digit has a value from 0 to 7, which corresponds to 3 bits in binary. Octal values originate from the days long ago when computer memory was in terms of 36-bit words, so a word was a multiple of 3 bits. Thus, a 36-bit binary word could be written as 12 octal digits. Octal constants are rarely used these days, but you need to be aware of them so you don’t specify an octal constant by mistake.

An integer constant that starts with a zero, such as 014, will be interpreted by your compiler as an octal number. Thus, 014 is the octal equivalent of the decimal value 12. If you meant it to be the decimal value 14, it is obviously wrong, so don’t put a leading zero in your integers unless you really want to specify an octal value. There is rarely a need to use octal values.

Default Integer Constant Types

As we have learned, an integer constant without a suffix will be of type int by default, but what if the value is too large for type int? In this case, the compiler will create a constant of a type that it determines based on any suffix that is present whether or not the value is decimal. Table 2-5 shows what the compiler will decide in various situations.
Table 2-5.

Type Names for Unsigned Integer Types

Suffix

Decimal constant

Octal or hexadecimal constant

none

1. int

2. long

3. long long

1. int

2. unsigned int

3. long

4. unsigned long

5. long long

6. unsigned long long

U

1. unsigned int

2. unsigned long

3. unsigned long long

1. unsigned int

2. unsigned long

3. unsigned long long

L

1. long

2. long long

1. λονγ

2. unsigned long

3. long long

4. unsigned long long

UL

1. unsigned long

2. unsigned long long

1. unsigned long

2. unsigned long long

LL

1. long long

1. long long

2. unsigned long long

ULL

1. unsigned long long

1. unsigned long long

The compiler chooses the first type that accommodates the value, as the numbers in the table entries indicate. For instance, a hexadecimal constant with u or U appended will be unsigned int by default; otherwise, it will be unsigned long, and if the range for that is too limited, it will be unsigned long long. Of course, if you specify an initial value for a variable that does not fit within the range of the variable type, you will get an error message from the compiler.

Working with Floating-Point Numbers

Floating-point numbers hold values that are written with a decimal point, so you can represent fractional as well as integral values. The following are examples of floating-point values:
1.6    0.00008    7655.899 100.0
The last constant is integral, but it will be stored as a floating-point value because of the presence of the decimal point. Because of the way floating-point numbers are represented, they define a fixed number of digits. Limiting accuracy to a fixed number of digits is a bit of a disadvantage, but floating-point numbers can represent a very wide range of values—much wider than integer types, which more than compensates. Floating-point numbers are often expressed as a decimal value multiplied by some power of 10, where the power of 10 is called the exponent . For example, each of the examples of preceding floating-point numbers could be expressed as shown in Table 2-6.
Table 2-6.

Expressing Floating-Point Numbers

Value

With an exponent

Can also be written in C as

1.6

0.16×101

0.16E1

0.00008

0.8×10-4

0.8E-4

7655.899

0.7655899×104

0.7655899E4

100.0

1.0×102

1.0E2

The center column shows how the numbers in the left column could be written with an exponent. This isn’t how you write these numbers in C; it’s just an alternative way of representing the values. The right column shows how the representation in the center column can be expressed in C. The E in each of the numbers is for exponent, and you could equally well use a lowercase e. Of course, you can write each of these numbers in your program without an exponent, just as they appear in the left column, but for very large or very small numbers, the exponent form is very useful. I’m sure you would rather write 0.5E-15 than 0.0000000000000005, wouldn’t you?

Floating-Point Number Representation

Internal floating-point number representation is a little complicated. You can skip this section if you are not really interested in what goes on inside your computer. I am including this explanation here because an understanding of how your computer handles floating-point values will give you much better insight into why the range of such values is what it is. Figure 2-2 shows how a floating-point number is stored in a 4-byte word in memory on an Intel PC.
../images/311070_6_En_2_Chapter/311070_6_En_2_Fig3_HTML.png
Figure 2-2.

A single-precision floating-point value in memory 32 bit

(1 bit for sign, 11 bits for exponent, and 52 bits for mantissa)

This is a single-precision floating-point value, which occupies 4 bytes of memory. The value consists of three parts:
  • A sign bit that is 0 for a positive value and 1 for a negative value

  • An 8-bit exponent

  • A 23-bit mantissa

The mantissa contains the digits in the number and occupies 23 bits. It is assumed to be a binary value of the form 1.bbb...b, with 23 bits to the right of the binary point. Thus, the value of the mantissa is always greater than or equal to 1 and less than 2. I’m sure you are wondering how you get a 24-bit value into 23 bits, but it’s quite simple. The leftmost bit is always 1, so it does not need to be stored. Adopting this approach provides an extra binary digit of precision.

The exponent is an unsigned 8-bit value, so the exponent value can be from 0 to 255. The actual value of the floating-point number is the mantissa multiplied by 2 to the power of the exponent, or 2exp, where exp is the exponent value. You need negative exponent values to allow small fractional numbers to be represented. To accommodate this, the actual exponent for a floating-point value has 127 added to it. This allows for values from –127 to 128 to be represented as an 8-bit unsigned value. Thus, an exponent of –6 will be stored as 121, and an exponent of 6 will be stored as 133. However, there are a few complications.

An actual exponent of –127, which corresponds to a stored exponent of 0, is reserved for a special case. A floating-point value of zero is represented by a word with all bits in the mantissa and exponent as 0, so the actual exponent value of –127 cannot be used for other values.

Another complication arises because it is desirable to be able to detect division by zero. Two more special values are reserved that represent +infinity and -infinity, the values that result from dividing a positive number or a negative number by zero. (This can be achieved when there is an overflow or underflow for the data type of the variable since the C99 compiler handles these boundaries—by multiplying or dividing—by replacing the result with -+INF.) Dividing a positive number by zero will generate a result with a zero sign bit, all the exponent bits 1, and all the mantissa bits 0. This value is special and represents +infinity and not the value 1 × 2 and all the mantissa bits 0. Dividing a negative number by zero results in the negation of that value, so –1 × 2128 is a special value too.

The last complication arises because it is desirable to be able to represent the result dividing zero by zero. This is referred to as “Not a Number” (NaN) . The value reserved for this has all exponent bits 1 and the leading digit in the mantissa as 1 or 0 depending on whether the NaN is a quiet NaN, which allows execution to continue, or it is a signaling NaN, which causes an exception in the code that can terminate execution. When NaN has a leading 0 in the mantissa, at least one of the other mantissa bits is 1 to distinguish it from infinity.

Also, there are other definitions for the IEEE-754 standard, for example, double precision which contains for its representation 1 bit for sign, 11 bits for exponent, and 52 bits for mantissa. These will be described later for float, double, and long double.

Caution

Because your computer stores a floating-point value as a binary mantissa combined with a binary exponent, some fractional decimal values cannot be represented exactly in this way. The binary places to the right of the binary point in the mantissa, .1, .01, .001, .0001, and so on, are equivalent to the decimal fractions 1/2, 1/4, 1/8, 1/16, and so on. Thus, the fractional part of the binary mantissa can only represent decimal values that are the sum of a subset of these decimal fractions. You should be able to see that values such as 1/3 or 1/5 cannot be represented exactly in a binary mantissa because there is no combination of the binary digits that will sum to these values.

Floating-Point Variables

Floating-point variable types only store floating-point numbers. You have a choice of three types of floating-point variables, and these are shown in Table 2-7.
Table 2-7.

Floating-Point Variable Types

Keyword

Number of bytes

Range of values

float

4

±3.4E±38 (6–7 decimal digits of precision)

double

8

±1.7E±308 (15 decimal digits of precision)

long double

12

±1.19E±4932 (18 decimal digits of precision)

Float

These are typical values for the number of bytes occupied and for the ranges of values that are supported. Like the integer types, the memory occupied and the range of values are dependent on the machine and the compiler. The type long double (it exists since C90) is sometimes exactly the same as type double with some compilers. Note that the number of decimal digits of precision is an approximation because floating-point values will be stored internally in binary form, and a binary mantissa does not map to an exact number of decimal digits.

You declare a floating-point variable in a similar way to an integer variable. You just use the keyword for the floating-point type that you want:
float radius;
double biggest;

If you need to store numbers with up to roughly seven decimal digits of accuracy (typically with a range of 10–38–10+38), you should use variables of type float. Values of type float are known as single-precision floating-point numbers. This type will occupy 4 bytes in memory, as you can see from the table. Using variables of type double will allow you to store double-precision floating-point values (64 bit) . Each variable of type double will occupy 8 bytes in memory and provide roughly 15-digit precision with a range of 10–308–10+308. Variables of type double suffice for the majority of requirements, but some specialized applications require even more accuracy and range. The long double type typically provides the exceptional range and precision shown in the table, but this depends on the compiler.

To write a constant of type float , you append an f to the number to distinguish it from type double. You could initialize the previous two variables when you declare them like this:
float radius = 2.5f;
double biggest = 123E30;

The variable radius has the initial value 2.5, and the variable biggest is initialized to the number that corresponds to 123 followed by 30 zeroes. Any number that you write containing a decimal point is of type double unless you append the F to make it type float. When you specify an exponent value with E or e, the constant need not contain a decimal point. For instance, 1E3f is of type float, and 3E8 is of type double.

To specify a long double constant, you append an uppercase or lowercase letter L, as in the following example:
long double huge = 1234567.89123L;

Division Using Floating-Point Values

You have seen that division with integer operands always produces an integer result. Unless the left operand of a division is an exact multiple of the right operand when dividing one integer by another, the result will be inherently inaccurate. Of course, the way integer division works is an advantage if you’re distributing cookies to children, but it isn’t particularly useful when you want to cut a 10-foot plank into four equal pieces. This is a job for floating-point values.

Division with floating-point operands will give you an exact result—at least, a result that is as exact as it can be with a fixed number of digits of precision. The next example illustrates how division operations work with variables of type float.

Try It Out: Division With Values of Type Float
Here’s a simple example that divides one floating-point value by another and outputs the result:
// Program 2.7 Division with float values
#include <stdio.h>
int main(void)
{
  float plank_length = 10.0f;               // In feet
  float piece_count = 4.0f;                 // Number of equal pieces
  float piece_length = 0.0f;                // Length of a piece in feet
  piece_length = plank_length/piece_count;
  printf("A plank %f feet long can be cut into %f pieces %f feet long. ",
                                       plank_length, piece_count, piece_length);
  return 0;
}
This program produces the following output:
A plank 10.000000 feet long can be cut into 4.000000 pieces 2.500000 feet long.

How It Works

You should not have any trouble understanding how you chop the plank into equal pieces. You use a new format specifier for values of type float in the printf() statement:
  printf("A plank %f feet long can be cut into %f pieces %f feet long. ",
                                       plank_length, piece_count, piece_length);

You use the format specifier %f to display floating-point values. In general, the format specifier that you use must correspond to the type of value you’re outputting. If you output a value of type float with the specifier %d that’s to be used with integer values, you’ll get garbage. This is because the float value will be interpreted as an integer, which it isn’t. Similarly, if you use %f with a value of an integer type, you’ll also get garbage as output.

Controlling the Number of Decimal Places in the Output

In the previous example, you got a lot of decimal places in the output that you really didn’t need. You may be good with a ruler and a saw, but you aren’t going to be able to cut the plank with a length of 2.500000 feet rather than 2.500001 feet. You can specify the number of places that you want to see after the decimal point in the format specifier. To obtain the output to two decimal places, you would write the format specifier as %.2f. To get three decimal places, you would write %.3f.

You can change the printf() statement in the previous example so that it will produce more suitable output:
  printf("A plank %.2f feet long can be cut into %.0f pieces %.2f feet long. ",
                                       plank_length, piece_count, piece_length);
The first format specification applies to the value of plank_length and will produce output with two decimal places. The second specification will produce no decimal places—this makes sense here because the piece_count value is a whole number. The last specification is the same as the first. Thus, if you run the example with this version of the last statement, the output will be the following:
A plank 10.00 feet long can be cut into 4 pieces 2.50 feet long.

This is much more appropriate and looks a lot better. Of course, you could make piece_count an integer type, which would be better still.

Controlling the Output Field Width

The field width for an output value is the total number of characters used for the value including spaces. In this program, it has been determined by default. The printf() function works out how many character positions will be required for a value, given the number of decimal places you specify, and uses that as the field width. However, you can specify the field width. You can also specify the number of decimal places. A fixed field width is essential if you want to output a column of values so they align vertically. If you let the printf() function work out the field width, you’re likely to get a ragged column of output. The general form of the format specifier for floating-point values can be written like this:
 %[width][.precision][modifier]f

The square brackets here aren’t part of the specification. They enclose bits of the specification that are optional. Thus, you can omit width or .precision or modifier or any combination of these. The width value is an integer specifying the total number of characters in the output including spaces, so it is the output field width. The precision value is an integer specifying the number of decimal places that are to appear after the decimal point. The modifier part is L when the value you are outputting is type long double; otherwise, you omit it.

You could rewrite the printf() call in the previous example to specify the field width as well as the number of digits you want after the decimal point:
  printf("A %8.2f plank foot can be cut into %5.0f pieces %6.2f feet long. ",
                                               plank_length, piece_count, piece_length);

I changed the text a little here because of the page width in the book. The first output value now has a field width of eight and two decimal places after the decimal point. The second output value, which is the count of the number of pieces, has a field width of five characters and no decimal places. The third output value will be presented in a field width of six characters with two decimal places.

When you specify the field width, the output value will be right aligned by default. If you want the value to be left aligned in the field, just put a minus sign following the %. For instance, the specification %-10.4f will output a floating-point value left aligned in a field width of ten characters with four digits following the decimal point.

You can also specify a field width and the alignment in the field with a specification for outputting an integer value. For example, %-15d specifies an integer value will be presented left aligned in a field width of 15 characters. This is not all there is to format specifiers. We’ll learn more about them later. Try out some variations using the previous example. In particular, see what happens when the field width is too small for the value.

More Complicated Expressions

Of course, arithmetic can get a lot more complicated than just dividing one number by another. If that were the case, you could get by with paper and pencil. With more complicated calculations, you will often need more control over the sequence of operations when an expression is evaluated. Parentheses in an arithmetic expression provide you with this capability. They can also help to make complicated expressions clearer.

Parentheses in arithmetic expressions work much as you’d expect. Subexpressions that are enclosed within parentheses are evaluated in sequence, starting with the expression in the innermost pair of parentheses and progressing through to the outermost. The normal rules you’re used to for operator precedence apply, so multiplication and division happen before addition or subtraction. For example, the expression 2 * (3 + 3 * (5 + 4)) evaluates to 60. You start with the expression 5 + 4, which produces 9. The result is multiplied by 3, which gives 27. Then you add 3 to that total (giving 30) and finally multiply 30 by 2.

You can insert spaces to separate operands from operators to make your arithmetic statements more readable, or you can leave them out when you need to make the code more compact. Either way, the compiler doesn’t mind, as it will ignore spaces. If you’re not quite sure of how an expression will be evaluated according to the precedence rules, you can always put in some parentheses to make sure it produces the result you want.

Try It Out: Arithmetic in Action
This time you’ll have a go at calculating the circumference and area of a circular table from an input value for its diameter. You may remember from elementary math the equations to calculate the area and circumference of a circle using π or pi (circumference = 2πr and area = πr2, where r is the radius). If you don’t, don’t worry. This isn’t a math book, so just look at how the program works:
// Program 2.8 calculations on a table
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
  float radius = 0.0f;                      // The radius of the table
  float diameter = 0.0f;                    // The diameter of the table
  float circumference = 0.0f;               // The circumference of the table
  float area = 0.0f;                        // The area of the table
  float Pi = 3.14159265f;
  printf("Input the diameter of the table:");
  scanf("%f", &diameter);               // Read the diameter from the keyboard
  radius = diameter/2.0f;               // Calculate the radius
  circumference = 2.0f*Pi*radius;       // Calculate the circumference
  area = Pi*radius*radius;              // Calculate the area
  printf(" The circumference is %.2f", circumference) ;
  printf(" The area is %.2f. ", area);
  return 0;
}

scanf could overflow the buffer, generating problems; for the corresponding error-prone behavior, the C11 version standard defined in Annex K safer functions (they have suffix _s in their names) that handle these possible overflows. We are introducing scanf_s later in Chapter 6. This doesn't mean that scanf is deprecated (although MS declares it). GCC and other compilers still use it. Microsoft compiler implements an approximation of these safer functions. If you still want to use the original scanf, then you must define _CRT_SECURE_NO_WARNINGS to avoid an error from the Visual Studio compiler.

Here’s some typical output from this example:
Input the diameter of the table: 6
The circumference is 18.85.
The area is 28.27.

How It Works

Up to the first printf(), the program looks much the same as those you’ve seen before:
  float radius = 0.0f;                      // The radius of the table
  float diameter = 0.0f;                    // The diameter of the table
  float circumference = 0.0f;               // The circumference of the table
  float area = 0.0f;                        // The area of the table
  float Pi = 3.14159265f;

You declare and initialize five variables, where Pi has its usual value. Note how all the initial values have an f at the end because you’re initializing values of type float. Without the f, the values would be of type double. They would still work here, but you would be introducing some unnecessary conversion that the compiler would have to arrange, from type double to type float. There are more digits in the value of Pi that type float can accommodate, so the compiler will chop off the least significant part so it fits.

The next statement outputs a prompt for input from the keyboard:
  printf("Input the diameter of the table:");
The next statement deals with reading the value for the diameter of the table. You use a new standard library function, the scanf() function, to do this:
  scanf("%f", &diameter);                   // Read the diameter from the keyboard

The scanf() function is another function that requires the stdio.h header file to be included. This function handles input from the keyboard. In effect, it takes what you enter through the keyboard and interprets it as specified by the first argument, which is a control string between double quotes. In this case, the control string is "%f" because you’re reading a value of type float. It stores the result in the variable specified by the second argument, diameter in this instance. The first argument is a control string similar to what we used with the printf() function, except here it controls input rather than output. We’ll learn more about the scanf() function in Chapter 10; and, for reference, Appendix D summarizes the control strings you can use with it.

You’ve undoubtedly noticed something new here: the & preceding the variable name diameter. This is called the address of operator, and it’s needed to allow the scanf() function to store the value that is read from the keyboard in your variable, diameter. The reason for this is bound up with the way argument values are passed to a function. For the moment, I won’t go into a more detailed explanation of this; you’ll see more on this in Chapter 8. Just remember to use the address of operator (the & sign) before a variable when you’re using the scanf() function and not to use it when you use the printf() function.

Within the control string for the scanf() function , the % character identifies the start of a format specification for an item of data. The f that follows the % indicates that the input should be interpreted as a floating-point value. In general, there can be several format specifications within the control string, in which case these determine how each of a succession of input values will be interpreted. There must be as many argument variables following the control string in the scanf() function call as there are format specifications. We’ll learn a lot more on how scanf() works later in the book, but for now the basic set of format specifiers you can use for reading data of various types are shown in Table 2-8.
Table 2-8.

Format Specifiers for Reading Data

Action

Required control string

To read a value of type short

%hd

To read a value of type int

%d

To read a value of type long

%ld

To read a value of type float

%f or %e

To read a value of type double

%lf or %le

In the %ld and %lf format specifiers, l is lowercased. Don’t forget you must always prefix the name of the variable that’s receiving the input value with &. Also, if you use the wrong format specifier—if you read a value into a variable of type float with %d, for instance—the data value in your variable won’t be correct, but you’ll get no indication that a junk value has been stored.

Next, you have three statements that calculate the results you’re interested in:
  radius = diameter/2.0f;               // Calculate the radius
  circumference = 2.0f*Pi*radius;       // Calculate the circumference
  area = Pi*radius*radius;              // Calculate the area

The first statement calculates the radius as half of the value of the diameter that was entered. The second statement computes the circumference of the table, using the value that was calculated for the radius. The third statement calculates the area. Note that if you forget the f in 2.0f, you’ll probably get a warning message from your compiler. This is because without the f, the constant is of type double, and you would be mixing different types in the same expression. You’ll see more about this later.

You could have written the statements to calculate the circumference and area like this:
  circumference = 2.0f*Pi*(diameter/2.0f);       // Calculate the circumference
  area = Pi*(diameter/2.0f)*(diameter/2.0f);     // Calculate the area

The parentheses ensure that the value for the radius is calculated first in each statement. They also help to make it clear that it is the radius that is being calculated. A disadvantage to these statements is that the radius calculation is potentially carried out three times, when it is only necessary for it to be carried out once. A clever compiler can optimize this code and arrange for the radius calculation to be done only once.

The next two statements output the values you’ve calculated:
  printf(" The circumference is %.2f.", circumference);
  printf(" The area is %.2f. ", area);

These printf() statements output the values of the variables circumference and area using the format specifier %.2f. The format specification outputs the values with two decimal places after the point. The default field width will be sufficient in each case to accommodate the value that is to be displayed.

Of course, you can run this program and enter whatever values you want for the diameter. You could experiment with different forms of floating-point input here, and you could try entering something like 1E1f, for example.

Defining Named Constants

Although Pi is defined as a variable in the previous example, it’s really a constant value that you don’t want to change. The value of π is always a fixed number with an unlimited number of decimal digits. The only question is how many digits of precision you want to use in its specification. It would be nice to make sure its value stayed fixed in a program so it couldn’t be changed by mistake.

There are a couple of ways in which you can approach this. The first is to define Pi as a symbol that’s to be replaced in the program by its value during compilation. In this case, Pi isn’t a variable at all, but more a sort of alias for the value it represents. Let’s try that out.

Try It Out: Defining a Constant
Let’s look at specifying the name PI as an alias for its value:
// Program 2.9 More round tables
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define PI   3.14159f                       // Definition of the symbol PI
int main(void)
{
  float radius = 0.0f;
  float diameter = 0.0f;
  float circumference = 0.0f;
  float area = 0.0f;
  printf("Input the diameter of a table:");
  scanf("%f", &diameter);
  radius = diameter/2.0f;
  circumference = 2.0f*PI*radius;
  area = PI*radius*radius;
  printf(" The circumference is %.2f. ", circumference);
  printf(" The area is %.2f. ", area);
  return 0;
}

This produces the same output as the previous example.

How It Works

After the comment and the #include directive for the header file, there is the following preprocessing directive:
#define PI   3.14159f                       // Definition of the symbol PI

This defines PI as a symbol that is to be replaced in the code by the string 3.14159f. I used PI rather than Pi, because it’s a common convention in C to write identifiers that appear in a #define directive in capital letters. Wherever you reference PI within an expression in the program, the preprocessor will substitute the string that you have specified for it in the #define directive. All the substitutions will be made before compiling the program. When the program is ready to be compiled, it will no longer contain references to PI, because all occurrences will have been replaced by the sequence of characters you’ve specified in the #define directive. This all happens internally while your program is processed by the compiler. Your source file will not be changed; it will still contain the symbol PI.

Caution The preprocessor makes the substitution for a symbol in the code without regard for whether it makes sense. If you make an error in the substitution string, if you wrote 3.14.159f, for example, the preprocessor will still replace every occurrence of PI in the code with this, and the program will not compile.

Your other option is to define Pi as a variable, but to tell the compiler that its value is fixed and must not be changed. You can fix the value of any variable when you declare it by prefixing the type name with the keyword const, for example:
  const float Pi = 3.14159f;                // Defines the value of Pi as fixed

The advantage of defining Pi in this way is that you are now defining it as a constant numerical value with a specified type. In the previous example, PI was just a sequence of characters that replaced all occurrences of PI in your code.

The keyword const in the declaration for Pi causes the compiler to check that the code doesn’t attempt to change its value. Any code that does so will be flagged as an error, and the compilation will fail. Let’s see a working example of this.

Try It Out: Defining a Variable With a Fixed Value
You can use a const variable in a variation of the previous example but with the code shortened a little:
// Program 2.10 Round tables again but shorter
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
  float diameter = 0.0f;                    // The diameter of a table
  float radius = 0.0f;                      // The radius of a table
  const float Pi = 3.14159f;                // Defines the value of Pi as fixed
  printf("Input the diameter of the table:");
  scanf("%f", &diameter);
  radius = diameter/2.0f;
  printf(" The circumference is %.2f.", 2.0f*Pi*radius);
  printf(" The area is %.2f. ", Pi*radius*radius);
  return 0;
}

How It Works

Following the declaration for the variable radius is this statement:
  const float Pi = 3.14159f;                // Defines the value of Pi as fixed

This declares the variable Pi and defines a value for it; Pi is still a variable here, but the initial value cannot be changed. The const modifier achieves this effect. It can be applied to the definition of any variable of any type to fix its value. The compiler will check your code for attempts to change variables that you’ve declared as const, and if it discovers an attempt to change a const variable, it will complain. There are ways to trick the compiler to allow a const variable to be changed, but this defeats the whole point of using const in the first place.

The two statements that produce the output from the program are
  printf(" The circumference is %.2f.", 2.0f*Pi*radius);
  printf(" The area is %.2f. ", Pi*radius*radius);

In this example, you no longer use variables to store the circumference and area of the circle. The expressions for these now appear as arguments in the printf() statements, where they’re evaluated, and their values are passed directly to the function.

As we learned before, a value that you pass to a function can be the result of an expression. In this case, the compiler creates a temporary variable to hold the value of the result of the expression, and that will be passed to the function. The temporary variable is subsequently discarded. This is fine, as long as you don’t want to use these values elsewhere.

Knowing Your Limitations

Of course, it may be important to be able to determine within a program exactly what the limits are on the values that can be stored by a given integer type. As I mentioned earlier, the limits.h header file defines symbols that represent values for the limits for each type. Table 2-9 shows the symbol names corresponding to the limits for each signed integer type.
Table 2-9.

Symbols Representing Range Limits for Integer Types

Type

Lower limit

Upper limit

char

CHAR_MIN

CHAR_MAX

short

SHRT_MIN

SHRT_MAX

int

INT_MIN

INT_MAX

long

LONG_MIN

LONG_MAX

long long

LLONG_MIN

LLONG_MAX

The lower limits for the unsigned integer types are all 0, so there are no symbols for these. The symbols corresponding to the upper limits for the unsigned integer types are UCHAR_MAX, USHRT_MAX, UINT_MAX, ULONG_MAX, and ULLONG_MAX.

To be able to use any of these symbols in a program, you must have an #include directive for the limits.h header file in the source file:
#include <limits.h>
You could initialize a variable with the maximum possible value for type int like this:
int number = INT_MAX;

This statement sets the value of number to be the maximum possible, whatever that may be for the compiler used to compile the code.

The float.h header file defines symbols that characterize floating-point values. Some of these are quite technical, so I’ll just mention those you are most likely to be interested in. The symbols defining the maximum and minimum positive values that can be represented by the three floating-point types are shown in Table 2-10. You can also access the symbols FLT_DIG, DBL_DIG, and LDBL_DIG that indicate the number of decimal digits that can be represented by the binary mantissa of the corresponding types. Let’s explore in a working example how to access some of the symbols characterizing integers and floating-point values.
Table 2-10.

Symbols Representing Range Limits for Floating-Point Types

Type

Lower limit

Upper limit

float

FLT_MIN

FLT_MAX

double

DBL_MIN

DBL_MAX

long double

LDBL_MIN

LDBL_MAX

Try It Out: Finding the Limits
This program outputs the values corresponding to the symbols defined in the header files, so it will tell you the limits for your compiler:
// Program 2.11 Finding the limits
#include <stdio.h>                          // For command line input and output
#include <limits.h>                         // For limits on integer types
#include <float.h>                          // For limits on floating-point types
int main(void)
{
  printf("Variables of type char store values from %d to %d ", CHAR_MIN, CHAR_MAX);
  printf("Variables of type unsigned char store values from 0 to %u ", UCHAR_MAX);
  printf("Variables of type short store values from %d to %d ", SHRT_MIN, SHRT_MAX);
  printf("Variables of type unsigned short store values from 0 to %u ", USHRT_MAX);
  printf("Variables of type int store values from %d to %d ", INT_MIN,  INT_MAX);
  printf("Variables of type unsigned int store values from 0 to %u ", UINT_MAX);
  printf("Variables of type long store values from %ld to %ld ", LONG_MIN, LONG_MAX);
  printf("Variables of type unsigned long store values from 0 to %lu ", ULONG_MAX);
  printf("Variables of type long long store values from %lld to %lld ", LLONG_MIN, LLONG_MAX);
  printf("Variables of type unsigned long long store values from 0 to %llu ", ULLONG_MAX);
  printf(" The size of the smallest positive non-zero value of type float is %.3e ", FLT_MIN);
  printf("The size of the largest value of type float is %.3e ", FLT_MAX);
  printf("The size of the smallest non-zero value of type double is %.3e ", DBL_MIN);
  printf("The size of the largest value of type double is %.3e ", DBL_MAX);
  printf("The size of the smallest non-zero value of type long double is %.3Le ", LDBL_MIN);
  printf("The size of the largest value of type long double is %.3Le ",  LDBL_MAX);
  printf(" Variables of type float provide %u decimal digits precision. ", FLT_DIG);
  printf("Variables of type double provide %u decimal digits precision. ", DBL_DIG);
  printf("Variables of type long double provide %u decimal digits precision. ",
                                                                        LDBL_DIG);
  return 0;
}
You’ll get output somewhat similar to the following, which corresponds to what my compiler offers:
Variables of type char store values from -128 to 127
Variables of type unsigned char store values from 0 to 255
Variables of type short store values from -32768 to 32767
Variables of type unsigned short store values from 0 to 65535
Variables of type int store values from -2147483648 to 2147483647
Variables of type unsigned int store values from 0 to 4294967295
Variables of type long store values from -2147483648 to 2147483647
Variables of type unsigned long store values from 0 to 4294967295
Variables of type long long store values from -9223372036854775808 to 9223372036854775807
Variables of type unsigned long long store values from 0 to 18446744073709551615
The size of the smallest positive non-zero value of type float is 1.175e-038
The size of the largest value of type float is 3.403e+038
The size of the smallest non-zero value of type double is 2.225e-308
The size of the largest value of type double is 1.798e+308
The size of the smallest non-zero value of type long double is 3.362e-4932
The size of the largest value of type long double is 1.190e+4932
Variables of type float provide 6 decimal digits precision.
Variables of type double provide 15 decimal digits precision.
Variables of type long double provide 18 decimal digits precision.

How It Works

You output the values of symbols that are defined in the limits.h and float.h header files in a series of printf() function calls. Numbers in your computer are always limited in the range of values that can be stored, and the values of these symbols represent the boundaries for values of each numerical type. You have used the %u specifier to output the unsigned integer values. If you use %d for the maximum value of an unsigned type, values that have the leftmost bit (the sign bit for signed types) as 1 won’t be interpreted correctly.

You use the %e specifier for the floating-point limits, which presents the values in exponential form. You also specify just three digits’ precision, as you don’t need the full accuracy in the output. The L modifier is necessary when the value being displayed by the printf() function is type long double. Remember, this has to be a capital letter L; a lowercase letter won’t do here. The %f specifier presents values without an exponent, so it’s rather inconvenient for very large or very small values. If you try it in the example, you’ll see what I mean.

Introducing the sizeof Operator

You can find out how many bytes are occupied by a given type by using the sizeof operator . Of course, sizeof is a keyword in C. The expression sizeof(int) will result in the number of bytes occupied by a variable of type int, and the result is an integer of type size_t. Type size_t is defined in the standard header file stddef.h, as well as several other headers, and will correspond to one of the basic integer types. Because the choice of type that corresponds to type size_t may differ between one C library and another, it’s best to use variables of size_t to store the value produced by the sizeof operator, even when you know the basic type to which it corresponds. Here’s how you could store a value that results from applying the sizeof operator:
size_t size = sizeof(long long);

You can also apply the sizeof operator to an expression, in which case the result is the size of the value that results from evaluating the expression. In this context, the expression would usually be just a variable of some kind. The sizeof operator has uses other than just discovering the memory occupied by a value of a basic type, but for the moment, let’s just use it to find out how many bytes are occupied by each type.

Try It Out: Discovering the Number of Bytes Occupied by a Type
This program will output the number of bytes occupied by each numeric type:
// Program 2.12 Finding the size of a type
#include <stdio.h>
int main(void)
{
  printf("Variables of type char occupy %u bytes ", sizeof(char));
  printf("Variables of type short occupy %u bytes ", sizeof(short));
  printf("Variables of type int occupy %u bytes ", sizeof(int));
  printf("Variables of type long occupy %u bytes ", sizeof(long));
  printf("Variables of type long long occupy %u bytes ", sizeof(long long));
  printf("Variables of type float occupy %u bytes ", sizeof(float));
  printf("Variables of type double occupy %u bytes ", sizeof(double));
  printf("Variables of type long double occupy %u bytes ", sizeof(long double));
  return 0;
}
On my system I get the following output:
Variables of type char occupy 1 bytes
Variables of type short occupy 2 bytes
Variables of type int occupy 4 bytes
Variables of type long occupy 4 bytes
Variables of type long long occupy 8 bytes
Variables of type float occupy 4 bytes
Variables of type double occupy 8 bytes
Variables of type long double occupy 12 bytes

How It Works

Because the sizeof operator results in an unsigned integer value, you output it using the %u specifier. Note that you can also obtain the number of bytes occupied by a variable, var_name, with the expression sizeof var_name. Obviously, the space between the sizeof keyword and the variable name in the expression is essential.

Now you know the range limits and the number of bytes occupied by each numeric type with your compiler.

Note

If you want to apply the sizeof operator to a type, the type name must be between parentheses, like this: sizeof(long double). When you apply sizeof to an expression, the parentheses are optional.

Choosing the Correct Type for the Job

You have to be careful to select the type of variable that you’re using in your calculations so that it accommodates the range of values you expect. If you use the wrong type, you may find that errors creep into your programs that can be hard to detect. (Besides the undefined behavior that may occur, there are known exploits of buffer overflow; however, they are beyond this book's scope. Please check OWASP Buffer Overflow.) This is best shown with an example.

Try It Out: The Right Types of Variables
Here’s an example that shows how things can go horribly wrong if you choose an unsuitable type for your variables:
// Program 2.13 Choosing the correct type for the job  1
#include <stdio.h>
int main(void)
{
  const float Revenue_Per_150 = 4.5f;
  short JanSold = 23500;                               // Stock sold in January
  short FebSold = 19300;                               // Stock sold in February
  short MarSold = 21600;                               // Stock sold in March
  float  RevQuarter = 0.0f;                            // Sales for the quarter
  short QuarterSold = JanSold + FebSold + MarSold;     // Calculate quarterly total
  // Output monthly sales and total for the quarter
  printf("Stock sold in Jan: %d Feb: %d Mar: %d ", JanSold, FebSold, MarSold);
  printf("Total stock sold in first quarter: %d ", QuarterSold);
  // Calculate the total revenue for the quarter and output it
   RevQuarter = QuarterSold/150*Revenue_Per_150;
  printf("Sales revenue this quarter is:$%.2f ", RevQuarter);
  return 0;
}
These are fairly simple calculations, and you can see that the total stock sold in the quarter should be 64400. This is just the sum of each of the monthly totals, but if you run the program, the output you get is this:
Stock sold in
 Jan: 23500
 Feb: 19300
 Mar: 21600
Total stock sold in first quarter: -1136
Sales revenue this quarter is:$-31.50

Obviously there is something wrong here. It doesn’t take a genius or an accountant to tell you that adding three big, positive numbers together should not produce a negative result.

How It Works

First, you define a constant that will be used in the calculation:
  const float Revenue_Per_150 = 4.5f;

This defines the revenue obtained for every 150 items sold. There’s nothing wrong with that.

Next, you declare four variables and assign initial values to them:
  short JanSold = 23500;                              // Stock sold in January
  short FebSold = 19300;                              // Stock sold in February
  short MarSold = 21600;                              // Stock sold in March
  float  RevQuarter = 0.0f;                           // Sales for the quarter

The first three variables are of type short, which is quite adequate to store the initial value. The RevQuarter variable is of type float because you want two decimal places for the quarterly revenue.

The next statement declares the variable QuarterSold and stores the sum of the sales for each of the months:
  short QuarterSold = JanSold + FebSold + MarSold;    // Calculate quarterly total

It looks like the cause of the erroneous results is in the declaration of the QuarterSold variable. You’ve declared it to be of type short and given it the initial value of the sum of the three monthly figures. You know that their sum is 64400 and that the program outputs a negative number. The error must therefore be in this statement.

The problem arises because you’ve tried to store a number that’s too large for type short. If you recall, the maximum value that a short variable can hold is 32767. The computer can’t interpret the value of QuarterSold correctly and happens to give a negative result. A secondary consideration is that the quantity sold is not going to be negative, so perhaps an unsigned type would be more appropriate. The solution to the problem is to use a variable of type unsigned long for QuarterSold that will allow you to store much larger numbers. You can also specify the variables holding the monthly figures as unsigned.

Solving the Problem

Try changing the program and running it again. You need to change only five lines in the body of the function main(). The new and improved program is as follows:
// Program 2.14 Choosing the correct type for the job  2
#include <stdio.h>
int main(void)
{
  const float Revenue_Per_150 = 4.5f;
  unsigned short JanSold =23500;              // Stock sold in January
  unsigned short FebSold =19300;              // Stock sold in February
  unsigned short MarSold =21600;              // Stock sold in March
  float  RevQuarter = 0.0f;                   // Sales for the quarter
  unsigned long QuarterSold = JanSold + FebSold + MarSold; // Calculate quarterly total
  // Output monthly sales and total for the quarter
  printf("Stock sold in Jan: %d Feb: %d Mar: %d ", JanSold, FebSold, MarSold);
  printf("Total stock sold in first quarter: %ld ", QuarterSold);
  // Calculate the total revenue for the quarter and output it
  RevQuarter = QuarterSold/150*Revenue_Per_150;
  printf("Sales revenue this quarter is:$%.2f ", RevQuarter);
  return 0;
}
When you run this program, the output is more satisfactory:
Stock sold in
   Jan: 23500
   Feb: 19300
   Mar: 21600
  Total stock sold in first quarter: 64400
Sales revenue this quarter is :$1930.50

The stock sold in the quarter is correct, and you have a reasonable result for revenue. Notice that you use %ld to output the total stock sold. This tells the compiler that it is to use a long conversion for the output of this value. Just to check the program, calculate the result of the revenue yourself with a calculator.

The result you should get is, in fact, $1,932. Somewhere you’ve lost a dollar and a half. Not such a great amount, but try telling that to an accountant. You need to find the lost $1.50. Consider what’s happening when you calculate the value for revenue in the program:
  RevQuarter = QuarterSold/150*Revenue_Per_150;
Here you’re assigning a value to RevQuarter . The value is the result of the expression on the right of the = sign. The result of the expression will be calculated, step by step, according to the precedence rules you have already learned in this chapter. Here you have quite a simple expression that’s calculated from left to right, since division and multiplication have the same priority. Let’s work through it:
  • QuarterSold /150 is calculated as 64400/150, which should produce the result 429.333.

This is where your problem arises. QuarterSold is an integer, and so the computer truncates the result of the division to an integer, ignoring the .333. This means that when the next part of the calculation is evaluated, the result will be slightly off:
  • 429*Revenue_Per_150 is calculated as 429 * 4.5 which is 1930.50.

You now know where the error has occurred, but what can you do about it? You could change all of your variables to floating-point types, but that would defeat the purpose of using integers in the first place. The numbers entered really are integers, so you’d like to store them as such. Is there an easy solution to this? In this case, there are two. First, you can rewrite the statement as follows:
  RevQuarter = Revenue_Per_150*QuarterSold/150;

Now the multiplication will occur first; and because of the way arithmetic works with operands of different types, the result will be of type float. The compiler will automatically arrange for the integer operand to be converted to floating point. When you then divide by 150, that operation will execute with float values too, with 150 being converted to 150f. The net effect is that the result will now be correct.

Second, you could just use 150.0 as the divisor. The dividend will then be converted to floating point before the division is executed.

However, there’s more to it than that. Not only do you need to understand more about what happens with arithmetic between operands of different types but you also need to understand how you can control conversions from one type of data to another. In C, you have the ability to explicitly convert a value of one type to another type.

Explicit Type Conversion

Let’s look again at the original expression to calculate the quarterly revenue in Program 2.14 and see how you can control what goes on so that you end up with the correct result:
  RevQuarter = QuarterSold/150*Revenue_Per_150;
You know that if the result is to be correct, this statement has to be amended so that the expression is evaluated with floating-point operands throughout. If you could convert the value of QuarterSold to type float, the expression will be evaluated as floating point, and your problem would be solved. To convert the value of a variable to another type, you place the type you want to cast the value to in parentheses in front of the variable. Thus, the statement to calculate the result correctly will be the following:
  RevQuarter = (float)QuarterSold/150*Revenue_Per_150;

This is exactly what you require. You’re using the right types of variables in the right places. You’re also ensuring you don’t use integer arithmetic when you want to keep the fractional part of the result of a division. An explicit conversion from one type to another is called a cast.

Of course you can cast the result of an expression to another type. In this case, you should put the expression between parentheses, for example:
double result = 0.0;
int a = 5;
int b = 8;
result = (double)(a + b)/2 - (a + b)/(double)(a*a + b*b);

By casting the result of evaluating (a + b) to type double, you ensure that the division by 2 is done as a floating-point operation. The value 2 will be converted to type double, so it is the same type as the left operand for the division operation. Casting the integer result of the divisor, (a*a + b*b), to type double has a similar effect on the second division operation; the value of the left operand will be promoted to type double before the division is executed.

Automatic Conversions

Look at the output from the second version of the program again:
Sales revenue this quarter is :$1930.50
Even without the explicit cast in the expression, the result is in floating-point form, though it is clearly wrong. The result is floating point because binary operators require the operands to be of the same type. The compiler automatically converts one of the operands to be the same type as the other when an operation involves operands of different types. Whenever you use operands in a binary operation that are of different types, the compiler arranges for the value that is of a type with a more limited range to be converted to the type of the other operand. This is called an implicit conversion . So referring back to the expression to calculate revenue:
   QuarterSold / 150 * Revenue_Per_150

It is evaluated as 64400 (int)/150 (int), which equals 429 (int). Then 429, after an implicit conversion from type int to type float, is multiplied by 4.5 (float), giving the result 1930.5 (float).

An implicit conversion always applies when a binary operator involves operands of different types, including different integer types. With the first operation, the numbers are both of type int, so the result is of type int. With the second operation, the first value is type int and the second value is type float. Type int is more limited in its range than type float, so the value of type int is automatically cast to type float. Whenever there is a mixture of types in an arithmetic expression, your compiler will use specific rules to decide how the expression will be evaluated. Let’s have a look at these rules now.

Rules for Implicit Conversions

The mechanism that determines which operand in a binary operation is to be changed to the type of the other is relatively simple. Broadly, it works on the basis that the operand with the type that has the more restricted range of values will be converted to the type of the other operand, although in some instances both operands will be promoted.

To express accurately in words how this works is somewhat more complicated than the description in the previous paragraph, so you may want to ignore the fine detail that follows and refer back to it if you need to. If you want the full story, read on.

The compiler determines the implicit conversion to use by checking the following rules in sequence until it finds one that applies:
  1. 1.

    If one operand is of type long double, the other operand will be converted to type long double.

     
  2. 2.

    Otherwise, if one operand is of type double, the other operand will be converted to type double.

     
  3. 3.

    Otherwise, if one operand is of type float, the other operand will be converted to type float.

     
  4. 4.
    Otherwise, if the operands are both of signed integer types or both of unsigned integer types, the operand of the type of lower rank is converted to the type of the other operand.
    1. a.

      The unsigned integer types are ranked from low to high in the following sequence: signed char, short, int, long, long long.

       
    2. b.

      Each unsigned integer type has the same rank as the corresponding signed integer type, so type unsigned int has the same rank as type int, for example.

       
     
  5. 5.

    Otherwise, if the operand of the signed integer type has a rank that is less than or equal to the rank of the unsigned integer type, the signed integer operand is converted to the unsigned integer type.

     
  6. 6.

    Otherwise, if the range of values the signed integer type can represent includes the values that can be represented by the unsigned integer type, the unsigned operand is converted to the signed integer type.

     
  7. 7.

    Otherwise, both operands are converted to the unsigned integer type corresponding to the signed integer type.

     

Implicit Conversions in Assignment Statements

You can also cause an implicit conversion to be applied when the value of the expression on the right of the assignment operator is a different type from the variable on the left. In some circumstances, this can cause values to be truncated so information is lost. For instance, if an assignment operation stores a value of type float or double to a variable of type int or long, the fractional part of the float or double will be lost, and just the integer part will be stored. The following code fragment illustrates this situation:
int number = 0;
float value = 2.5f;
number = value;

The value stored in number will be 2. Because you’ve assigned the value of value (2.5) to the variable number, which is of type int, the fractional part, .5, will be lost and only the 2 will be stored.

An assignment statement that may lose information because an automatic conversion has to be applied will usually result in a warning from the compiler. However, the code will still compile, so there’s a risk that your program may be doing things that will lead to incorrect results. Generally, it’s better to put explicit casts in your code wherever conversions that may result in information being lost are necessary.

Let’s look at an example to see how the conversion rules in assignment operations work in practice. Look at the following code fragment:
double price = 10.0;                        // Product price per unit
long count = 5L;                            // Number of items
float ship_cost = 2.5F;                     // Shipping cost per order
int discount = 15;                          // Discount as percentage
long double total_cost = (count*price + ship_cost)*((100L - discount)/100.0F);
This defines four variables and computes the total cost of an order from the values of these variables. I chose the types primarily to demonstrate implicit conversions; these types would not represent a sensible choice in normal circumstances. Let’s see what happens in the last statement to produce the value for total_cost:
  1. 1.

    count*price is evaluated first, and count will be implicitly converted to type double to allow the multiplication to take place, and the result will be of type double. This results from the second rule.

     
  2. 2.

    Next, ship_cost is added to the result of the previous operation; and, to make this possible, the value of ship_cost is converted to the type of the previous result, type double. This conversion also results from the second rule.

     
  3. 3.

    Next, the expression 100L - discount is evaluated, and to allow this to occur, the value of discount will be converted to type long, the type of the other operand in the subtraction. This is a result of the fourth rule, and the result will be type long.

     
  4. 4.

    Next, the result of the previous operation (of type long) is converted to type float to allow the division by 100.0F (of type float) to take place. This is the result of applying the third rule, and the result is of type float.

     
  5. 5.

    The result of step 2 is divided by the result of step 4, and to make this possible, the float value from the previous operation is converted to type double. This is a consequence of applying the second rule, and the result is of type double.

     
  6. 6.

    Finally, the previous result is stored in the variable total_cost as a result of the assignment operation. An assignment operation always causes the type of the right operand to be converted to that of the left when the operand types are different, regardless of the types of the operands, so the result of the previous operation is converted to type long double. No compiler warning will occur because all values of type double can be represented as type long double.

     
Caution

If you find that you are having to use a lot of explicit casts in your code, you may have made a poor choice of types for storing the data.

More Numeric Data Types

To complete the basic set of numeric data types, I’ll now cover those that I haven’t yet discussed. The first is one that I mentioned previously: type char. A variable of type char can store the code for a single character. Because it stores a character code, which is an integer, it’s considered to be an integer type. Because it’s an integer type, you can treat a char value just like any other integer so you can use it in arithmetic calculations.

Character Type

Values of type char occupy the least amount of memory of all the data types. They typically require just 1 byte. The integer that’s stored in a variable of type char may be a signed or unsigned value, depending on your compiler. As an unsigned type, the value stored in a variable of type char can range from 0 to 255. As a signed type, a variable of type char can store values from –128 to +127. Of course, both ranges correspond to the same set of bit patterns: from 0000 0000 to 1111 1111. With unsigned values, all 8 bits are data bits, so 0000 0000 corresponds to 0, and 1111 1111 corresponds to 255. With signed values, the leftmost bit is a sign bit, so –128 is the binary value 1000 0000, 0 is 0000 0000, and 127 is 0111 1111. The value 1111 1111 as a signed binary value is the decimal value –1.

From the point of view of representing character codes, which are bit patterns, it doesn’t matter whether type char is regarded as signed or unsigned. Where it does matter is when you perform arithmetic operations with values of type char.

You can specify the initial value for a variable of type char by a character constant. A character constant can be just a character written between single quotes. Here are some examples:
char letter = 'A';
char digit = '9';
char exclamation = '!';
You can use an escape sequence between a pair of single quotes to specify a character constant too:
char newline = ' ';
char tab = ' ';
char single_quote = ''';

Of course, in every case, the variable will be set to the code for the character between single quotes. In principle, the actual code value depends on your computer environment, but by far the most common is American Standard Code for Information Interchange (ASCII). You can find the ASCII character codes in Appendix B.

You can also initialize a variable of type char with an integer value, as long as the value fits into the range for type char with your compiler, as in this example:
char character = 74;                        // ASCII code for the letter J
A variable of type char has a sort of dual personality: you can interpret it as a character or as an integer. Here’s an example of an arithmetic operation with a value of type char:
char letter = 'C';                          // letter contains the decimal code value 67
letter = letter + 3;                        // letter now contains 70, which is 'F'

Thus, you can perform arithmetic on a value of type char and still treat it as a character.

Note

Regardless of whether type char is implemented as a signed or unsigned type, the types char, signed char, and unsigned char are all different and require conversions to map from one of these types to another.

Character Input and Character Output

You can read a single character from the keyboard and store it in a variable of type char using the scanf() function with the format specifier %c, for example:
char ch = 0;
scanf("%c", &ch);                           // Read one character

As we learned earlier, you must add an #include directive for the stdio.h header file to any source file in which you use the scanf() function.

To write a single character to the command line with the printf() function, you use the same format specifier, %c:
printf("The character is %c ", ch);
Of course, you can output the numeric value of a character too:
printf("The character is %c and the code value is %d ", ch, ch);

This statement will output the value in ch as a character and as a numeric value.

Try It Out: Character Building

If you’re completely new to programming, you may be wondering how on earth the computer knows whether it’s dealing with a character or an integer. The reality is that it doesn’t. It’s a bit like when Alice encounters Humpty Dumpty who says, “When I use a word, it means just what I choose it to mean—neither more nor less.” An item of data in memory can mean whatever you choose it to mean. A byte containing the value 70 is a perfectly good integer. It’s equally correct to regard it as the code for the letter F.

Let’s look at an example that should make it clear. Here, you’ll use the conversion specifier %c, which indicates that you want to output a value of type char as a character rather than an integer:
// Program 2.15 Characters and numbers
#include <stdio.h>
int main(void)
{
  char first = 'T';
  char second = 63;
  printf("The first example as a letter looks like this - %c ", first);
  printf("The first example as a number looks like this - %d ", first);
  printf("The second example as a letter looks like this - %c ", second);
  printf("The second example as a number looks like this - %d ", second);
  return 0;
}
The output from this program is the following:
The first example as a letter looks like this - T
The first example as a number looks like this - 84
The second example as a letter looks like this - ?
The second example as a number looks like this - 63

How It Works

The program starts off by declaring two variables of type char:
   char first = 'T';
   char second = 63;

You initialize the first variable with a character constant and the second variable with an integer.

The next four statements output the value of each variable in two ways:
  printf("The first example as a letter looks like this - %c ", first);
  printf("The first example as a number looks like this - %d ", first);
  printf("The second example as a letter looks like this - %c ", second);
  printf("The second example as a number looks like this - %d ", second);

The %c conversion specifier interprets the contents of the variable as a single character, and the %d specifier interprets it as an integer. The numeric values that are output are the codes for the corresponding characters. These are ASCII codes in this instance, and will be in most instances, so that’s what you’ll assume throughout this book.

Tip

As noted earlier, not all computers use the ASCII character set, so you may get different values than those shown previously. As long as you use the character notation for a character constant, you’ll get the character you want regardless of the character coding in effect.

You can also output the integer values of the variables of type char as hexadecimal values by using the format specifier %x instead of %d. You might like to try that.

Try It Out: Arithmetic With Values That are Characters
Let’s look at another example in which you apply arithmetic operations to values of type char:
// Program 2.16 Using type char
#include <stdio.h>
int main(void)
{
  char first = 'A';
  char second = 'B';
  char last = 'Z';
  char number = 40;
  char ex1 = first + 2;                     // Add 2 to 'A'
  char ex2 = second - 1;                    // Subtract 1 from 'B'
  char ex3 = last + 2;                      // Add 2 to 'Z'
  printf("Character values      %-5c%-5c%-5c ", ex1, ex2, ex3);
  printf("Numerical equivalents %-5d%-5d%-5d ", ex1, ex2, ex3);
  printf("The number %d is the code for the character %c ", number, number);
  return 0;
}
When you run the program, you should get the following output:
Character values      C    A    
Numerical equivalents 67   65   92
The number 40 is the code for the character (

How It Works

This program demonstrates how you can happily perform arithmetic with char variables that you’ve initialized with characters. The first three statements in the body of main() are as follows:
  char first = 'A';
  char second = 'B';
  char last = 'Z';

These initialize the variables first, second, and last to the character values you see. The numerical values of these variables will be the ASCII codes for the respective characters. Because you can treat them as numeric values as well as characters, you can perform arithmetic operations with them.

The next statement initializes a variable of type char with an integer value:
  char number = 40;

The initializing value must be within the range of values that a 1-byte variable can store; so with my compiler, where char is a signed type, it must be between -128 and 127. Of course, you can interpret the contents of the variable as a character. In this case, it will be the character that has the ASCII code value 40, which happens to be a left parenthesis.

The next three statements declare three more variables of type char:
  char ex1 = first + 2;                     // Add 2 to 'A'
  char ex2 = second - 1;                    // Subtract 1 from 'B'
  char ex3 = last + 2;                      // Add 2 to 'Z'

These statements create new values and therefore new characters from the values stored in the variables first, second, and last; the results of these expressions are stored in the variables ex1, ex2, and ex3.

The next two statements output the three variables ex1, ex2, and ex3 in two different ways:
  printf("Character values      %-5c%-5c%-5c ", ex1, ex2, ex3);
  printf("Numerical equivalents %-5d%-5d%-5d ", ex1, ex2, ex3);

The first statement interprets the values stored as characters by using the %-5c conversion specifier. This specifies that the value should be output as a character that is left aligned in a field width of five. The second statement outputs the same variables again, but this time interprets the values as integers by using the %-5d specifier. The alignment and the field width are the same, but d specifies the output is an integer. You can see that the two lines of output show the three characters on the first line with their ASCII codes aligned on the line beneath.

The last line outputs the variable number as a character and as an integer:
  printf("The number %d is the code for the character %c ", number, number);

To output the variable value twice, you just write it twice—as the second and third arguments to the printf() function. It’s output first as an integer value and then as a character.

This ability to perform arithmetic with characters can be very useful. For instance, to convert from uppercase to lowercase, you can simply add the result of 'a'-'A' (which is 32 for ASCII) to the uppercase character. To achieve the reverse, just subtract the value of 'a'-'A'. You can see how this works if you have a look at the decimal ASCII values for the alphabetic characters in Appendix B of this book. Of course, this operation depends on the character codes for a–z and A–Z being a contiguous sequence of integers. If this is not the case for the character coding used by your computer, this won’t work.

Note

The standard library ctype.h header provides the toupper() and tolower() functions for converting a character to uppercase or lowercase.

Enumerations

Situations arise quite frequently in programming when you want a variable that will store a value from a very limited set of possible values. One example is a variable that stores a value representing the current month in the year. You really would only want such a variable to be able to assume one of 12 possible values, corresponding to January–December. The enumeration in C is intended specifically for such purposes.

With an enumeration, you define a new integer type where variables of the type have a fixed range of possible values that you specify. Here’s an example of a statement that defines an enumeration type with the name Weekday:
enum Weekday {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};

This statement defines a type—not a variable. The name of the new type, Weekday in this instance, follows the enum keyword, and this type name is referred to as the tag of the enumeration. Variables of type Weekday can have any of the values specified by the names that appear between the braces that follow the type name. These names are called enumerators or enumeration constants , and there can be as many of these as you want. Each enumerator is identified by the unique name you assign, and the compiler will assign a value of type int to each name. An enumeration is an integer type, and the enumerators that you specify will correspond to integer values. By default, the enumerators will start from zero, with each successive enumerator having a value of one more than the previous one. Thus, in this example, the values MondaySunday will have values 0–6.

You could declare a variable of type Weekday and initialize it like this:
enum Weekday today = Wednesday;

This declares a variable with the name today, and it initializes it to the value Wednesday. Because the enumerators have default values, Wednesday will correspond to the value 2. The actual integer type that is used for a variable of an enumeration type is implementation defined, and the choice of type may depend on how many enumerators there are.

It is also possible to declare variables of the enumeration type when you define the type. Here’s a statement that defines an enumeration type plus two variables:
enum Weekday {Monday, Tuesday, Wednesday, Thursday,
                             Friday, Saturday, Sunday} today, tomorrow;
This declares the enumeration type Weekday and two variables of that type, today and tomorrow. Naturally you could also initialize the variable in the same statement, so you could write this:
enum Weekday {Monday, Tuesday, Wednesday, Thursday,
                      Friday, Saturday, Sunday} today = Monday, tomorrow = Tuesday;

This initializes today and tomorrow to Monday and Tuesday, respectively.

Because variables of an enumeration type are of an integer type, they can be used in arithmetic expressions. You could write the previous statement like this:
enum Weekday {Monday, Tuesday, Wednesday, Thursday,
              Friday, Saturday, Sunday} today = Monday, tomorrow = today + 1;

Now the initial value for tomorrow is one more than that of today. However, when you do this kind of thing, it is up to you to ensure that the value that results from the arithmetic is a valid enumerator value.

Note

Although you specify a fixed set of values for an enumeration type, there is no checking mechanism to ensure that only these values are used in your program. It is up to you to ensure that you use only valid values for a given enumeration type. You can do this by only using the names of enumeration constants to assign values to variables.

Choosing Enumerator Values

You can specify your own integer value for any or all of the enumerators explicitly. Although the names you use for enumerators must be unique, there is no requirement for the enumerator values themselves to be unique. Unless you have a specific reason for making some of the values the same, it is usually a good idea to ensure that they are unique. Here’s how you could define the Weekday type so that the enumerator values start from 1:
enum Weekday {Monday = 1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
Now the enumerators MondaySunday will correspond to values 1–7. The enumerators that follow an enumerator with an explicit value will be assigned successive integer values. This can cause enumerators to have duplicate values, as in the following example:
enum Weekday {Monday = 5, Tuesday = 4, Wednesday,
              Thursday = 10, Friday = 3, Saturday, Sunday};

Monday, Tuesday, Thursday, and Friday have explicit values specified. Wednesday will be set to Tuesday+1 so it will be 5, the same as Monday. Similarly, Saturday and Sunday will be set to 4 and 5, so they also have duplicate values. There’s no reason why you can’t do this, although unless you have a good reason for making some of the enumeration constants the same, it does tend to be confusing.

You can use an enumeration in any situation where you want a variable with a specific limited number of possible values. Here’s another example of defining an enumeration:
enum Suit{clubs = 10, diamonds, hearts, spades};
enum Suit card_suit = diamonds;
The first statement defines the enumeration type Suit, so variables of this type can have one of the four values between the braces. The second statement defines a variable of type Suit and initializes it with the value diamonds, which will correspond to 11. You could also define an enumeration to identify card face values like this:
enum FaceValue { two=2, three, four, five, six, seven,
                 eight, nine, ten, jack, queen, king, ace};

In this enumeration, the enumerators will have integer values that match the card values with ace as high.

When you output the value of a variable of an enumeration type, you’ll just get the numeric value. If you want to output the enumerator name, you have to provide the program logic to do this. You’ll be able to do this with what you learn in the next chapter.

Unnamed Enumeration Types

You can create variables of an enumeration type without specifying a tag, so there’s no enumeration type name, for example:
enum {red, orange, yellow, green, blue, indigo, violet} shirt_color;

There’s no tag here, so this statement defines an unnamed enumeration type with the possible enumerators from red to violet. The statement also declares one variable of the unnamed type with the name shirt_color.

You can assign a value to shirt_color in the normal way:
shirt_color = blue;

Obviously, the major limitation on unnamed enumeration types is that you must declare all the variables of the type in the statement that defines the type. Because you don’t have a type name, there’s no way to define additional variables of this type later in the code.

Variables That Store Boolean Values

The type _Bool stores Boolean values. A Boolean value typically arises from a comparison where the result may be true or false; you’ll learn about comparisons and using the results to make decisions in your programs in Chapter 3. The value of a variable of type _Bool can be either 0 or 1, corresponding to the Boolean values false and true, respectively, and because the values 0 and 1 are integers, type _Bool is regarded as an integer type. You declare a _Bool variable just like any other, for example:
_Bool valid = 1;                            // Boolean variable initialized to true

_Bool is not an ideal type name. The name bool would be less clumsy looking and more readable. The Boolean type was introduced into the C language in C99 version, so the type name was chosen to minimize the possibility of conflicts with existing code. If bool had been chosen as the type name, any program that used the name bool for some purpose most probably would not compile with a compiler that supported bool as a built-in type.

Having said that, you can use bool as the type name. You just need to add an #include directive for the standard header file stdbool.h to your source file. As well as defining bool to be the equivalent of _Bool, the header file also defines the symbols true and false to correspond to 1 and 0, respectively. Thus, if you include the header in your source file, you can rewrite the previous declaration as the following:
bool valid = true;                          // Boolean variable initialized to true

This looks much clearer than the previous version, so it’s best to include the stdbool.h header unless you have a good reason not to. I’ll use bool for the Boolean type throughout the rest of the book, but keep in mind that you need the appropriate header to be included and that the fundamental type name is _Bool.

You can cast between Boolean values and other numeric types. A nonzero numeric value will result in 1 (true) when cast to type bool, and 0 will cast to 0 (false). If you use a bool variable in an arithmetic expression, the compiler will insert an implicit conversion where necessary. Type bool has a rank lower than any of the other types, so in an operation involving type bool and a value of another type, it is the bool value that will be converted to the other type. I won’t elaborate further on working with Boolean variables at this point. You’ll learn more about using them in the next chapter.

The op= Form of Assignment

C is a very concise language, and it provides you with abbreviated ways of specifying some operations. Consider the following statement:
number = number + 10;
This sort of assignment, in which you’re incrementing or decrementing a variable, occurs very often, so there’s a shorthand version:
number += 10;
The += operator after the variable name is one example of a family of op= operators. This statement has exactly the same effect as the previous one, and it saves a bit of typing. The op in op= can be any of these arithmetic operators:
+  -  *  /  %
If you suppose number has the value 10, you can write the following statements:
number *= 3;           // number will be set to number*3 which is 30
number /= 3;           // number will be set to number/3 which is 3
number %= 3;           // number will be set to number%3 which is 1
The op in op= can also be a few other operators that you haven’t encountered yet:
 <<  >>  &  ^  |

I’ll defer discussion of these until Chapter 3.

The op= set of operators always works the same way. Here is the general form of statements using op=:
lhs op= rhs;
where rhs represents any expression on the right-hand side of the op= operator. The effect is the same as the following statement form:
lhs = lhs op (rhs);
Note the parentheses around the rhs expression. This means that the right operand of op is the value that results from evaluating the entire rhs expression, whatever it is. Just to reinforce your understanding of this, I’ll show you a few more examples. First, consider this statement:
variable *= 12;
This is the same as
variable = variable * 12;
You now have two different ways of incrementing an integer variable by 1. Both of the following statements increment count by 1:
count = count + 1;
count += 1;

You’ll learn about yet another way to do this in the next chapter. This amazing level of choices tends to make it virtually impossible for indecisive individuals to write programs in C.

Because the op in op= applies to the result of evaluating the rhs expression, the statement
a /= b + 1;
is the same as
a = a/(b + 1);

Your computational facilities have been somewhat constrained so far. You’ve been able to use only a basic set of arithmetic operators. You can put more power in your calculating elbow using a few more standard library facilities. Before I come to the final example in this chapter, I’ll introduce some of the mathematical functions that the standard library offers.

Mathematical Functions

The math.h header file includes declarations for a wide range of mathematical functions. To give you a feel for what’s available, I’ll describe those that are used most frequently. All the functions return a value of type double.

You have the set of functions shown in Table 2-11 available for numerical calculations of various kinds. These all require arguments to be of type double.
Table 2-11.

Functions for Numerical Calculations

Function

Operation

floor(x)

Returns the largest integer that isn’t greater than x as type double

ceil(x)

Returns the smallest integer that isn’t less than x as type double

fabs(x)

Returns the absolute value of x

log(x)

Returns the natural logarithm (base e) of x

log10(x)

Returns the logarithm to base 10 of x

exp(x)

Returns the base e exponential of x

sqrt(x)

Returns the square root of x

pow(x, y)

Returns the value xy

There are also versions of these for types float and long double that have f or l, respectively, appended to the function name, so ceilf() applies to float values and sqrtl() applies to long double values, for example. Here are some examples demonstrating use of the functions presented in Table 2-11:
double x = 2.25;
double less = 0.0;
double more = 0.0;
double root = 0.0;
less = floor(x);                            // Result is 2.0
more = ceil(x);                             // Result is 3.0
root = sqrt(x);                             // Result is 1.5
You also have a range of trigonometric functions available, and some of them are shown in Table 2-12. Those for type float and type long double have f or l, respectively, appended to the name. Arguments and values returned are of type float, type double, or type long double; and angles are expressed in radians.
Table 2-12.

Functions for Trigonometry

Function

Operation

sin(x)

Sine of x expressed in radians

cos(x)

Cosine of x

tan(x)

Tangent of x

If you’re into trigonometry, the use of these functions will be fairly self-evident. Here are some examples:
double angle = 45.0;                        // Angle in degrees
double pi = 3.14159265;
double sine = 0.0;
double cosine = 0.0;
sine = sin(pi*angle/180.0);                 // Angle converted to radians
cosine = cos(pi*angle/180.0);               // Angle converted to radians

Because 180 degrees is the same angle as π radians, dividing an angle measured in degrees by 180 and multiplying by the value of π will produce the angle in radians, as required by these functions.

You also have the inverse trigonometric functions available, asin(), acos(), and atan(), as well as the hyperbolic functions sinh(), cosh(), and tanh(). Don’t forget, you must include math.h into your program if you wish to use any of these functions. If this stuff is not your bag, you can safely ignore this section. Remember to use the flag –lm to the frontend compiler-linker on Linux because most of them will not find the math.h library and it must be declared explicitly in the command line.

Designing a Program

Now it’s time for the end-of-chapter real-life example. This will enable you to try out some of the numeric types. I’ll take you through the basic elements of the process of writing a program from scratch. This involves receiving an initial specification of the problem, analyzing it, preparing a solution, writing the program, and, of course, running and testing the program to make sure it works. Each step in the process can introduce problems, beyond just the theory.

The Problem

The height of a tree is of great interest to many people. For one thing, if a tree is being cut down, knowing its height tells you how far away safe is. This is very important to those with a nervous disposition. Your problem is to find out the height of a tree without using a very long ladder, which itself would introduce risk to life and limb. To find the height of a tree, you’re allowed the help of a friend—preferably a short friend unless you yourself are short, in which case you need a tall friend. You should assume that the tree you’re measuring is taller than both you and your friend. Trees that are shorter than you present little risk, unless they’re of the spiky kind.

The Analysis

Real-world problems are rarely expressed in terms that are directly suitable for programming. Before you consider writing a line of code, you need to be sure you have a complete understanding of the problem and how it’s going to be solved. Only then can you estimate how much time and effort will be involved in creating the solution.

The analysis phase involves gaining a full understanding of the problem and determining the logical process for solving it. Typically this requires a significant amount of work. It involves teasing out any detail in the specification of the problem that is vague or missing. Only when you fully understand the problem can you begin to express the solution in a form that’s suitable for programming.

You’re going to determine the height of a tree using some simple geometry and the heights of two people: you and one other. Let’s start by naming the tall person (you) Lofty and the shorter person (your friend) Shorty. If you’re vertically challenged, the roles can be reversed. For more accurate results, the tall person should be significantly taller than the short person. If they are not, the tall person could consider standing on a box. The diagram in Figure 2-3 will give you an idea of what you’re trying to do in this program.
../images/311070_6_En_2_Chapter/311070_6_En_2_Fig4_HTML.png
Figure 2-3.

The height of a tree

Finding the height of the tree is actually quite simple. You can get the height of the tree, h3, if you know the other dimensions shown in the illustration: h1 and h2, which are the heights of Shorty and Lofty, and d1 and d2, which are the distances between Shorty and Lofty and Lofty and the tree, respectively. You can use the technique of similar triangles to work out the height of the tree. You can see this in the simplified diagram in Figure 2-4.

Here, because the triangles are similar, height1 divided by distance1 is equal to height2 divided by distance2. Using this relationship, you can get the height of the tree from the height of Shorty and Lofty and the distances to the tree, as shown in Figure 2-5.
../images/311070_6_En_2_Chapter/311070_6_En_2_Fig5_HTML.png
Figure 2-4.

Similar triangles

../images/311070_6_En_2_Chapter/311070_6_En_2_Fig6_HTML.png
Figure 2-5.

Calculating the tree height

The triangles ADE and ABC are the same as those shown in Figure 2-4. The triangles are similar, which just means that if you divide the length of any side of one triangle by the length of the corresponding side of the other, you’ll always get the same result. You can use this to calculate the height of the tree, as shown in the equation at the bottom of Figure 2-5.

Thus you can calculate the height of the tree in your program from four values:
  • The distance between Shorty and Lofty, d1 in the diagram. You’ll use the variable shorty_to_lofty to store this value.

  • The distance between Lofty and the tree, d2 in the diagram. You’ll use the variable lofty_to_tree to store this value.

  • The height of Lofty from the ground to the top of his head, h2 in the diagram. You’ll use the variable lofty to store this value.

  • The height of Shorty’s eyes from the ground, h1 in the diagram. You’ll use the variable shorty to store this value.

You can plug these values into the equation for the height of the tree.

Your first task in your program is to read these four values from the keyboard. You can then use your ratios to find out the height of the tree and finally output the answer. The steps are as follows:
  1. 1.

    Read in the values you need.

     
  2. 2.

    Calculate the height of the tree using the equation in Figure 2-5.

     
  3. 3.

    Display the answer.

     

The Solution

This section outlines the programming steps you’ll take to solve the problem.

Step 1

Your first step is to get the values you need. This means you have to include the stdio.h header file, because you will need to use both printf() and scanf(). First, you must define the variables that will store the input values. Then you can use printf() to prompt for the input and scanf() to read the values from the keyboard.

You’ll provide for the heights of the participants to be entered in feet and inches for the convenience of the user. Inside the program, it will be easier to work with all heights and distances in the same units, so you’ll convert all measurements to inches. You’ll need two variables to store the heights of Shorty and Lofty in inches. You’ll also need variables to store the distance between Lofty and Shorty and the distance from Lofty to the tree—both distances in inches.

In the input process, you’ll first read Lofty’s height as a number of whole feet and then the number of inches as a second value, prompting for each value as you go along. You can use two more variables for this: one to store the feet value and the other to store the inches value. You’ll then convert these into just inches and store the result in the variable you’ve reserved for Lofty’s height. You’ll do the same thing for Shorty’s height (which is only up to the height of their eyes), and finally you’ll read in the value for the distance between them. For the distance to the tree, you’ll use only whole feet, because this will be accurate enough—and you’ll convert the distance to inches for calculation purposes. You can reuse the same variables for each measurement in feet and inches that is entered. So here goes with the first part of the program:
// Program 2.17  Calculating the height of a tree
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
  long shorty = 0L;                         // Shorty's height in inches
  long lofty = 0L;                          // Lofty's height in inches
  long feet = 0L;
  long inches = 0L;
  long shorty_to_lofty = 0L;                // Distance from Shorty to Lofty in inches
  long lofty_to_tree = 0L;                  // Distance from Lofty to the tree in inches
  const long inches_per_foot = 12L;
  // Get Lofty's height
  printf("Enter Lofty's height to the top of his/her head, in whole feet: ");
  scanf("%ld", &feet);
  printf("              ...and then inches: ");
  scanf("%ld", &inches);
  lofty = feet*inches_per_foot + inches;
  // Get Shorty's height up to his/her eyes
  printf("Enter Shorty's height up to his/her eyes, in whole feet: ");
  scanf("%ld", &feet);
  printf("                               ... and then inches: ");
  scanf("%ld", &inches);
  shorty = feet*inches_per_foot + inches;
  // Get the distance from Shorty to Lofty
  printf("Enter the distance between Shorty and Lofty, in whole feet: ");
  scanf("%ld", &feet);
  printf("                                        ... and then inches: ");
  scanf("%ld", &inches);
  shorty_to_lofty = feet*inches_per_foot + inches;
  // Get the distance from Lofty to the tree
  printf("Finally enter the distance from Lofty to the tree to the nearest foot: ");
   scanf("%ld", &feet);
  lofty_to_tree = feet*inches_per_foot;
  // The code to calculate the height of the tree will go here
  // The code to display the result will go here
  return 0;
}

Notice how the program code is spaced out to make it easier to read. You don’t have to do this, but if you want to change the program next year, it will make it much easier to see how the program works if it’s well laid out. You should always add comments to your programs to help with this. It’s particularly important to at least make clear what the variables are used for and to document the basic logic of the program.

You use a variable that you’ve declared as const to convert from feet to inches. The variable name, inches_per_foot, makes it reasonably obvious what’s happening when it’s used in the code. This is much better than using the “magic number” 12. Here you’re dealing with feet and inches, and people in the United States or the United Kingdom will be aware that there are 12 inches in a foot. In other countries that use the metric system and in other circumstances, the significance of numeric constants may not be so obvious. If you’re using the value 0.22 in a program calculating salaries, it’s not obvious what this represents. Consequently, the calculation may seem rather obscure. If you use a const variable tax_rate that is initialized to 0.22, then the mist clears. The variables are strongly recommended to be meaningful. There are real-life examples where the physics unit was assumed to be one, but, nevertheless, was implemented a totally different force unit. This happened to Mars Climate Orbiter by confusing pound-force seconds (lbf*s) instead of the SI units of newton-seconds (N*s).

Step 2

Now that you have all the required data, you can calculate the height of the tree. You just need to express the equation for the tree height in terms of your variables. You’ll need to declare another variable to store the height of the tree. You can add the code that’s shown here in bold type to do this:
// Program 2.18  Calculating the height of a tree
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(void)
{
  long shorty = 0L;                         // Shorty's height in inches
  long lofty = 0L;                          // Lofty's height in inches
  long feet = 0L;
  long inches = 0L;
  long shorty_to_lofty = 0L;                // Distance from Shorty to Lofty in inches
  long lofty_to_tree = 0L;                  // Distance from Lofty to the tree in inches
  long tree_height = 0L;                    // Height of the tree in inches
  const long inches_per_foot = 12L;
  // Get Lofty's height
  printf("Enter Lofty's height to the top of his/her head, in whole feet: ");
  scanf("%ld", &feet);
  printf("                                            ...and then inches: ");
  scanf("%ld", &inches);
  lofty = feet*inches_per_foot + inches;
  // Get Shorty's height up to his/her eyes
  printf("Enter Shorty's height up to his/her eyes, in whole feet: ");
  scanf("%ld", &feet);
  printf("                                    ... and then inches: ");
  scanf("%ld", &inches);
  shorty = feet*inches_per_foot + inches;
  // Get the distance from Shorty to Lofty
  printf("Enter the distance between Shorty and Lofty, in whole feet: ");
  scanf("%ld", &feet);
  printf("                                        ... and then inches: ");
  scanf("%ld", &inches);
  shorty_to_lofty = feet*inches_per_foot + inches;
  // Get the distance from Lofty to the tree
  printf("Finally enter the distance from Lofty to the tree to the nearest foot: ");
  scanf("%ld", &feet);
  lofty_to_tree = feet*inches_per_foot;
  // Calculate the height of the tree in inches
  tree_height = shorty + (shorty_to_lofty + lofty_to_tree)*(lofty-shorty)/
                                                                 shorty_to_lofty;
  // The code to display the result will go here
  return 0;
}

The statement to calculate the height is essentially the same as the equation in the diagram. It’s a bit messy, but it translates directly to the statement in the program to calculate the height.

Step 3

Finally, you need to output the height of the tree. To present the results in the most easily understandable form, you can convert the results that you’ve stored in tree_height—which are in inches—back into feet and inches:
// Program 2.18  Calculating the height of a tree
#include <stdio.h>
int main(void)
{
  long shorty = 0L;                         // Shorty's height in inches
  long lofty = 0L;                          // Lofty's height in inches
  long feet = 0L;
  long inches = 0L;
  long shorty_to_lofty = 0L;                // Distance from Shorty to Lofty in inches
  long lofty_to_tree = 0L;                  // Distance from Lofty to the tree in inches
  long tree_height = 0L;                    // Height of the tree in inches
  const long inches_per_foot = 12L;
  // Get Lofty's height
  printf("Enter Lofty's height to the top of his/her head, in whole feet: ");
  scanf("%ld", &feet);
  printf("                                           ... and then inches: ");
  scanf("%ld", &inches);
  lofty = feet*inches_per_foot + inches;
  // Get Shorty's height up to his/her eyes
  printf("Enter Shorty's height up to his/her eyes, in whole feet: ");
  scanf("%ld", &feet);
  printf("                                    ... and then inches: ");
  scanf("%ld", &inches);
  shorty = feet*inches_per_foot + inches;
  // Get the distance from Shorty to Lofty
  printf("Enter the distance between Shorty and Lofty, in whole feet: ");
  scanf("%ld", &feet);
  printf("                                       ... and then inches: ");
  scanf("%ld", &inches);
  shorty_to_lofty = feet*inches_per_foot + inches;
  // Get the distance from Lofty to the tree
  printf("Finally enter the distance from Lofty to the tree to the nearest foot: ");
  scanf("%ld", &feet);
  lofty_to_tree = feet*inches_per_foot;
  // Calculate the height of the tree in inches
  tree_height = shorty + (shorty_to_lofty + lofty_to_tree)*(lofty-shorty)/
                                                                 shorty_to_lofty;
  // Display the result in feet and inches
  printf("The height of the tree is %ld feet and %ld inches. ",
                      tree_height/inches_per_foot, tree_height% inches_per_foot);
return 0;
}
And there you have it. The output from the program looks something like this:
Enter Lofty's height to the top of his/her head, in whole feet first: 6
                                                 ... and then inches: 2
Enter Shorty's height up to his/her eyes, in whole feet: 4
                                    ... and then inches: 6
Enter the distance between Shorty and Lofty, in whole feet : 5
                                        ... and then inches: 0
Finally enter the distance to the tree to the nearest foot: 20
The height of the tree is 12 feet and 10 inches.

Summary

This chapter covered quite a lot of ground. By now, you know how a C program is structured, and you should be fairly comfortable with any kind of arithmetic calculation. You should also be able to choose variable types to suit the job at hand. Aside from arithmetic, you’ve added some input and output capability to your knowledge. You should now feel at ease with inputting values into variables via scanf(). You can output text and the values of character and numeric variables to the screen. You won’t remember it all the first time around, but you can always look back over this chapter if you need to. Not bad for the first two chapters, is it?

In the next chapter, you’ll start looking at how you can control the program by making decisions depending on the values you enter. As you can probably imagine, this is key to creating interesting and professional programs.

Table 2-13 summarizes the variable types you’ve used so far. You can look back at these when you need a reminder as you continue through the book.
Table 2-13.

Variable Types and Typical Value Ranges

Type

Typical number of bytes

Typical range of values

char

1

–128 to +127 or 0 to +255

unsigned char

1

0 to +255

short

2

–32,768 to +32,767

unsigned short

2

0 to +65,535

int

2 or 4

–32,768 to +32,767 or –2,147,438,648 to +2,147,438,647

unsigned int

4

0 to +65,535 or 0 to +4,294,967,295

long

4

–2,147,438,648 to +2,147,438,647

unsigned long

4

0 to +4,294,967,295

long long

8

–9,223,372,036,854,775,808 to +9,223,372,036,854,775,807

unsigned long long

8

0 to +18,446,744,073,709,551,615

float

4

±3.4E±38 (6 digits)

double

8

±1.7E±308 (15 digits)

long double

12

±1.2E±4932 (19 digits)

You have seen and used some of the data output format specifications with the printf() function in this chapter, and you’ll find the complete set described in Appendix D, which also describes the input format specifiers you use to control how data are interpreted when they are read from the keyboard by the scanf() function. Whenever you are unsure about how you deal with a particular kind of data for input or output, just look in Appendix D.

Exercises

The following exercises enable you to try out what you’ve learned in this chapter. If you get stuck, look back over the chapter for help. If you’re still stuck, you can download the solutions from the Source Code/Download section of the Apress website (www.apress.com), but that really should be a last resort.

Exercise 2-1. Write a program that prompts the user to enter a distance in inches and then outputs that distance in yards, feet, and inches. (For those unfamiliar with imperial units, there are 12 inches in a foot and 3 feet in a yard.)

Exercise 2-2. Write a program that prompts for input of the length and width of a room in feet and inches and then calculates and outputs the floor area in square yards with two decimal places after the decimal point.

Exercise 2-3. You’re selling a product that’s available in two versions: type 1 is a standard version priced at $3.50, and type 2 is a deluxe version priced at $5.50. Write a program using only what you’ve learned up to now that prompts for the user to enter the product type and a quantity and then calculates and outputs the price for the quantity entered.

Exercise 2-4. Write a program that prompts for the user’s weekly pay in dollars and the hours worked to be entered through the keyboard as floating-point values. The program should then calculate and output the average pay per hour in the following form:
Your average hourly pay rate is 7 dollars and 54 cents.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.70.203