In This Chapter
The most fundamental of all concepts in C++ is the variable— a variable is like a small box. You can store things in the box for later use, particularly numbers. The concept of a variable is borrowed from mathematics. A statement such as
x = 1
stores the value 1 in the variable x
. From that point forward, the mathematician can use the variable x
in place of the constant 1 — until he changes the value of x
to something else.
Variables work the same way in C++. You can make the assignment
x = 1;
From that point forward in the execution of the program, until the value of x is changed, the value of x is 1. References to x are replaced by the value 1. In this chapter, you will find out how to declare and initialize variables in C++ programs. You will also see the different types of variables that C++ defines and when to use each.
A mathematician might write something like the following:
(x + 2) = y / 2 x + 4 = y solve for x and y
Any reader who's had algebra realizes right off that the mathematician has introduced the variables x
and y
. But C++ isn't that smart. (Computers may be fast, but they're stupid.)
You have to announce each variable to C++ before you can use it. You have to say something soothing like this:
int x; x = 10; int y; y = 5;
These lines of code declare that a variable x
exists, is of type int
, and has the value 10; and that a variable y
of type int
also exists with the value 5. (The next section discusses variable types.) You can declare variables (almost) anywhere you want in your program — as long as you declare the variable before you use it.
If you're on friendly terms with math (and who isn't?), you probably think of a variable in mathematics as an amorphous box capable of holding whatever you might choose to store in it. You might easily write something like the following:
x = 1 x = 2.3 x = "this is a sentence"
Alas, C++ is not that flexible. (On the other hand, C++ can do things that people can't do, such as add a billion numbers or so in a second, so let's not get too uppity.) To C++, there are different types of variables just as there are different types of storage bins. Some storage bins are so small that they can handle only a single number. It takes a larger bin to handle a sentence.
Some computer languages try harder to accommodate the programmer by allowing her to place different types of data in the same variable. These languages are called weakly typed languages. C++ is a strongly typed language — it requires the programmer to specifically declare each variable along with its exact type.
The variable type int
is the C++ equivalent of an integer — a number that has no fractional part. (Integers are also known as counting numbers or whole numbers.)
Integers are great for most calculations. I made it through most of elementary school with integers. It isn't until I turned 11 or so that my teachers started mucking up the waters with fractions. The same is true in C++: More than 90 percent of all variables in C++ are declared to be of type int
.
Unfortunately, int
variables aren't adapted to every problem. For example, if you worked through the temperature-conversion program in Chapter 1, you might have noticed that the program has a potential problem — it can calculate temperatures to the nearest degree. No fractions of a degree are allowed. This integer limitation wouldn't affect daily use because it isn't likely that someone (other than a meteorologist) would get all excited about being off a fraction of a degree. There are plenty of cases, however, where this isn't the case — for example, you wouldn't want to come up a half mile short of the runway on your next airplane trip due to a navigational round-off.
The int
variable type is the C++ version of an integer. int
variables suffer the same limitations as their counting-number integer equivalents in math do.
Lopping off the fractional part of a number is called truncation. Consider the problem of calculating the average of three numbers. Given three int
variables — nValue1, nValue2
, and nValue3
— an equation for calculating the average is
int nAverage; int nValue1; int nValue2; int nValue3; nAverage = (nValue1 + nValue2 + nValue3) / 3;
Because all three values are integers, the sum is assumed to be an integer. Given the values 1, 2, and 2, the sum is 5. Divide that by 3, and you get 1⅔, or 1.666. C++ uses slightly different rules: Given that all three variables nValue1, nValue2
, and nValue3
are integers, the sum is also assumed to be an integer. The result of the division of one integer by another integer is also an integer. Thus, the resulting value of nAverage
is the unreasonable but logical value of 1.
The problem is much worse in the following mathematically equivalent formulation:
int nAverage; int nValue1; int nValue2; int nValue3; nAverage = nValue1/3 + nValue2/3 + nValue3/3;
Plugging in the same 1, 2, and 2 values, the resulting value of nAverage
is 0 (talk about logical but unreasonable). To see how this can occur, consider that ⅓ truncates to 0, ⅔ truncates to 0, and ⅔ truncates to 0. The sum of 0, 0, and 0 is 0. You can see that integer truncation can be completely unacceptable.
A second problem with the int
variable type is its limited range. A normal int
variable can store a maximum value of 2,147,483,647 and a minimum value of −2,147,483,648 — roughly from positive 2 billion to negative 2 billion, for a total range of about 4 billion.
Two billion is a very large number: plenty big enough for most uses. But it's not large enough for some applications, including computer technology. In fact, your computer probably executes faster than 2 gigahertz, depending on how old your computer is. (Giga is the prefix meaning billion.) A single strand of communications fiber — the kind that's been strung back and forth from one end of the country to the other — can handle way more than 2 billion bits per second.
The limitations of int
variables can be unacceptable in some applications. Fortunately, C++ understands decimal numbers that have a fractional part. (Mathematicians also call those real numbers.) Decimal numbers avoid many of the limitations of int
type integers. To C++ all decimal numbers have a fractional part even if that fractional part is 0. In C++, the number 1.0 is just as much a decimal number as 1.5. The equivalent integer is written simply as 1. Decimal numbers can also be negative, such as −2.3.
When you declare variables in C++ that are decimal numbers, you identify them as floating-point or simply float
variables. The term floating-point means the decimal point is allowed to float back and forth, identifying as many decimal places as necessary to express the value. Floating-point variables are declared in the same way as int
variables:
float fValue1;
Once declared, you cannot change the type of a variable. fValue1
is now a float
and will be a float
for the remainder of the program. To see how floating-point numbers fix the truncation problem inherent with integers, convert all the int
variables to float
. Here's what you get:
float fValue; fValue = 1.0/3.0 + 2.0/3.0 + 2.0/3.0;
is equivalent to
fValue = 0.333... + 0.666... + 0.666...;
which results in the value
fValue = 1.666...;
I have written the value 1.6666 ...
as if the number of trailing 6s goes on forever. This is not necessarily the case. A float
variable has a limit to the number of digits of accuracy, but it's a lot more than I can keep track of.
A constant that has a decimal point is assumed to be a floating-point value. However, the default type for a floating-point constant is something known as a double precision, which in C++ is called simply double
, as we'll see in the next section.
The programs IntAverage
and FloatAverage
are available on the enclosed CD in the CPP_ProgramsChap02
directory to demonstrate the round-off error inherent in integer variables.
Although floating-point variables can solve many calculation problems such as truncation, they have some limitations themselves — the reverse of those associated with integer variables. Floating-point variables can't be used to count things, are more difficult for the computer to handle, and also suffer from round-off error (though not nearly to the same degree as int
variables).
You cannot use floating-point variables in applications where counting is important. This includes C++ constructs that count. C++ can't verify which whole number value is meant by a given floating-point number.
For example, it's clear to you and me that 1.0 is 1 but not so clear to C++. What about 0.9 or 1.1? Should these also be considered as 1? C++ simply avoids the problem by insisting on using int
values when counting is involved.
Historically, a computer processor can process integer arithmetic quicker than it can floating-point arithmetic. Thus, while a processor can add 1 million integer numbers in a given amount of time, the same processor may be able to perform only 200,000 floating-point calculations during the same period.
Calculation speed is becoming less of a problem as microprocessors get faster. In addition, today's general-purpose microprocessors include special floating-point circuitry on board to increase the performance of these operations. However, arithmetic on integer values is just a heck of a lot easier and faster than performing the same operation on floating-point values.
Floating-point float
variables have a precision of about 6 digits, and an extra-economy size, double-strength version of float
known as a double
can handle about 13 significant digits. This can cause round-off problems as well.
Consider that ⅓ is expressed as 0.333 ... in a continuing sequence. The concept of an infinite series makes sense in math but not to a computer because it has a finite accuracy. The FloatAverage
program outputs 1.66667 as the average 1, 2, and 2 — that's a lot better than the 0 output by the IntAverage
version but not even close to an infinite sequence.
C++ can correct for round-off error in a lot of cases. For example, on output, C++ can sometimes determine that the user really meant 1 instead of 0.999999. In other cases, even C++ cannot correct for round-off error.
Although the double
data type has a range much larger than that of an integer, it's still limited. The maximum value for an int
is a skosh more than 2 billion. The maximum value of a double
variable is roughly 10 to the 38th power. That's 1 followed by 38 zeroes; it eats 2 billion for breakfast. (It's even more than the national debt, at least at the time of this writing.)
Remember, only the first 13 digits or so have any meaning; the remaining 25 digits are noise having succumbed to floating-point round-off error.
So far in this chapter, I have been trumpeting that variables must be declared and that they must be assigned a type. Fortunately (ta-dah!), C++ provides a number of variable types. See Table 2-1 for a list of variables, their advantages, and limitations.
Table 2.1. Common C++ Variable Types
Variable | Defining a Constant | What It Is |
---|---|---|
|
| A simple counting number, either positive or negative. |
|
| A potentially smaller version of int. It uses less memory but has a smaller range. |
|
| A potentially larger version of int. There is no difference between long and int with gcc. |
|
| A potentially even larger version of int. |
|
| A single precision real number. This smaller version takes less memory than a double but has less accuracy and a smaller range. |
|
| A standard floating-point variable. |
|
| A potentially larger floating-point number. On the PC, long double is used for the native size of the 80×86 floating-point processor, which is 80 bits. |
|
| A single char variable stores a single alphabetic or digital character. Not suitable for arithmetic. |
|
| A larger character capable or storing symbols with larger character sets like Chinese. |
|
| A string of characters forms a sentence or phrase. |
|
| The only other value is false. No, I mean, it's really false. Logically false. Not false as in fake or ersatz or ... never mind. |
The long long int
and long double
were officially introduced with C++ '09.
The integer types come in both signed and unsigned versions. Signed is always the default (for everything except char
and wchar_t
). The unsigned version is created by adding the keyword unsigned
in front of the type in the declaration. The unsigned constants include a U or u in their type designation. Thus, the following declares an unsigned int
variable and assigns it the value 10:
unsigned int uVariable; uVariable = 10U;
The following statement declares the two variables lVariable1
and lVariable2
as type long int
and sets them equal to the value 1, while dVariable
is a double set to the value 1.0. Notice in the declaration of lVariable2
that the int
is assumed and can be left off:
// declare two long int variables and set them to 1 long int lVariable1 long lVariable2; // int is assumed lVariable1 = lVariable2 = 1; // declare a variable of type double and set it to 1.0 double dVariable; dVariable = 1.0;
You can declare a variable and initialize it in the same statement:
int nVariable = 1; // declare a variable and // initialize it to 1
A char
variable can hold a single character; a character string (which isn't really a variable type but works like one for most purposes) holds a string of characters. Thus, 'C'
is a char
that contains the character C, whereas "C"
is a string with one character in it. A rough analogy is that a 'C'
corresponds to a nail in your hand, whereas "C"
corresponds to a nail gun with one nail left in the magazine. (Chapter 9 describes strings in detail.)
If an application requires a string, you've gotta provide one, even if the string contains only a single character. Providing nothing but the character just won't do the job.
A constant value is an explicit number or character (such as 1, 0.5, or 'c') that doesn't change. As with variables, every constant has a type. In an expression such as n = 1;
the constant value 1 is an int
. To make 1 a long
integer, write the statement as n = 1L;
. The analogy is as follows: 1 represents a pickup truck with one ball in it, whereas 1L
is a dump truck also with one ball. The number of balls is the same in both cases, but the capacity of one of the containers is much larger.
Following the int
to long
comparison, 1.0 represents the value 1 but in a floating-point container. Notice, however, that the default for floating-point constants is double
. Thus, 1.0 is a double
number and not a float
.
The constant values true
and false
are of type bool
. In keeping with C++'s attention to case, true
is a constant but TRUE
has no meaning.
A variable can be declared constant when it is created via the keyword const
:
const double PI = 3.14159; // declare a constant variable
A const
variable must be initialized with a value when it is declared, and its value cannot be changed by any future statement.
Variables declared const
don't have to be named with all capitals, but by convention they often are. This is just a hint to the reader that this so-called variable is, in fact, not.
I admit that it may seem odd to declare a variable and then say that it can't change. Why bother? Largely because carefully named const
variables can make a program a lot easier to understand. Consider the following two equivalent expressions:
double dC = 6.28318 * dR; // what does this mean? double dCircumference = 2 * PI * dRadius; // this is a // lot easier to understand
It should be a lot clearer to the reader of this code that the second expression is multiplying the radius of something by 2π to calculate the circumference.
It may seem odd but the C++ standard doesn't say exactly how big a number each of the data types can accommodate. The standard speaks only to the relative size of each data type. For example, it says that the maximum long int
is at least as large as the maximum int
.
The authors of C++ weren't trying to be mysterious. They merely wanted to allow the compiler to implement the absolute fastest code possible for the base machine. The standard was designed to work for all different types of processors running different operating systems.
However, it is useful to know the limits for your particular implementation. Table 2-2 shows the size of each number type on a Windows PC using the Code::Blocks/gcc compiler that comes on the enclosed CD-ROM.
Table 2.2. Range of Numeric Types in Code::Block/gcc
Variable | Size (bytes) | Accuracy | Range |
---|---|---|---|
| 2 | exact | −32768 to 32767 |
| 4 | exact | −2,147,483,648 to 2,147,483,647 |
| 4 | exact | −2,147,483,648 to 2,147,483,647 |
| 8 | exact | –9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 |
| 4 | 7 digits | ±3.4028 × 10±38 |
| 8 | 16 digits | ±1.7977 × 10±308 |
| 12 | 19 digits | ±1.1897 × 10±4932 |
Attempting to calculate a number that's beyond the range of its type is known as an overflow. The C++ standard generally leaves the results of an overflow as undefined. That's another way that the definers of C++ remained flexible.
On the PC, a floating-point overflow results in an exception, which if not handled will cause your program to crash. (I don't discuss exceptions until Chapter 24.) As bad as that sounds, an integer overflow is worse — C++ silently generates an incorrect value without complaint.
You can store any printable character you want in a char
or string
variable. You can also store a set of nonprintable characters that are used as character constants. See Table 2-3 for a description of these important nonprintable characters.
Table 2.3. Special Characters
Character Constant | What It Is |
---|---|
| newline |
| tab |
| The character whose value is 40 in octal (see Chapter 4 for a discussion of number systems) |
| The character whose value is 20 in hexadecimal (this is the same as ' 40') |
| null (i.e., the character whose value is 0) |
| backslash |
You have already seen the newline character at the end of strings. This character breaks a string and puts the parts on separate lines. A newline character may appear anywhere within a string. For example:
"This is line 1 This is line 2"
appears on the output as
This is line 1 This is line 2
Similarly, the
tab character moves output to the next tab position. (This position can vary, depending on the type of computer you're using to run the program.)
The numerical forms allow you to specify any nonprinting character that you like, but results may vary. The character represented by 0xFB
, for example, depends on the font and the character set (and may not be a legal character at all).
Because the backslash character is used to signify special characters, a character pair for the backslash itself is required. The character pair \
represents the backslash.
The standard char
variable is a scant 1 byte wide and can handle only 255 different characters. This is plenty enough for European languages but not big enough to handle symbol-based languages such as kanji.
Several standards have arisen to extend the character set to handle the demands of these languages. UTF-8 uses a mixture of 8-, 16-, and 32-bit characters to implement almost every kanji or hieroglyph you can think of but still remain compatible with simple 8-bit ASCII. UTF-16 uses a mixture of 16- and 32-bit characters to achieve an expanded character set, and UTF-32 uses 32 bits for all characters.
UTF stands for Unicode Transformation Format, from which it gets the common nickname Unicode.
Table 2-4 describes the different character types supported by C++. At first, C++ tried to get by with a vaguely defined wide character type, wchar_t
. This type was intended to be the wide character type native to the application program's environment. C++ '09 introduced specific types for UTF-16 and UTF-32.
UTF-16 is the standard encoding for Windows and .NET applications. The wchar_t
type refers to UTF-16 in the Code::Blocks/gcc compiler included on the CD-ROM.
Table 2.4. The C++ Character Types
Variable | Example | What It Is |
---|---|---|
|
| ASCII character |
|
| Character in wide format |
|
| UTF-16 character |
|
| UTF-32 character |
Any of the character types in Table 2-4 can be combined into strings as well:
wchar_t* wideString = L"this is a wide string";
(Ignore the asterisk for now. I have a lot to say about its meaning in Chapter 8.)
C++ provides a logical variable called bool
. The type bool
comes from Boole, the last name of the inventor of the logical calculus. A Boolean variable has two values: true
and false
.
There are actually calculations that result in the value bool
. For example, "x is equal to y"
is either true
or false
.
C++ allows you to mix variable types in a single expression. That is, you are allowed to add an integer with a double
precision floating-point value. In the following expression, for example, nValue1
is allowed to be an int
:
// in the following expression the value of nValue1 // is converted into a double before performing the // assignment int nValue1 = 1; nValue1 + 1.0;
An expression in which the two operands are not the same type is called a mixed-mode expression. Mixed-mode expressions generate a value whose type is equal to the more capable of the two operands. In this case, nValue1
is converted to a double
before the calculation proceeds. Similarly, an expression of one type may be assigned to a variable of a different type, as in the following statement:
// in the following assignment, the whole // number part of fVariable is stored into nVariable double dVariable = 1.0; int nVariable; nVariable = dVariable;
You can lose precision or range if the variable on the left side of the assignment is smaller. In the preceding example, C++ truncates the value of dVariable
before storing it in nVariable
.
Converting a larger value type into a smaller value type is called demotion, whereas converting values in the opposite direction is known as promotion. Programmers say that the value of int
variable nVariable1
is promoted to a double
in expressions such as the following:
int nVariable1 = 1; double dVariable = nVariable1;
Mixed-mode expressions are not a good idea. Avoid forcing C++ to do your conversions for you.
This entire section works only for C++ '09.
If you are really lazy, you can let C++ determine the types of your variables for you. Consider the following declaration:
int nVar = 1;
You might ask, "Why can't C++ figure out the type of nVar
?" The answer is, as of C++ '09, it will if you ask nicely, as follows:
auto var1 = 1; auto var2 = 2.0;
This says, "declare var1
to be a variable of the same type as the constant value 1 (which happens to be an int
) and declare var2
to be the same type as 2.0 (which is a double
)."
I consider the term auto
to be a particularly unfortunate choice for this purpose because prior to C++ '09, the keyword auto
had a completely different meaning. However, auto
had fallen out of use for at least ten years, so the standards people figured that it would be safe to usurp the term. Just be aware that if you see the keyword auto
in some old code, you will need to remove it.
You can also tell C++ that you want a variable to be declared to be of the same type as another variable, whatever that might be, using the keyword decltyple()
.
int var1; decltype(var1) var2; // declare var2 to be of the // same type as var1
C++ replaces the decltype(var1)
with the type of var1
, again an int
.
3.147.83.176