C Programming For Beginners (2015)
CHAPTER 2
C – The Basics
In this chapter, we will explain the following:
- What is an alphabet, a character set and a token
- What is a syntax rule and a syntax error
- What is a reserved word
- How to create identifiers in C
- What is a symbolic constant
- The C data types—int, floatand double
- How to write intand double expressions
- How to print an integer using a field width
- How to print a floating-point number to a required number of decimal places
- What happens when intand double values are mixed in the same expression
- What happens when we assign intto double and double to int
- How to declare a variable to hold a string
- How to assign a string value to a string variable
- Some problems to avoid when using the assignment statement
2.1 Introduction
In this chapter, we discuss some basic concepts you need to know in order to write programs in the C programming language.
A programming language is similar to speaking languages in many respects. It has an alphabet (more commonly referred to as a character set) from which everything in the language is constructed. It has rules for forming words(also called tokens), rules for forming statements and rules for forming programs. These are called the syntax rules of the language and must be obeyed when writing programs. If you violate a rule, your program will contain a syntax error. When you attempt to compile the program, the compiler will inform you of the error. You must correct it and try again.
The first step to becoming a good programmer is learning the syntax rules of the programming language. This is the easy part and many people mistakenly believe that this makes them a programmer. It is like saying that learning some rules of English grammar and being able to write some correctly formed sentences makes one a novelist. Novel-writing skills require much more than learning some rules of grammar. Among other things, it requires insight, creativity and a knack for using the right words in a given situation.
In the same vein, a good programmer must be able to creatively use the features of the language to solve a wide variety of problems in an elegant and efficient manner. This is the difficult part and can be achieved only by long, hard study of problem-solving algorithms and writing programs to solve a wide range of problems. But we must start with baby steps.
2.2 The C Alphabet
In Section 1.4 we introduced the idea of a character. We can think of the C alphabet as consisting of all the characters one could type on a standard English keyboard, for example, the digits, uppercase and lowercase letters, and special characters such as +, =, <, >, & and %.
More formally, C uses the ASCII (American Standard Code for Information Interchange, pronounced ass-key) character set. This is a character standard which includes the letters, digits and special characters found on a standard keyboard. It also includes control characters such as backspace, tab, line feed, form feed and carriage return. Each character is assigned a numeric code. The ASCII codes run from 0 to 127.
The programs in this book will be written using the ASCII character set. The characters in the ASCII character set are shown in Appendix B.
Character handling will be discussed in detail in Chapter 6.
2.3 C Tokens
The tokens of a language are the basic building blocks which can be put together to construct programs. A token can be a reserved word (such as int or while), an identifier (such as b or sum), a constant (such as 25 or "Alice in Wonderland"), a delimiter (such as } or ;) or an operator (such as + or =).
For example, consider the following portion of Program P1.4 given at the end of the last chapter:
int main() {
int a, b, sum;
a = 14;
b = 25;
sum = a + b;
printf("%d + %d = %d\n", a, b, sum);
}
Starting from the beginning, we can list the tokens in order:
token |
type |
int |
reserved word |
main |
identifier |
( |
left bracket, delimiter |
) |
right bracket, delimiter |
{ |
left brace, delimiter |
int |
reserved word |
a |
identifier |
, |
comma, delimiter |
b |
identifier |
, |
comma, delimiter |
sum |
identifier |
; |
semicolon, delimiter |
a |
identifier |
= |
equals sign, delimiter |
14 |
constant |
; |
semicolon, delimiter |
and so on. Therefore we can think of a program as a stream of tokens, which is precisely how the compiler views it. So that, as far as the compiler is concerned, the above could have been written like this:
int main() { int a, b, sum;
a = 14; b = 25; sum = a + b;
printf("%d + %d = %d\n", a, b, sum); }
The order of the tokens is exactly the same; to the compiler, it is the same program. To the computer, only the order of the tokens is important. However, layout and spacing are important to make the program more readable to human beings.
2.3.1 Spacing Within a Program
Generally speaking, C programs can be written using “free format”. The language does not require us, for instance, to write one statement on a line. Even a simple statement like
a = 14;
can be written on four separate lines, like this:
a
=
14
;
Only the order of the tokens is important. However, since 14 is one token, the 1 cannot be separated from the 4. You are not even allowed to put a space between 1 and 4.
Except within a string or character constant, spaces are not significant in C. However, judicious use of spaces can dramatically improve the readability of your program. A general rule of thumb is that wherever you can put one space, you can put any number of spaces without affecting the meaning of your program. The statement
a = 14;
can be written as
a=14;
or
a = 14 ;
or
a= 14;
The statement
sum = a + b;
can be written as
sum=a+b;
or
sum= a + b ;
or
sum = a+b;
Note, of course, that you cannot have spaces within the variable sum. It would be wrong to write s um or su m. In general, all the characters of a token must stay together.
2.3.2 Reserved Words
The C language uses a number of keywords such as int, char and while. A keyword has a special meaning in the context of a C program and can be used for that purpose only. For example, int can be used only in those places where we need to specify that the type of some item is integer. All keywords are written in lowercase letters only. Thus int is a keyword but Int and INT are not. Keywords are reserved, that is, you cannot use them as youridentifiers. As such, they are usually called reserved words. A list of C keywords is given in Appendix A.
2.3.3 Identifiers
The C programmer needs to make up names for things such as variables, function names (Chapter 7) and symbolic constants (see next section). A name that he makes up is called a user identifier. There are a few simple rules to follow in naming an identifier:
- It must start with a letter or underscore.
- If other characters are required, they can be any combination of letters, digits or underscore.
The length of an identifier cannot exceed 63 characters.
Examples of valid identifiers:
r
R
sumOfRoots1and2
_XYZ
maxThrowsPerTurn
TURNS_PER_GAME
R2D2
root1
Examples of invalid identifiers:
2hotToHandle // does not start with a letter
Net Pay // contains a space
ALPHA;BETA //contains an invalid character ;
Important points to note:
- Spaces are not allowed in an identifier. If you need one which consists of two or more words, use a combination of uppercase and lowercase letters (as in numThrowsThisTurn) or use the underscore to separate the words (as in num_throws_this_turn). We prefer the uppercase/lowercase combination.
- In general, C is case-sensitive(an uppercase letter is considered different from the corresponding lowercase letter). Thus r is a different identifier from R. And sum is different from Sum is different from SUM is different from SuM.
- You cannot use a C reserved word as one of your identifiers.
2.3.4 Some Naming Conventions
Other than the rules for creating identifiers, C imposes no restriction on what names to use, or what format (uppercase or lowercase, for instance) to use. However, good programming practice dictates that some common-sense rules should be followed.
An identifier should be meaningful. For example, if it’s a variable, it should reflect the value being stored in the variable; netPay is a much better variable than x for storing someone’s net pay, even though both are valid. If it’s a function (Chapter 7), it should give some indication of what the function is supposed to do; playGame is a better identifier than plg.
It is a good idea to use upper and lower case combinations to indicate the kind of item named by the identifier. In this book, we use the following conventions:
- A variableis normally written in lowercase, for example, sum. If we need a variable consisting of two or more words, we start the second and subsequent words with an uppercase letter, for example, voteCount or sumOfSeries.
- A symbolic(or named) constant is an identifier which can be used in place of a constant such as 100. Suppose 100 represents the maximum number of items we wish to process in some program. We would probably need to use the number 100 in various places in the program. But suppose we change our mind and want to cater for 500 items. We would have to change all occurrences of 100 to 500. However, we would have to make sure that we do not change an occurrence of 100 used for some purpose other than the maximum number of items (in a calculation like principal*rate/100, say).
- To make it easy to change our mind, we can set the identifier MaxItemsto 100 and use MaxItems whenever we need to refer to the maximum number of items. If we change our mind, we would only need to set MaxItems to the new value. We will begin a symbolic constant with an uppercase letter. If it consists of more than one word, we will begin each word with uppercase, as in MaxThrowsPerTurn.
- We will see how to use symbolic constants in Section 4.5.
2.4 Basic Data Types
In Section 1.3 we briefly touched on the concept of a data type. For most of this book, we will use the following data types:
int, double and char
These, among others, are referred to as primitive data types.
Each data type defines constants of that type. When we declare a variable to be of a particular type, we are really saying what kind of constants (values) can be stored in that variable. For example, if we declare the variable num to be int, we are saying that the value of num at any time can be an integer constant such as 25, -369 or 1024.
2.5 Integer Numbers - int
An int variable is used to store an integer (whole number) value. An integer value is one of 0, ±1, ±2, ±3, ±4, etc. However, on a computer, the largest and smallest integers which can be stored are determined by the number of bitsused to store an integer. Appendix C shows how integers can be represented on a computer.
Typically, an int variable occupies 16 bits (2 bytes) and can be used to store whole numbers in the range -32,768 to +32,767. Note, however, that on some machines, an int could occupy 32 bits, in which case it can store whole numbers from -2,147,483,648 to +2,147,483,647. In general, if n bits are used to store an int, the range of numbers which can be stored is -2n-1 to +2n-1 - 1.
As an exercise, find out the largest and smallest int values on your computer.
2.5.1 Declaring Variables
In C, a variable is declared by specifying a type name followed by the variable. For example,
int h;
declares h to be a variable of type int.
You can declare several variables of the same type in one statement as in:
int a, b, c; // declares 3 variables of type int
The variables are separated by commas, with a semicolon after the last one.
You can declare a variable and give it an initial value in one statement, as in:
int h = 14;
This declares h to be int and gives it a value of 14.
2.5.2 Integer Expressions
An integer constant is written in the manner we are all accustomed to, for example, 354, 639, -1, 30705 and -4812. Note that you can use only a possible sign followed by digits from 0 to 9. In particular, you cannot use commas as you might do to separate thousands; thus 32,732 is an invalid integer constant—you must write it as 32732.
An integer expression can be written using the following arithmetic operators:
+ |
add |
− |
subtract |
* |
multiply |
/ |
divide |
% |
find remainder |
For example, suppose we have the following declaration:
int a, b, c;
then the following are all valid expressions:
a + 39
a + b - c * 2
b % 10 //the remainder when b is divided by 10
c + (a * 2 + b * 2) / 2
The operators +, - and * all give the expected results. However, / performs integer division; if there is any remainder, it is thrown away. We say integer division truncates. Thus 19/5 gives the value 3; the remainder 4 is discarded.
But what is the value of -19/5? The answer here is –3. The rule is that, in C, integer division truncates towards zero. Since the exact value of –19 ÷ 5 is –3.8, truncating towards zero gives –3. (In the next section, we show how to get the precise value for the division of one integer by another.)
The % operator gives the remainder when one integer is divided by another. For example,
19 % 5 evaluates to 4;
h % 7 gives the remainder when h is divided by 7;
You can use it to test, for instance, if a number h is even or odd. If h % 2 is 0 then h is even; if h % 2 is 1, h is odd.
2.5.3 Precedence of Operators
C evaluates an expression based on the usual precedence of operators: multiplication and division are done before addition and subtraction. We say that multiplication and division have higher precedence than addition and subtraction. For example, the expression
5 + 3 * 4
is evaluated by first multiplying 3 by 4 (giving 12) and then adding 5 to 12, giving 17 as the value of the expression.
As usual, we can use brackets to force the evaluation of an expression in the order we want. For example,
(5 + 3) * 4
first adds 5 and 3 (giving 8), and then multiplies 8 by 4, giving 32.
When two operators which have the same precedence appear in an expression, they are evaluated from left to right, unless specified otherwise by brackets. For example,
24 / 4 * 2
is evaluated as
(24 / 4) * 2
(giving 12) and
12 - 7 + 3
is evaluated as
(12 - 7) + 3
giving 8. However,
24 / (4 * 2)
is evaluated as expected, giving 3, and
12 - (7 + 3)
is evaluated as expected, giving 2.
In C, the remainder operator % has the same precedence as multiplication (*) and division (/).
Exercise: What is printed by the following program? Verify your answer by typing and running the program.
#include <stdio.h>
int main() {
int a = 15;
int b = 24;
printf("%d %d\n", b - a + 7, b - (a + 7));
printf("%d %d\n", b - a - 4, b - (a - 4));
printf("%d %d\n", b % a / 2, b % (a / 2));
printf("%d %d\n", b * a / 2, b * (a / 2));
printf("%d %d\n", b / 2 * a, b / (2 * a));
}
2.5.4 Print an Integer Using a "Field Width"
We have seen that we can print an integer value by specifying the value (either by a variable or an expression) in a printf statement. When we do so, C prints the value using as many “print columns” as needed. For instance, if the value is 782, it is printed using 3 print columns since 782 has 3 digits. If the value is -2345, it is printed using 5 print columns (one for the minus sign).
While this is usually sufficient for most purposes, there are times when it is useful to be able to tell C how many print columns to use. For example, if we want to print the value of n in 5 print columns, we can do this by specifying a field width of 5, as in:
printf("%5d", n);
Instead of the specification %d, we now use %5d. The field width is placed between % and d. The value of n is printed “in a field width of 5”.
Suppose n is 279; there are 3 digits to print so 3 print columns are needed. Since the field width is 5, the number 279 is printed with 2 spaces before it, thus: ◊◊279 (◊ denotes a space). We also say “printed with 2 leading blanks/spaces” and “printed padded on the left with 2 blanks/spaces”.
A more technical way of saying this is “n is printed right-justified in a field width of 5”. “Right-justify” means that the number is placed as far right as possible in the field and spaces added in front of it to make up the field width. If the number is placed as far left as possible and spaces are added after it to make up the field width, the number is left-justified. For example, 279◊◊ is left-justified in a field width of 5.
The minus sign can be used to specify left-justification; %-wd will print a value left-justified in a field width of w. For example, to print an integer value left-justified in field width of 5, we use %-5d.
For another example, suppose n is -7 and the field width is 5. Printing n requires two print columns (one for - and one for 7); since the field width is 5, it is printed with 3 leading spaces, thus: ◊◊◊-7.
You may ask, what will happen if the field width is too small? Suppose the value to be printed is 23456 and the field width is 3. Printing this value requires 5 columns which is greater than the field width 3. In this case, C ignores the field width and simply prints the value using as many columns as needed (5, in this example).
In general, suppose the integer value v is printed with the specification %wd where w is an integer, and suppose n columns are needed to print v. There are 2 cases to consider:
- If nis less than w (the field width is bigger), the value is padded on the left with (w - n) spaces. For example, if w is 7 and v is -345 so that n is 4, the number is padded on the left with (7-4) = 3 spaces and printed as ◊◊◊-345.
- If nis greater than or equal to w (field width is the same or smaller), the value is printed using n print columns. In this case, the field width is ignored.
A field width is useful when we want to line up numbers one below the other. Suppose we have three int variables a, b and c with values 9876, -3 and 501, respectively. The statements
printf("%d\n", a);
printf("%d\n", b);
printf("%d\n", c);
will print
9876
-3
501
Each number is printed using just the number of columns required. Since this varies from one number to the next, they do not line up. If we want to, we could get the numbers lined up using a field width of 5, say. The statements
printf("%5d\n", a);
printf("%5d\n", b);
printf("%5d\n", c);
will print (◊ denotes a space)
◊9876
◊◊◊-3
◊◊501
which will look like this (without ◊):
9876
-3
501
all nicely lined up.
As a matter of interest, we don’t really need 3 printf statements. We can replace the last 3 printf statements with
printf("%5d\n%5d\n%5d\n", a, b, c);
Each \n forces the following output onto a new line.
2.6 Floating-point Numbers – float and double
A floating-point number is one which may have a fractional part. A floating-point constant can be written in one of two ways:
- The normal way, with an optional sign, and including a decimal point; for example, -3.75, 0.537, 47.0.
- Using scientificnotation, with an optional sign, including a decimal point and including an ‘exponent’ part; for example, -0.375E1 which means “-0.375 multiplied by 10 to the power 1”, that is, -3.75. Similarly, 0.537 can be written as 5.37e-1, that is, 5.37 x 10-1. The exponent can be specified using either e or E.
- Note that there are several ways to write the same number. For example, the following all represent the same number 27.96:
- 27.96E00 2.796E1 2.796E+1 2.796E+01 0.2796E+02 279.6E-1
In C, we can declare a floating-point variable using either float or double. A float value is normally stored as a 32-bit floating-point number, giving about 6 or 7 significant digits. A double value is stored as a 64-bit floating-point number, giving about 15 significant digits.
A floating-point constant is of type double unless it is followed by f or F, in which case it is of type float. Thus 3.75 is of type double but 3.75f or 3.75F is of type float. Most calculations are done using double precision. The type float is useful if you need to store lots of floating-point numbers and you wish to use as little storage as possible (and do not mind just 6 or 7 digits of precision).
In this book, we will mostly use double for working with floating-point numbers.
2.6.1 Print double and float Variables
We have been using the format specification %d in a printf statement to print the value of an integer variable. If we wish to print the value of a double or float variable, we can use %f. For example, consider the following:
double d = 987.654321;
printf("%f \n", d);
The value of d will be printed to a pre-defined number of decimal places (usually 6, but could vary from one compiler to the next). In this case, the value printed will be 987.654321. However, if d were assigned 987.6543215, the value printed would be 987.654322 (rounded to 6 decimal places).
Similarly, if x is of type float, its value could be printed using:
printf("%f \n", x);
We just saw that the specification %f prints the number to a pre-defined number of decimal places. Most times, though, we want to say how many decimal places to print and, sometimes, how many columns to use. For example, if we want to print d, above, to 2 decimal places in a field width of 6, we can use:
printf("%6.2f \n", d);
Between % and f, we write 6.2, that is, the field width, followed by a . (point), followed by the number of decimal places. The value is rounded to the stated number of decimal places and then printed. Here, the value printed will be 987.65, which occupies exactly 6 print columns. If the field width were bigger, the number will be padded on the left with spaces. If the field width were smaller, it is ignored, and the number is printed using as many columns as necessary.
As another example, consider
b = 245.75;
printf("%6.1f \n", b);
In the specification %6.1f, 1 says to round the number to 1 decimal place; this gives 245.8, which requires 5 columns for printing.
6 says to print 245.8 in 6 columns; since only 5 columns are needed for printing the number, one space is added at the beginning to make up 6 columns, so the number is printed as ◊245.8 (◊ denotes a space).
Similarly,
printf("%6.0f \n", b);
will print b as ◊◊◊246 (rounded to 0 decimal places and printed in a field width of 6).
If the specification were %3.1f and the value to be printed is 245.8, it would be printed using 5 print columns, even though the field width is 3. Again, when the field width specified is smaller than the number of print columns required, C ignores the field width and prints the value using as many columns as needed.
We can sometimes use this to our advantage. If we do not know how big a value might be, we can deliberately use a small field width to ensure it is printed using the exact number of print columns required for printing the value.
In general, suppose the float or double value v is to be printed with the specification %w.df where w and d are integers. Firstly, the value v is rounded to d decimal places. Suppose the number of print columns required to print v, including a possible point (there will be no point if d = 0; the value is to be rounded to a whole number) and a possible sign, is n. There are 2 cases to consider:
- If nis less than w (the field width is bigger), the value is padded on the left with (w - n) spaces. For example, suppose w is 7 and the value to be printed is -3.45 so that n is 5. The number is padded on the left with (7-5) = 2spaces and printed as ◊◊-3.45.
- If nis greater than or equal to w (field width is the same or smaller), the value is printed using n print columns. In this case, the field width is ignored.
As with integers, a field width is useful when we want to line up numbers one below the other. Assume we have three double variables a, b and c with values 419.563, -8.7 and 3.25, respectively. Suppose we want to print the values to 2 decimal places, lined up on the decimal point, like this:
419.56
-8.70
3.25
Since the biggest number requires 6 print columns, we can line them up using a field width of at least 6. The following statements will line them up as above:
printf("%6.2f \n", a);
printf("%6.2f \n", b);
printf("%6.2f \n", c);
If we use a field width bigger than 6, the numbers will still line up but with leading spaces.
For example, if we use a field width of 8, we will get (◊ denotes a space)
◊◊419.56
◊◊◊-8.70
◊◊◊◊3.25
Again, we can use one printf instead of three to achieve the same effect:
printf("%6.2f \n%6.2f \n%6.2f \n", a, b, c);
Each \n forces the following output onto a new line.
2.6.2 Assignment Between double and float
As expected, you can store a float value in a float variable and a double value in a double variable. Since float is smaller than double, C allows you to store a float value in a double variable without any problems. However, if you assign a double to a float, some precision may be lost. Consider the following:
double d = 987.654321;
float x = d;
printf("%f \n", x);
Since a float variable allows only about 7 digits of precision, we should expect that the value of d may not be assigned precisely to x. Indeed, when run using one compiler, the value 987.654297 was printed for x. When d was changed to 987654321.12345, the value printed was 987654336.000000. In both cases, about 6 or 7 digits of precision were retained.
As an exercise, see what values are printed using your compiler.
2.6.3 Floating-point Expressions
Floating-point expressions can be written using the following operators:
+ |
addition |
− |
subtraction |
* |
multiplication |
/ |
division |
These operate as expected; in particular, division is performed in the usual way so that, for example, 19.0/5.0 gives the value 3.8.
If op1 and op2 are the two operands of an operator, the following shows the type of calculation performed:
op1 |
op2 |
type of calculation |
float |
float |
float |
float |
double |
double |
double |
float |
double |
double |
double |
double |
Thus float is performed only if both operands are float; otherwise double is performed.
2.6.4 Expressions with Integer and Floating-point Values
It is quite common to use expressions involving both integer and floating-point values, for example,
a / 3 where a is float
n * 0.25 where n is int
In C, the rule for such expressions is this:
If either operand of an arithmetic operator is floating-point, the calculation is done in floating-point arithmetic. The calculation is done in float unless at least one operand is double, in which case the calculation is done in double.
In the first example above, the integer 3 is converted to float and the calculation is done in float. In the second example, n is converted to double (since 0.25 is double) and the calculation is done in double.
How do we get the exact value of an integer division, 19/5, say? We can force a double precision calculation by writing one or both constants as double, thus: 19/5.0, 19.0/5 or 19.0/5.0. We can also use a cast, as in
(double) 19 / 5
A cast consists of a type name enclosed in brackets and allows us to force the conversion of one type to another. Here, 19 is cast to double, forcing 5 to be converted to double and a double precision division is performed.
However, we must be careful with a construct like
(double) (19 / 5)
This may not do what we think. This does NOT do a floating-point division. Since both constants are integer, the expression inside the brackets is evaluated as an integer division, giving 3; this value is converted to double, giving 3.0.
2.6.5 Assigning double/float to int
Consider:
double d = 987.654321;
int n = d;
printf("%d \n", n);
The value 987 is printed. When we assign a floating-point value to an int, the fractional part, if any, is dropped (not rounded) and the resulting integer value is assigned. It is up to us to ensure that the integer obtained is small enough to fit in an int. If not, the resulting value is unpredictable.
On one compiler, where the largest value of an int was 32767, when d was changed to 987654.321, the value printed was 4614, a far cry from what might be expected, seemingly unpredictable. (Not quite unpredictable; the value assigned is 987654 % 32768 which is 4614. In general, if big represents a value that is too big to be stored, the value actually stored is big % 32768 for integers stored in 16 bits.) This is because the truncated value of d is 987654 which is too big to fit in an int variable. As an exercise, see what value would be printed on your compiler.
If we want the rounded value of d stored in n, we could do this with
n = d + 0.5;
If the first digit after the point in d is 5 or more, adding 0.5 would add 1 to the whole number part. If the first digit after the point is less than 5, adding 0.5 would not change the whole number part.
For example, if d is 245.75, adding 0.5 would give 246.25 and 246 would be assigned to n. But if d were 245.49, adding 0.5 would give 245.99 and 245 would be assigned to n.
2.7 Strings
So far, we have seen several examples of string constants in printf statements.
A string constant is any sequence of characters enclosed in double quotes. Examples are:
"Once upon a time"
"645-2001"
"Are you OK?"
"c:\\data\\castle.in"
The opening and closing quotes must appear on the same line. In other words, C does not allow a string constant to continue on to another line. However, a long string can be broken up into pieces, with each piece on one line. When the program is compiled, C will join the pieces, making one string. For example,
printf("Place part of a long string on one line and "
"place the next part on the next line. The parts are "
"separated by whitespace, not comma or ; \n");
The value of a string constant is the sequence of characters without the beginning and ending quotes. Thus, the value of "Are you OK?" is Are you OK?.
If you want the double quote to be part of a string, you must write it using the escape sequence \", as in
"\"Don’t move!\", he commanded"
The value of this string is
"Don’t move!", he commanded
Each \" is replaced by " and the beginning and ending quotes are dropped.
The C language does not have a predefined string type. This presents difficulties for the beginning programmer since he cannot work with string variables the way he can with numeric variables.
In C, a string is stored in an “array of characters”. Since we discuss characters in Chapter 6 and arrays in Chapter 8, we could be patient and wait until then to understand what is an array, how strings are stored and how we can use them to store a name, for instance. Or, we could accept a few things on faith and reap the benefit of being able to work with strings, in a limited way, much sooner than we normally would. We’ll be impatient and choose the latter.
Suppose we wish to store a person’s name in some variable name. We can declare name as follows:
char name[50];
This declares name to be a “character array” of size 50. As we will explain in Chapter 8, this allows us to store a maximum of 49 characters in name. If you find this is too much (or too little) for your purposes, you can use a different number.
If we want to, we can assign a string constant to name in the declaration, thus:
char name[50] = "Alice Wonder";
This stores the characters from A to r, including the space, in name. The quotes are not stored. Once this is done, we could print the value of name using the specification %s in printf, thus:
printf("Hello, %s\n", name);
This will print
Hello, Alice Wonder
The value of name replaces %s.
Unfortunately, we cannot assign a string constant to name, other than in the declaration of name. C does not permit us to write an assignment statement such as
name = "Alice in Wonderland"; // this is not valid
to assign a value to name. What we can do is use the standard function strcpy (for string copy), as in:
strcpy(name, "Alice in Wonderland"); // this is valid
But in order to use strcpy (and other string functions), we must precede our program with the directive:
#include <string.h>
We summarize all of this in Program P2.1.
Program P2.1
#include <stdio.h> //needed for printf
#include <string.h> // needed for strcpy
int main() {
char name[50];
strcpy(name, "Alice in Wonderland");
printf("Hello, %s\n", name);
}
When run, this program will print
Hello, Alice in Wonderland
In Sections 3.3 and 5.9, we will see how to read a string value into a variable.
Joining two strings is an operation we sometimes want to perform. We say we want to concatenate the two strings. We can do this with the standard string function strcat (string concatenation). For example, suppose we have:
char name[30] = "Alice";
char last[15] = "Wonderland";
The statement
strcat(name, last);
will add the string in last to the one in name. It is up to us to ensure that name is big enough to hold the joined strings. The result is that name will now hold AliceWonderland; the value in last does not change. The following statements will set name to Alice in Wonderland:
strcat(name, " in "); //one space before and after "in"
strcat(name, last);
2.8 The Assignment Statement
In Section 1.8, we introduced the assignment statement. Recall that an assignment statement consists of a variable followed by an equals sign (=) followed by the value to be assigned to the variable, followed by a semicolon. We could write this as:
<variable> = <value>;
<value> must be compatible with <variable> otherwise we will get an error. For example, if <variable> is int, we must be able to derive an integer from <value>. And if <variable> is double, we must be able to derive a floating-point value from <value>. If n is int and x is double, we cannot, for instance, write
n = "Hi there"; //cannot assign string to int
x = "Be nice"; //cannot assign string to double
It is useful to think of the assignment statement being executed as follows: the value on the right-hand side of = is evaluated. The value obtained is stored in the variable on the left-hand side. The old value of the variable, if any, is lost. For example, if score had the value 25, then after the statement
score = 84;
the value of score would be 84; the old value 25 is lost. We can picture this as:
score |
2 84 |
A variable can take on any of several values, but only one at a time. As another example, consider this statement:
score = score + 5;
Suppose score has the value 84 before this statement is executed. What is the value after execution?
First, the right-hand side score + 5 is evaluated using the current value of score, 84. The calculation gives 89—this value is then stored in the variable on the left-hand side; it happens to be score. The end result is that the value of score is increased by 5 to 89. The old value 84 is lost.
It is possible that even though an assignment statement is valid, it could produce an error when the program is run. Consider the following (a, b, c, d and e are int):
a = 12;
b = 5;
c = (a – b) * 2;
d = c + e;
Each of these is a correctly formed assignment statement. However, when these statements are executed, an error will result. Can you see how?
The first statement assigns 12 to a; the second assigns 5 to b; the third assigns 14 to c; no problem so far. However, when the computer attempts to execute the fourth statement, it runs into a problem. There is no value for e, so the expression c + e cannot be evaluated. We say that e is undefined—it has no value.
Before we can use any variable in an expression, it must have been assigned a value by some previous statement. If not, we will get an “undefined variable” error and our program will halt.
The moral of the story is: a valid program is not necessarily a correct program.
Exercise: What is printed by the following?
a = 13;
b = a + 12;
printf("%d %d\n", a, b);
c = a + b;
a = a + 11;
printf("a = %d b = %d c = %d\n", a, b, c);
2.9 printf
We have seen several examples of the printf statement. We have used it to print string constants, integer values and floating-point values. And we have printed values with and without field widths. We have also seen how to use the escape sequence \n to force output onto a new line.
It is worth emphasizing that the characters in the format string are printed exactly as they appear except that a format specification is replaced by its corresponding value. For example, if a is 25 and b is 847, consider the statement
printf("%d%d\n", a, b);
This will print
25847
The numbers are stuck together and we cannot tell what is a and what is b! This is so because the specification %d%d says to print the numbers next to each other. If we want them separated by one space, say, we must put a space between %d and %d, like this:
printf("%d %d\n", a, b);
This will print
25 847
To get more spaces between the numbers, we simply put how many we want between %d and %d.
Exercise: What is printed by the following?
printf("%d\n %d\n", a, b);
The following are some useful things to know about format specifications.
Suppose num is int and its value is 75:
- The specification %dwill print 75 using 2 print columns: 75
- The specification %5dwill print 75 with 3 leading spaces: ◊◊◊75
- The specification %-5dwill print 75 with 3 trailing spaces: 75◊◊◊
- The specification %05dwill print 75 with 3 leading zeroes: 00075
For an example in which leading 0s might be useful, consider the statement
printf("Pay this amount: $%04d\n", num);
This will print
Pay this amount: $0075
This is better than printing
Pay this amount: $ 75
since someone can insert numbers between $ and 7.
In general, the minus sign specifies left-justification and a 0 in front of the field width specifies 0 (zero, rather than a space) as the padding character.
EXERCISES 2
- In the ASCII character set, what is the range of codes for (a) the digits (b) the uppercase letters and (c) the lowercase letters?
- What is a token? Give examples.
- Spaces are normally not significant in a program. Give an example showing where spaces are significant.
- What is a reserved word? Give examples.
- Give the rules for making up an identifier.
- What is a symbolic constant and why is it useful?
- Give examples of integer constants, floating-point constants and string constants.
- Name 5 operators which can be used for writing integer expressions and give their precedence in relation to each other.
- Give the value of (a) 39 % 7 (b) 88 % 4 (c) 100 % 11 (d) -25 % 9
- Give the value of (a) 39 / 7 (b) 88 / 4 (c) 100 / 11 (d) -25 / 9
- Write a statement which prints the value of the intvariable sum, right justified in a field width of 6.
- You are required to print the values of the intvariables b, h and n. Write a statement which prints b with its rightmost digit in column 10, h with its rightmost digit in column 20 and n with its rightmost digit in column 30.
- Write statements which print the values of b, hand n lined up one below the other with their rightmost digits in column 8.
- Using scientific notation, write the number 345.72 in 4 different ways.
- Write a statement which prints the value of the doublevariable total to 3 decimal places, right justified in a field width of 9.
- You need to print the values of the floatvariables a, b and c to 1 decimal place. Write a statement which prints a with its rightmost digit in column 12, b with its rightmost digit in column 20 and c with its rightmost digit in column 32.
- What kind of variable would you use to store a telephone number? Explain.
- Write statements to print the values of 3 double variables a, band c, to 2 decimal places, The values must be printed one below the other, with their rightmost digits in column 12.
- How can you print the value of a doublevariable, rounded to the nearest whole number?
- What happens if you try to print a number (int, floator double) with a field width and the field width is too small? What if the field width is too big?
- Name some operators which can be used for writing floating-point expressions.
- Describe what happens when we attempt to assign an intvalue to a float variable.
- Describe what happens when we attempt to assign a floatvalue to an int variable.
- Write a statement to print the following: Use \n to end a line of output.
- Write a statement to increase the value of the intvariable quantity by 10.
- Write a statement to decrease the value of the int variable quantityby 5.
- Write a statement to double the value of the intvariable quantity.
- Write a statement to set ato 2 times b plus 3 times c.
- The doublevariable price holds the price of an item. Write a statement to increase the price by (a) $12.50 (b) 25%.
- What will happen when the computer attempts to execute the following:
- p = 7;
- q = 3 + p;
- p = p + r;
- printf("%d\n", p);
- Suppose rate = 15. What is printed by each of the following?
- printf("Maria earns rate dollars an hour\n");
- printf("Maria earns %d dollars an hour\n", rate);
- If mis 3770 and n is 123, what is printed by each of the following?
- (a) printf("%d%d\n", n, m);
- (b) printf("%d\n%d\n", n, m);