Foundations of Programming Using C Evan Weaver
Foundations of Programming Using C Evan Weaver
Foundations of
Programming Using C
by
Evan
Weaver
School of Computer Studies
Seneca College of Applied Arts and Technology
July 2006
Section Page
Preface......................................................................................................1
1. Introduction...................................................................................3
What is programming? Computer
architecture. The programming process. The
C Language.
2. Basic Computations.....................................................................9
A first C program. Variables. int.
double. long. float. printf. scanf.
Arithmetic operators. Assignment using =.
3. Basic Logic...................................................................................21
if. Relational Operators. Expressions,
statements and code blocks. while.
Logical operators (&& and ||). if/else.
do/while. DeMorgan's law. for. switch.
Avoid continue and goto.
4. Modularity.....................................................................................41
Functions. Parameters. return. Function
prototype. #include. Comments. Avoid
global variables.
5. Addresses and Pointers.........................................................51
Addresses. Pointers. Indirection. void.
6. Filling In The Gaps................................................................57
#define. ++. --. @=. Initialization vs
assignment. Casts. Conditional
expression. char. Validations with scanf.
getchar. More on printf.
7. Arrays...............................................................................................73
Array. Element. Index. Passing arrays.
Initializing arrays. Character strings.
strcpy. String constants. strlen. strcmp.
gets. Arrays of strings.
8. Files.................................................................................................89
File. fopen. fprintf. fscanf. fclose.
rewind. fgetc. fgets. fputc. fputs.
putchar. puts. Records and fields. Flags.
Not operator.
Preface
Preface Page 1
Downloaded by Vinh Ph?m (trumrua0804@gmail.com)
lOMoARcPSD|28142605
Chapter 1. Introduction
What Is A Computer?
What Is Programming?
Such languages are usually more precise, more rigid and less
flexible than the languages humans use to communicate. While
this should make a computer language easier to learn than a
human language, many people cannot cope with the lack of
flexibility and find programming to be an exercise in
frustration. But for many others, fluency in a first computer
language can be developed in a year or two (compared to 10 years
or so for a person's first human language), with fluency in
subsequent languages often taking a matter of months (versus
years in the case of human languages).
must analyze what you want the computer to do, and plan out the
entire program before you start to write it. Finally, a
programmer must be confident. The computer does not have any
willingness to cooperate with you. It is not going to cheer you
on. You must know what the computer is capable of, and be sure
that you can make it do what you want it to do.
Computer Architecture
All computers today are binary, which means that each wire or
channel within the computer carries a signal that is effectively
either "on" or "off". The CPU has many wires coming into it and
going out of it. Each machine language instruction is one set of
"on" and "off" values for each of the input lines. Most
programmers and engineers represent the state of each wire with
a 0 (for "off") or 1 (for "on"), so that each machine language
instruction can be represented as a series of 0s and 1s. This
can mathematically be interpreted as a number in base 2 (also
called the binary numbering system). Once something is
represented as a number in any base, it can be converted to a
number in any other base, such as base 10 (or decimal), which is
what people usually use to represent numbers. The net result of
all this is that the machine language for a particular CPU is
Note that the devices may or may not be in the same physical box
as the CPU and memory. On a typical microcomputer, for example,
it is common for the disk drives to be in the same box as the
CPU, but the printer, monitor and keyboard are usually separate,
connected to the CPU box by wires.
Once the program passes the testing step, it may be used for its
intended purpose. Note that in most cases, the user of the
program will not be the programmer.
The C Language
A First C Program
This:
main()
{
printf("Hello, world!\n");
}
Hello, world!
main()
{
printf("Hello, world!\n");
printf("Hello,\nworld!\n");
Hello,
world!
Hello, world!
This program was typed in by Joan Smith
In order to have the user enter something, you first need to set
aside some space (in the computer's memory) where that data will
be stored. In C you do this by defining a variable. A variable
is a named area of memory. You define a variable by stating what
kind of data you wish to store, and what name you want to use.
For example,
int number;
x
first_number
account_balance
DayOfWeek
r2d2
amount3
There are some keywords used by the C language that you should
not use as variable names. These reserved words are listed in
Appendix F.
main()
{
int number;
The next step uses another C statement, scanf, which gets input
from the user (by "scanning" the keyboard). Here, scanf is being
given two things, or parameters, separated by a comma (,). The
first parameter is a character string that describes the format
of the desired input. This string usually contains one or more
format specifications, which begin with a percent sign (%) and
describe the type of data to be input. The format specification,
%d, is used to tell scanf that integer data is expected. (The
"d" in %d comes from the fact that the integer will be be
entered in base 10, or decimal, notation). Each data type has a
different format specification to be used in scanf, and as we
learn the various data types available, we will also learn the
corresponding format specifications.
If we run this program and enter the number 56, the output would
be:
actually get into the program in such a way that we can make use
of it.
Two other numeric data types are long and float. On some
machines (so-called 16-bit computers, such as a PC running DOS
or Windows 3.1), int variables can only store numbers that are
between -32768 and +32767. The long data type is like int,
except that the range of values that may be stored is wider,
typically from -2147483648 to +2147483647, with a corresponding
decrease in efficiency. While longs are slower than ints and
take more space, they are smaller and faster than doubles, so it
makes sense to use long (rather than double) if int does not
provide the necessary range, but long does. On most 32-bit
computers (such as a PC running Windows 95, or an RS/6000), int
is the same as long, so you don't need to worry about choosing
between them. Still, many programmers will use int and long as
if they were on a 16-bit computer, so that the program will be
easier to move to a 16-bit computer should that ever be
necessary. The scanf/printf specification for long is %ld (for
long integer in decimal format).
Arithmetic Operators
main()
{
int quantity;
double cost;
Note how the product of the int value 5 and the double value
0.56 is a double, which is why we have used %lf to display the
calculation. Note also how this double is shown with 6 digits
after the decimal place. A double value does not have a specific
preset number of decimal places, but the printf function will
always show 6 decimal places, unless we tell it otherwise. To
have printf show a specific number of decimal places other than
6, put a period followed by the number of places desired right
after the %, but before the lf, in the format specification. For
example, we would use the format specification "%.2lf" to have a
double value shown to 2 decimal places of accuracy. Thus,
changing the last line of the program to:
x + y * z
would add x and the product of y and z (rather than multiply the
sum of x and y by z). This fact that multiplication has higher
precedence than addition mimics the common notation used in all
basic mathematics courses. In C, both + and - have the same
precedence as each other, and both * and / have the same
precedence as each other, but have higher precedence than + and
-.
(x + y) * z
a - b + c - d
((a - b) + c) - d
main()
{
int minutes;
A third point about this program is that the last printf is too
long to neatly fit on one line of the program. Rather than
keeping it on one line, and have it run off the edge of the page
when we show it on paper, we have simply continued the printf on
the next line. The C language doesn't really care how the
program is spaced out on the screen, but rather cares about the
punctuation. The very first program we looked at could have been
typed in like this:
main(){printf("Hello, world!\n");}
and it would work exactly the same way. C allows spaces, tabs
and newlines to be freely inserted anywhere in a program EXCEPT
in the middle of a name (such as a variable name), language
keyword (such as int or double) or constant (such as 60 or
"Hello, world!\n"). In particular, wherever there is punctuation
(such as the commas between parameters) or an operator (such as
+) there is an opportunity to insert a space or even go to a new
line. By the way, C programmers use the term whitespace to refer
to any combination of one or more space, tab, vertical tab, form
feed and newline characters. We have been using this ability to
insert whitespace to make our programs look attractive and more
readable than they would be if they were just all jumbled up on
one line. While there are no hard and fast rules about using
whitespace, there are some guidelines that most programmers
follow:
Assignment Operator
main()
{ int quantity;
double cost, total, tax;
y = m*x + b
its value in the variable on the left side. In the case above,
quantity and cost must already have values, and the variable tax
will be set to be the product of those values. If you later
change the variable quantity (or cost), the variable tax will
not be affected (unless you then repeat the assignment
statement).
double cost;
double total;
double tax;
The if Statement
if (some condition)
some statement
main()
{
int quantity;
double cost, total, tax;
Note how after calculating total, but before figuring out the
taxes, we decide whether or not to recalculate total to reflect
the discount. In this case, we want to use the discount only if
the total is over $100. Make sure that you understand the
calculation that is being done under this condition. If the
total were, say, $120, then there would be a 10% discount on $20
(the amount in excess of $100), so the pretax total would be
Notice also how symbols like $ and % are not used to represent
dollars and percents. In C, any numeric amount is just a number.
Whether a number is a special kind of number or not can be
reflected in the output of the program (by placing a $ in a
printf statement, for example), but has no bearing on the simple
arithmetic calculations that you ask the computer to perform. As
an example, we have multiplied by 0.9 to get 90% of an amount;
we cannot multiply by 90%. (We have already seen that the % sign
means something else - modulus - in C computations).
Relational Operators
Symbol Meaning
> greater than
>= greater than or equal to
< less than
<= less than or equal to
!= not equal to
== equal to
if (x >= y)
printf("Hello\n");
would print out "Hello" (and move to the next line) if, and only
if, the number stored in x is greater than or equal to the
number stored in y. Be especially careful when asking if two
things are equal. The following statements look essentially the
same:
right:
if (x == y)
printf("They are the same\n");
wrong:
if (x = y)
printf("They are the same\n");
x + y < z * (200 + y)
x = y * (3 + z) + w;
if (x >= y)
printf("Hello\n");
printf("Hello\n");
main()
{
int quantity;
double cost, total, tax, discount;
This does occupy one more line of the page, but makes it harder
to "lose" the opening braces when you are quickly scanning the
program. Remember that the spacing of the program code is not an
issue to the compiler or to the correctness of the program, but
it can be tremendously helpful to someone (perhaps even
yourself!) reading your program at a later time. The generally
accepted rule is to indent lines of a program whenever the
execution of those lines may be dependent on something. That
way, it really stands out that the lines may or may not be
executed. There are many variations of this theme, and as you
continue to practice programming, you will settle on a style
that you like the best. It is more important that you develop a
style that you use consistently, than it is that you follow the
style used by any one book.
main()
{
int quantity;
double cost, total, tax, discount;
Notice how, using our coding style, the second if, which is part
of the first if, causes a secondary indentation.
The following program plays a simple little game with the user.
It asks for a number, expecting a particular value. If the
user's response isn't the one it expected, it gives a hint ("too
high" or "too low") and asks again. Eventually (hopefully) the
user will enter the correct number and the game ends.
main()
{
int guess;
Guess a number: 34
Too low! Try again: 50
Too high! Try again: 40
Too low! Try again: 42
You guessed the magic number! YOU WIN!!
(Of course, since you have read the code for this game, you know
what the answer is and would simply enter 42 right at the
beginning. But someone who hasn't read the program wouldn't be
able to get it right away without a great deal of luck).
Note how the last step in the while statement is to have the
user enter a number into the variable, guess, which also is
checked in the while's condition. It is critical that some value
involved in the condition has the opportunity to change during
the execution of the while statement. Otherwise, the statements
will simply be executed over and over again, forever. This kind
of programming error is called an infinite loop, because the
program keeps repeating, or "looping", the same code over and
over. If you do write a program with an infinite loop, and it
just keeps repeating over and over, there is a way to stop it,
but each system uses a slightly different method. On some
systems there is a key labelled "break" or "attention", and on
others there is a special key sequence (often, it is Control/C -
pressing C while holding down the key labelled "Control"), which
will terminate a runaway program.
Let us now add a little bit more to the game, so that the
computer can tell us how many tries it took:
main()
{
int guess, counter;
counter = 1;
printf("Guess a number: ");
scanf("%d", &guess);
while (guess != 42) {
if (guess > 42)
printf("Too high! ");
if (guess < 42)
printf("Too low! ");
printf("Try again: ");
scanf("%d", &guess);
counter = counter + 1;
}
if (counter == 1)
printf("WOW! Either you are very lucky or you CHEAT!\n");
if (counter != 1)
printf("You win in %d tries.\n", counter);
}
Compound Conditions
When two conditions are joined with && (and), the resulting
compound condition will be true if, and only if, both of the
sub-conditions are true. When two conditions are joined with ||
(or), the resulting condition will be true if either (or both)
of the sub-conditions are true. This matches what we usually use
"and" and "or" to mean in English, when we use them to combine
conditions.
is the same as
However, && has a higher precedence than || (in much the same
way as * has a higher precedence than +), so that
is the same as
Condition Value
x < z && y < z false (since x < z is false)
y < z && z < x true (since both y < z and z < x are true)
x < z || y < z true (since y < z is true)
y > x || y > z false (since neither y > x nor y > z is true)
Now that we can join conditions, we will refine our game a bit
more. We will make it stop when they get it right, or have made
100 tries, whichever comes first. If they don't get it in 100
tries, they lose. If they do win, we'll give them a different
message depending on how few or how many turns they took.
main()
{
int guess, counter;
counter = 1;
printf("Guess a number: ");
scanf("%d", &guess);
while (guess != 42 && counter < 100) {
if (guess > 42)
printf("Too high! ");
if (guess < 42)
printf("Too low! ");
printf("Try again: ");
scanf("%d", &guess);
counter = counter + 1;
}
if (counter == 1)
printf("WOW! Either you are very lucky or you CHEAT!\n");
if (counter >= 2 && counter < 5)
printf("Very good. You got it in %d turns\n", counter);
if (counter >= 5 && counter < 15)
printf("You win in %d turns, an average performance\n",
counter);
if (counter >= 15 && counter < 100)
printf("Duh, it took you %d tries to get it!\n", counter);
if (counter == 100 && guess == 42)
printf("You got it in the nick of time\n");
if (guess != 42)
printf("You lose\n");
}
the second condition false). Note that if the user does take 100
guesses, that 100th guess might be right (a narrow victory) or
wrong (a loss).
While any logic can be programmed using the simple if and while
statements, the C language provides a few alternative logic
control statements which can make a program more efficient or
easier to read.
if (some condition)
some statement
else
some other statement
if (x < 5)
printf("That number is too small\n");
if (x >= 5)
printf("That is a good number\n");
if (x < 5)
printf("That number is too small\n");
else
printf("That is a good number\n");
Here, the use of if/else would make the program more efficient,
because it would only have to compare x to 5 once, rather than
twice. The use of else is also more readable, in the sense that
it is clear that one thing or the other is going to be done. In
the first example, you must carefully examine both conditions to
see that they are opposites in order to tell that only one of
the two dependent statements is going to be executed.
A common situation is
if (x < 5)
printf("That number is too small\n");
else
if (x > 10)
printf("That number is too big\n");
else
printf("That number is nice\n");
if (x < 5)
printf("That number is too small\n");
else if (x > 10)
printf("That number is too big\n");
else
printf("That number is nice\n");
Using the if/else, we can make our game logic a little bit
cleaner:
main()
{
int guess, counter;
counter = 1;
printf("Guess a number: ");
scanf("%d", &guess);
while (guess != 42 && counter < 100) {
if (guess > 42)
printf("Too high! ");
else
printf("Too low! ");
printf("Try again: ");
scanf("%d", &guess);
counter = counter + 1;
}
if (counter == 1)
printf("WOW! Either you are very lucky or you CHEAT!\n");
else if (counter < 5)
printf("Very good. You got it in %d turns\n", counter);
else if (counter < 15)
printf("You win in %d turns, an average performance\n",
counter);
else if (counter < 100)
printf("Duh, it took you %d tries to get it!\n", counter);
else if (guess == 42)
printf("You got it in the nick of time\n");
else
printf("You lose\n");
}
to
counter < 5
do
some statement
while (some condition);
main()
{
int n;
main()
{
int n;
n = 0;
while (n < 1 || n > 5) {
printf("Please enter a number between 1 and 5:");
scanf("%d", &n);
}
printf("Thank you. You selected %d\n", n);
}
main()
{
int n;
do {
printf("Please enter a number between 1 and 5:");
scanf("%d", &n);
} while (n < 1 || n > 5);
printf("Thank you. You selected %d\n", n);
}
DeMorgan's Law
x < 1 || x > 5
initial;
while (condition) {
statement
trailing;
}
main()
{
int n, sum, counter;
sum = 0;
for (counter = 1; counter <= 10; counter = counter + 1) {
printf("Please enter number %d: ", counter);
scanf("%d", &n);
sum = sum + n;
}
printf("The sum of those numbers is %d\n", sum);
}
switch (expression) {
case constant1:
zero or more statements
case constant2:
zero or more statements
...and so on (as many cases as you like)...
default:
zero or more statements
}
The computer first figures out the value of expression, and then
compares that to constant1. If there is a match, it begins to
execute statements immediately after the colon (:) at the end of
the first "case" line. If it doesn't match, it compares the same
value to constant2. If that matches, it starts executing
statements immediately after the colon at the end of the second
"case". This continues until it finds a match or reaches
"default:", whichever comes first. If there is no match, the
statements after "default:" are executed.
break;
is almost always used with switch. The break statement tells the
computer to leave the current switch statement (and to resume
processing after the closing brace of the switch), and is
usually the last statement at the end of a "case" section.
Without a break statement, the logic from one case section
simply flows into the next.
main()
{
int door;
double retail;
main()
{
int door;
double retail;
case 2:
printf("your very own..........\n");
printf("...DONKEY! And a shovel, too!!\n");
printf("Too bad. Better luck next time.\n");
retail = 7.50;
break;
case 3:
printf("a BRAND NEW CAR!!\n");
printf("The fabulous 4-door\n");
printf("Liability by Generic Motors!\n");
printf("Congratulations!\n");
retail = 15700;
break;
default:
printf("a door that doesn't exist\n");
retail = 0;
}
printf("The retail value of your prize is $%.2lf\n", retail);
}
Note that the only values allowed after the word "case" are
constants, not variables or calculations. This means that switch
is only useful when there are certain specific values to which
you want to compare the original expression. You cannot specify
ranges, although you can have several "case constant:"
specifications, one after the other with no statements in
between them. If you do this, any of the listed constant values
will trigger the execution of the same code: the statements that
start after the last of the back-to-back cases. (In fact, it is
to allow this that the break statement is required).
Note also that you may omit the "default" section altogether. If
there is no "default" section, then nothing happens if there is
no match.
And keep in mind that many beginners using switch for the first
time will forget the break statements, which will cause one case
to run into the next, probably producing strange results. So if
you write a program using switch, and it compiles fine, but
doesn't seem to work right, first look for missing break
statements.
The formal term for statements executing one after the other is
sequence. To be complete, a programming language needs two ways
to modify the normal sequence of statements: selection, the
ability to select which statements will execute based on current
conditions, and iteration, the ability to repeat a sequence of
statements as much as necessary.
Chapter 4. Modularity
y = f(x)
2 2
let g(x, y) be (x - 2) + y
9 = g(x, y)
In C, we could write:
double f(double x)
{
return 1.5 * x + 5;
}
Here, there are two parameters, which we have called x and y and
which are both doubles. Note how a comma (rather than a semi-
colon) separates the parameter definitions. Note also how there
is no C arithmetic operator to raise a value to a certain power;
we have simply used multiplication to compute the squares of (x
- 2) and y.
t
p(1+r)
main()
{
double start, end, rate;
int years;
Second, note how the main function calls the invest function
mid-way through the while loop. Three variables from main
(start, rate and years) are passed to invest, which does some
calculations with them and sends back a value, which gets put
into main's variable, end. The values that main passes to invest
are called the arguments for the function call.
main()
{
double percent;
Function Prototypes
While these last two programs have two functions each (invest
and main), a typical program will have many functions. The hope
is that as a program grows more complex, the number of functions
increases rather than the complexity of the functions. Since
programs always start at the beginning of the function named
main, and since there will be many functions in a program, it is
common practice to place the main function first, so that it
will be easy to find.
A problem with putting main first is that the compiler will not
be able to tell if you are using any functions correctly in main
until those functions are defined. At that point, which is after
the compiler is finished with main, the compiler may give you a
message telling you that the function is wrong (not that main is
wrong), or may simply create an incorrect program without any
error messages. To avoid these difficulties, you may declare the
functions (which means to tell the computer the correct usage of
the functions), rather than fully defining them (which means to
write the code for the functions) at the top of the program,
then code the main function, then code the remaining functions
in any order you like (possibly in alphabetical order by
function name to make them easy to locate). To declare a
function without defining it, simply write its header line, and
put a semicolon at the end of it rather than putting a code
block with the function's logic. This declaration of the
function is called the function's prototype.
So all our programs thus far have been using functions without
properly declaring them. This is not a good thing. It has been a
carefully constructed "coincidence" that our examples haven't
had problems using printf and scanf. Every C compiler comes with
a set of files, called header files, which contain, among other
things, function prototypes for the functions in the standard C
library. The printf and scanf functions, for example, are
prototyped in a file named "stdio.h". (As we learn other
standard library functions, we will also learn the names of
their header files). In order to allow the compiler to ensure
that we are using standard library functions correctly, we can
copy the appropriate header file into our program, using a line
like
#include <stdio.h>
around the header file name indicates that the file is part of
the standard library and should be located where all the library
header files are. (You may use double quotes, ", instead of the
angle brackets to #include a file in the same location as the
file being compiled).
Program Comments
/************************************************
* Display a table of investment profits over a *
* 5-year period, using different interest rates *
* Written by: Evan Weaver October 9, 1996 *
************************************************/
#include <stdio.h>
double invest(double principal, double rate, int time);
main()
{
double percent;
(which is the program that called the main, based on some user
action), and most operating systems have a way of checking this
return value. It is common practice for main to return the
integer value 0 unless you have some reason to return a non-zero
value, but as long as you don't check the return value (by, say,
calling the program from an operating system script that
performs such a check), the value you actually return from main
is irrelevant. By ignoring the return value, our programs have
been returning a garbage value to the operating system. It would
be "cleaner" if we returned 0 at the end our main functions, but
unless we are going to check the value at the operating system
level, it really isn't necessary. The parameters for main, the
syntactical details of which you are not yet ready to
understand, represent the things typed at the operating system
level (along with the program's name) that caused the program to
be run. By ignoring the parameters, as our main functions have
all done, we are simply not using this information.
where the function sends back the hours as a return value and
passes an extra variable to hold the minutes. The problem with
this is that the parameters, total and minutes_left, are copies
of the actual function arguments. When this function changes
minutes_left, it is changing a copy of the second argument, not
the argument itself. When the function returns the number of
hours, the two parameter variables, which are local to the
function, simply disappear.
Addresses
There is, however, a trick that can make this work. Every
variable of a program is stored in the computer's memory
somewhere. Computer memory is really a series of numbered
storage locations, and machine language programs access these
locations by specifying the number of each location. Every
variable, then, has a memory location number. If we define a
variable, say,
int x;
then one thing the compiler does when translating the program is
to associate the name, x, with some memory location number, say,
location 3204. (The actual location for each variable is
dependent on such things as how many variables there are, how
big the program is, how many programs are currently loaded in
memory, and what other things are currently stored in memory).
There is a C operator, &, which can be used to find out what the
memory location of a variable is, by placing the & just before
the variable's name. We have seen this operator already, in our
calls to the scanf function such as:
scanf("%d", &x);
&x
Pointers
*p = 6;
*p
where data type refers to the data type of the variable being
pointed to.
int *p
*p = 2 * *p;
Note that some people use the term pointer to be synonymous with
the term address, and then speak of pointer variables, which we
refer to as pointers, and pointer values, which we refer to as
addresses.
main()
{
int tmin, hr, min;
Observe carefully how the calling function (main) uses & to send
the address of min to hours_and_minutes2, whereas the code for
hours_and_minutes2 uses * to resolve the pointer that receives
this address.
Void
In our last main, for example, both hr and min are being
changed, but by completely different mechanisms. The variable
#include <stdio.h>
void hours_and_minutes(int total, int *phrs, int *pmin);
main()
{
int tmin, hr, min;
return;
Often, a program may use the same constant value over and over
again. It is a good idea in such situations to give a name to
the constant value, and use the name throughout the code. One
way to do this is to use the directive, #define. Earlier (in
Chapter 3), we wrote a program that has the user guess a number.
The number the program was expecting was 42, and the constant
value 42 was used throughout the code. Instead, we could put the
directive
#define ANSWER 42
before the main function. Then throughout the code we would use
ANSWER instead of 42. This has three main advantages: (1) if we
want to change the program so that is looks for, say, 139
instead of 42, we only have one line of the program to change
[the #define], (2) the likelihood is reduced that we might
accidentally code the wrong constant value in one place, making
the program work incorrectly, and (3) by giving the constant
value a name, we help describe what the constant represents to
someone else reading the code.
The rules for the name are the same as the rules for a variable
or function name. However, many programmers choose to use all
capital letters for a #define name (and not all capital letters
for variables and functions), so that it is clear when reading
the code whether the name is a #define or a variable.
Incrementation
x = x + 1;
and
++x;
both do the same thing. In fact, the ++ may either precede the
variable name or the variable name may precede the ++, so
x++;
#include <stdio.h>
main()
{
int n, m;
m = 6;
n = 2 * ++m;
printf("n is %d and m is %d\n", n, m);
n = 3 - m++;
printf("and now n is %d and m is %d\n", n, m);
}
outputs:
n is 14 and m is 7
and now n is -4 and m is 8
n = 2 * ++m;
n = 2 * (m = m + 1);
A generalization of incrementation
n += 4;
n = n + 4;
n *= 3; /* triples n */
x /= 2; /* cuts x in half */
abc -= x * y; /* subtracts x * y from abc */
int x = 6, y, z = 12;
may be prototyped as
does not. The presence of the names, if they are well chosen,
may enable the programmer on occasion to avoid having to look up
the actual definition of the function to see exactly what the
parameters represent.
int n = 7, m = 5;
double x;
x = n / m;
x = (double)n / (double)m;
x = (double)n / m;
Note that casting has very high precedence (the same as ++, in
fact), so that n is cast to a double before that double is
divided by m. Note also that something like
Conditional Expression
if (x < y)
z = 3 * x + y - 2;
else
z = 4 * x + y - 2;
z = (x < y ? 3 : 4) * x + y - 2;
Character Data
All the data our programs have been working with have been
numeric. As you are probably aware, computers can manipulate
text data just as well. The way computers, which are basically
devices that perform numeric computations, deal with text data
is simply by treating each character of text as if it were a
number. The most widely used scheme for codifying text
characters is the ASCII (American Standard Code for Information
Interchange) table. This is simply a list of characters that can
char
'A'
'\n'
Note that the rules for non-printable characters are the same as
what we saw for character string constants in chapter 2. Also
keep in mind that C makes a large distinction between character
strings (several characters in between double quotes) and
individual characters (single characters in single quotes). In
this chapter, we are only going to learn how to handle
individual characters. The next chapter will show us the extra
complexities involved with working with strings.
#include <stdio.h>
main()
{
double fahr;
char again;
do {
printf("Enter degrees F: ");
scanf("%lf", &fahr);
printf("In Celsius that is %.1lf\n",
(fahr - 32) * 5.0 / 9.0);
printf("Another conversion? (y/n) ");
do {
scanf("%c", &again);
} while (again != 'y' && again != 'n');
} while (again == 'y');
}
Enter degrees F: 32
In Celsius that is 0.0
Another conversion? (y/n) y
Enter degrees F: 100
In Celsius that is 37.8
Another conversion? (y/n) y
Enter degrees F: 72
In Celsius that is 22.2
Another conversion? (y/n) n
scanf("%*c%c", &again);
then displays the total of all items entered, and (if the number
of items was non-zero) the number of items and the average unit
price. This program also will force the user to re-enter non-
numeric input. Notice particularly how the main function
contains the fundamental logic of the program; the details of
the numeric validations have been delegated to appropriate
functions. Also notice how many of the new things we have
recently learned (such as +=) have been used.
#include <stdio.h>
void clear_input(void);
int get_an_int(void);
double get_a_double(void);
main()
{
int qty, counter = 0;
double price, total = 0;
do {
printf("Enter quantity (0 to stop): ");
qty = get_an_int();
if (qty != 0) {
printf("Enter unit price: ");
price = get_a_double();
total += qty * price;
counter += qty;
}
} while (qty != 0);
printf("The total is %.2lf\n", total);
if (counter != 0)
printf(" for %d items at an average price of %.2lf\n",
counter, total/counter);
}
/* Gets an int value from the user and clears out the
* rest of the input line. The value entered is returned.
* If no int data is entered, the user is given an error
* message and a chance to re-enter.
*/
int get_an_int(void)
{
int n;
while (0 == scanf("%d", &n))
{
clear_input(); /* throw away the bad data */
printf(" Error! Please enter an integer: ");
}
clear_input(); /* throw away any extra data entered */
return n;
}
Exercise 6.1: One thing this program does not do is give the
user an error message if there is any garbage entered AFTER the
number on an input line. Rather, it ignores such garbage. Modify
the logic of the get_an_int and get_a_double functions, so that
anything after the number other than a newline character will
also cause the error message and re-entry of data. (Hint: don't
just read a number, but read a number and a character, so that
you can check that the character is a newline).
c = getchar();
and
scanf("%c", &c);
void clear_input(void)
{
while (getchar() != '\n')
; /* loop body is intentionally empty! */
}
Note how the body of the while loop is an empty statement (;).
In this case, the only thing that really needs to be repeated
(the getchar call, to read the next character of input) is done
as part of evaluating the condition for the loop. Because this
may be an obscure thing to do, we have placed an explanatory
comment beside the empty statement.
If the field width begins with a zero, then leading zeroes will
be used instead of leading spaces (provided that the field is
numeric, not character).
#include <stdio.h>
main()
{
printf("123456789012345678901234567890\n"); printf("%5d
%4c%-4d%05d%10.2lf\n", 15, 'x', 23, 321, 4.56);
}
outputs
123456789012345678901234567890
15 x23 00321 4.56
Notice how the field width for a double includes the decimal
places and the decimal point. Be aware that if you supply a
width that is too small to display the value, your field width
will be ignored and the entire field will be displayed.
#include <stdio.h>
main()
{
double fahr;
-10.0 -23.3
-5.0 -20.6
0.0 -17.8
....and so on
Chapter 7. Arrays
When you define an array, you specify the desired size (which,
with many compilers, must be a constant, by the way) inside
brackets ([]) immediately after the variable name. For example,
int x[10];
x[0] = 6;
x[7] = 23;
sets the third element to be the sum of the first and eighth
elements.
#include <stdio.h>
#define SIZE 5
main()
{
int nums[SIZE], i;
Also, be aware that you don't use the entire array, but rather
the individual elements of the array. Every use of the array,
nums, in the program above is followed by brackets containing an
index.
to specify a size for the array (since the size was specified
when the originating array was defined). A modular version of
the previous program is
#include <stdio.h>
#define SIZE 5
void fill(int x[]);
void reverse(int x[]);
main()
{
int nums[SIZE];
int x[]
#include <stdio.h>
#define MAX 80
void backwards(char line[], int size);
int getline(char line[]);
main()
{
char input[MAX];
int numchars;
printf("Enter a line to be reversed:\n");
numchars = getline(input);
printf("See if you can say this:\n");
backwards(input, numchars);
}
/* displays "size" characters from "line" in reverse order */
void backwards(char line[], int size)
{
int i;
Initializing arrays
Normally, you may only set the elements of an array one by one.
However, when you first define an array, you may supply initial
values for all elements of the array, by placing the desired
initial values in braces ({}) and separating them with commas.
For example,
int x[5];
x[0] = 10;
x[1] = -2;
x[2] = 3;
x[3] = 45;
x[4] = 6;
int x[5] = { 0 };
Character strings
Rather than use a char array to store the string and a separate
int variable to store the length of the string, these standard
functions store the length in a slightly more clever fashion.
A special char value, called the null character (or null byte,
since byte is a technical term for the amount of memory used to
store one character), is placed after the last character of data
in the array, and all the string-handling functions are designed
to recognize this character as the end of the data stored in the
string.
The null character has numeric code zero. (If you look on an
ASCII table, you will see the ASCII name for the null byte is
NUL). This character is non-printable, and so the special
notation
'\0'
Also, note that when you declare an array that will hold one of
these strings, the size of the array should be one element
larger than the largest string you wish to store, since the null
character itself occupies one element.
defines a to hold the word "hello", and then copies that word
into b. The actual working of strcpy is surprisingly simple. It
copies the first character of the second string to the first
position of the first string, the second character of the second
string into the second position of the first string, and so on,
until the null byte at the end of the second string is copied
over. If we were to write it ourselves we might write a function
something like:
One warning about strcpy: while strcpy can find the end of the
second string (by looking for the null byte), it has no
knowledge of the maximum capacity of the first array. Therefore,
whenever you use strcpy, you should be sure to supply, as the
first parameter, an array that is big enough to hold the string
you are copying. If the array is too small, some other parts of
memory may be overwritten, and your program will act
unpredictably.
is the same as
strcpy(name, "Wilma");
is
Barney is smaller
Keep in mind that, just as you must use strcpy (or something
equivalent) rather than = to assign one string to another, you
must also use strcmp (or something equivalent) rather than <,
<=, ==, >=, > or != when comparing two strings.
However, this capability has already been put into printf. You
may use the specification %s in a printf format string, and
With the %s specification, you may also supply what looks like a
number of decimal places. For a string, this is used to specify
the maximum number of characters from the string to display. The
specification "%-30.30s", for example, displays no more than 30
characters from a string, in a left justified 30 character
field.
You may also use %s as a format spec for scanf. In this case,
since arrays are automatically passed as addresses, you use the
array name, with no brackets ([]) and no ampersand (&), as the
corresponding value. For example,
char name[31];
printf("Enter your name: ");
scanf("%s", name);
would let you enter your name, placing the first word entered
into the array, name, as a string. Like the numeric formats (but
unlike %c), %s is designed to treat whitespace as a separator. A
side effect of this is that %s only takes one word from input.
Another format spec that may also be used with a string variable
is % followed by brackets ([]) containing a list of valid
characters. For example, "%[yYnN]" would accept a string of y's
and n's (either upper or lower case), stopping when some other
character is encountered. Two characters have a special
significance inside the brackets: - indicates a range of
characters, and ^ indicates a desire to accept all characters
except what is shown. For example
accepts only spaces, lower case letters (in the range between
'a' and 'z') upper case letters (in the range between 'A' and
'Z') and digits (in the range '0' to '9'). Once some other
character (such as a new-line or punctuation) is encountered,
input will stop being taken, and the string (name) will be
terminated. Similarly
scanf("%[^\n]", name);
scanf("%30[^\n]", name);
gets(name);
and
scanf("%[^\n]%*c", name);
Arrays of strings
char names[10][31];
gets(names[i]);
About the only rule change for dealing with such arrays, called
2-dimensional arrays, is that when a function is passed a 2-
dimensional array, the parameter in the function header MUST
have the size of the second dimension specified. For example, if
the names array define above were to be passed to a function
named foo, then the function call might be:
foo(names);
Customer Invoice
Total 2019.97
Tax 15.00 percent 303.00
main()
{
char descriptions[MAX][DESCSIZE];
int quantities[MAX], itemcount;
double prices[MAX];
if (i < max) {
printf("Enter item description (or quit to stop): ");
get_a_line(desc[i], DESCSIZE - 1);
}
}
return i;
}
/* Gets an int value from the user and clears out the
* rest of the input line. The value entered is returned.
* If no int data is entered, the user is given an error
* message and a chance to re-enter.
*/
int get_an_int(void)
{
int n;
while (0 == scanf("%d", &n))
{
clear_input(); /* throw away the bad data */
printf(" Error! Please enter an integer: ");
}
clear_input(); /* throw away any extra data entered */
return n;
}
Chapter 8. Files
But you can also have files which contain data that is accessed
by one or more programs. Accessing a file from a C program
involves three steps:
2. The program accesses data in the file. This may mean that
the program stores data on the file (sometimes called
"writing to the file"), or it may mean that the program
retrieves data from the file (sometimes called "reading
from the file"). Typically, the file is accessed numerous
times (in a loop, for example) while the file is open.
The library function, fprintf, is used to write to a
file, while the function fscanf is used to read from a
file. These functions make file access look very much
like output to and input from the terminal.
FILE *fp;
fp = fopen("data.dat", "r");
"r" - tells fopen you will be reading data from the file. If
no such file exists, fopen will fail.
"a" - tells fopen that you will be writing new data to the
end of the file (i.e. appending data to the file). If
the file already exists, all data written to the file
will appear after the pre-existing data. If the file
doesn't exist, a new (empty) file will be created.
would read the same sort of data from a file (into variables
name, salary and job) that the previously shown fprintf would
have written. Here, fin is a pointer to a FILE opened for "r"
access.
When you are done with a file, you must remember to close it. If
the FILE address from fopen was stored in a pointer named fp,
you would close it with the statement
fclose(fp);
Closing the file ensures that all data the program may have
written to the file actually gets onto the disk (usually, the
file functions may "cache" some of the data destined for the
disk in memory until there is enough accumulated data to justify
a disk write, which is a comparatively slow operation), and also
tells the operating system that you no longer need to use the
file, which may allow another program to use the file.
safe side, always remember to call fclose for every file that
was successfully opened, even though you may determine through
experimentation that forgetting to close does not seem to cause
problems on your system.
rewind(fp);
Also, just as getchar and gets perform duties that overlap what
scanf does, there are similar file functions related to fscanf.
The fgetc function reads one character from a file, so that
c = fgetc(fp);
The one way in which fgetc differs from the equivalent fscanf
call is what happens if there is no more data in the file. The
fscanf call will fail, returning -1 and not setting the
character variable. The fgetc function returns the integer value
-1. The value -1 is given the name EOF (again, using a #define)
in stdio.h, by the way.
In much the same way, fgets reads one line from a file into a
string.
except that the fgets call places the newline in the string s,
where the fscanf shown will simply discard it. (Note that the
fgets file function works a bit differently from the gets input
function, in that you supply fgets with the size of the char
array you are passing, and in that it stores, rather than
discards, the newline.)
fputc(ch, fp);
fputs(str, fp);
putchar(ch);
printf("%c", ch);
while
puts(str);
printf("%s\n", str);
Note that puts displays a newline after the string, whereas the
fputs functions does not write a newline character to the file
after writing the string.
You may write any data you like to a file, in any order you
want. But files are most commonly used in two different
situations: (1) storing data that is destined to be viewed later
or printed out (called a report file), and (2) storing data to
be used again later by the same or another program (called a
data file).
where each record forms one line of the file and the fields are
separated by semi-colons (;). A sample of the file might be:
Note that for brevity, this program does not do any input
validation; you may add in functions similar to those we have
written before to make the entry less error prone. Also, note
that if we had wanted to create a new file from scratch, rather
than adding to the end of any data that may already be in the
file, we would have used "w" instead of "a" in the fopen
statement.
main()
{
FILE *fp;
char name[36], job[41];
double salary;
fp = fopen("employee.dat", "a");
if (fp == NULL)
printf("Cannot open the employee.dat file\n");
else {
while (enter_employee(name, job, &salary))
fprintf(fp, "%s;%s;%.2lf\n", name, job, salary);
fclose(fp);
}
}
x != 0
strcmp(name, "")
rather than
strcmp(name, "") != 0
to see if the name is empty. (We could have gone one step
further and simply used name[0] as the condition - this asks if
the first character of the array is non-zero - which will only
be false if name[0] is the numeric value 0, i.e. the null
character).
!(x < y)
is the same as
x >= y
and
!ok || x >= y
#include <stdio.h>
double show_raise(char name[], double sal);
main()
{
FILE *fp;
char name[36], job[41];
double salary, total_cost = 0;
fp = fopen("employee.dat", "r");
if (fp) {
printf(" Proposed Employee Raises\n");
printf("%-35s%10s\n", "Name", "Raise");
while (3 == fscanf(fp, "%35[^;];%40[^;];%lf\n", name,
job, &salary))
total_cost += show_raise(name, salary);
printf("\nTotal cost for raises: $%.2lf\n", total_cost);
fclose(fp);
}
elseprintf("Cannot read employee.dat file\n");
In this program, note how we read each record of the file with
the following fscanf call:
Note that it could fail because we have already read all the
data and there is none left, or because the data that is there
is not of the correct type. We could distinguish between these
two if we wanted (the fscanf would return -1 if there were no
more data, and 0-2 if it were bad data) but we have chosen
simply to stop reading at the first sign of trouble, regardless
of the reason.
A Final Word
While these exercises should give you some feel for what you can
expect to have to be able to do on tests, realize that every
walkthrough you encounter will be a unique combination of the
syntactical elements you have been studying. Knowing how these
particular programs work should not be your goal. Rather, you
should be learning the concepts of programming, and the syntax
of the C language, well enough to be able to figure out programs
like these. If you can figure these out, it is a good sign. But
if you need too much help to get through them, that means you
need to do more work.
main()
{
int a;
double b, c;
a = 6;
b = 0.7;
while (a < 10 && b < 3.0) {
if (a < 8) {
a = a + 1;
b = b * 2;
c = a - b;
}
else {
a = a - 2;
b = b + 0.8;
}
c = a - b;
printf("%.2lf-%d-%.2lf\n", c, a, b);
}
}
main()
{
int num1;
double num2, num3;
num1 = 0;
num2 = 1.5;
num3 = num2 * 3.0;
if (num3 > num2) {
printf("I ");
num1 = num1 + 1;
while (num3 > num2) {
num2 = num2 + num1;
printf("*");
}
}
else
printf("You ");
printf(" C so well now\n");
printf("One: %d, Two: %.1lf, Three: %.1lf\n",
num1, num2, num3);
}
main()
{
double x;
int n, m;
n = 0;
m = 10;
for (x = 2.5; n < m; x = x + 0.3) {
if (n < 3) {
n = n + 1;
m = m - 2;
}
else
n = n + 1;
printf("%d %d %.1lf\n", n, m, x);
}
printf("%.1lf\n", x);
}
#include <stdio.h>
main()
{
int n = 8;
char c = 'f';
do {
switch (n) {
case 8:
case 11:
n++;
break;
case 9:
case 10:
case 12:
n += 2;
c = 'h';
break;
default: c
= 'q';
}
printf("n stores %d while c stores %c\n", n, c);
} while (c != 'q');
}
5. Basic modularity.
#include <stdio.h>
main()
{
int n, m;
n = 1;
m = 7;
while (m < 10) {
n = do_something(n, m);
m = m + 1;
printf("n is %d and m is %d\n", n, m);
}
printf("Phew!\n");
}
6. Basic modularity.
#include <stdio.h>
if (x < n)
y = x + n;
else
y = x - n;
printf("In foo (%.1lf)...", y);
return y;
}
main()
{
double a, b;
a = 6;
b = 12.5;
while (a < b) {
a = foo(b, 5);
printf("In main (%.1lf)\n", b);
b = b - 4.5;
}
}
7. Pointers.
#include <stdio.h>
main()
{
double x;
int i;
char c = 'a';
8. Arrays.
#include <stdio.h>
#define SZ 10
main()
{
int x[SZ], i, j = 5;
9. Strings.
#include <stdio.h>
#include <string.h>
int foo(char s1[], char s2[]);
main()
{
char name[21],
title[21];
strcpy(name, "Fred");
strcpy(title, "Crane Operator");
while (1 == foo(name, title))
printf("Looping...\n");
printf("All done\n");
}
int foo(char s1[], char s2[])
{
int num = 1;
printf("%s is a %s\n", s1, s2);
if (strcmp(s1, "Betty") == 0) {
strcpy(s1, "Barney");
strcpy(s2, "Programmer");
}
else if (strcmp(s1, "Fred") == 0) {
strcpy(s1, "Betty");
strcpy(s2, "Manager");
}
else {
strcpy(s1, "Joe");
strcpy(s2, "Leaf Blower");
num = 0;
}
return num;
}
10. Arrays.
#include <stdio.h>
#define MAX 5
main()
{
double nums[MAX];
int n;
fill(nums, MAX);
n = find(4.5, nums, MAX);
show(nums, n);
}
do {
diff = x[i] - num;
printf("%.2lf\n", diff);
i++;
} while (i < sz && (diff < -0.5 || diff > 0.5));
return i;
}
11. Arrays.
#include <stdio.h>
main()
{
double a[4], x = 0.5, y = 1.1, z;
int b[4], i;
#include <stdio.h>
#define SIZE 4
int calc(int nums[], int n, int *pe, int *po);
void set(int nums[], int n);
main()
{
int x[SIZE], even, odd, val;
set(x, SIZE);
val = calc(x, SIZE, &even, &odd);
printf("The amounts are %d, %d and %d\n", val, even, odd);
}
int calc(int nums[], int n, int *pe, int *po)
{
int i;
*pe = 0;
*po = 0;
for (i = 0; i < n; i+=2) {
*pe += nums[i];
if (i + 1 < n)
*po += nums[i + 1];
}
return *po + *pe;
}
void set(int nums[], int n)
{
int i, x;
x = 6;
for (i = 0; i < n; i++) {
printf("%d..", x);
nums[i] = x;
x = 2 * x;
if (x > 20)
x = 4;
}
}
13. Strings.
#include <stdio.h>
#include <string.h>
len = strlen(s);
for (i = 1; i + k < len; i++) {
for (j = i; s[j + k]; j++)
s[j] = s[j + k];
s[j] = '\0';
len -= k; k+
+;
}
}
main()
{
char s1[31], s2[31];
14. Strings.
#include <stdio.h>
#include <string.h>
main()
{
char s[30];
strcpy(s, "dirutawy5prefeghmr8jks");
puts("babies!");
disp1(s);
disp2(s);
}
15. Arrays.
#include <stdio.h>
main()
{
int n[10], i;
16. Files. Tricky - the program reads its own source file.
fp = fopen("q1.c", "r");
while (keepatit) {
if ('*' == (ch = fgetc(fp))) {
q1get(fp, s1, 5);
q1get(fp, s2, 3);
fclose(fp);
keepatit = 0;
}
}
printf("Well,%s%s it?!?\n", s2, s1);
}
5. Write a function
void histogram(int array[], int count)
which is passed an array of numbers and the number of
elements in the array, and then displays a histogram of that
array using asterisks. For example, if an array named x
contained the six values { 5, 1, 2, 4, 3, 4 }, then
histogram(x, 6) would output:
*****
*
**
****
***
****
(If a value is 0 or less, output an empty line for that
value)
A second call:
add_a_minute(&hour, &minute, &am_or_pm);
8. Write a function
Hint: Move through "str1" looking for spaces. Every time you
find one, copy everything on the right of it over one
character to the left.
9. Write a function
that removes all leading and trailing spaces from the null-
terminated string in "str1" and returns the new length of
the string.
11. Write a C program that asks the user to enter three names,
up to 30 characters each, and then displays the names in
alphabetical order. (Note that the order should be case
sensitive, with upper case letters first as in the ASCII
table, so that Fred is after FRED, but before fred, and aaa
is after ZZZ).
Seinfeld 50 75
Carson 40 20
Leno 45 50
Letterman 20 95
where each line of the file has a comedian's last name, his
or her F.I. (funny index, higher means funnier) and his or
her N.I. (nastiness index, higher means nastier). Spaces
separate each field in a record of this file.
12:56a 15
1:20p 20
3:01a 5
11:12a 55
Hints:
Read question 6 first.
54 68 69 73 20 69 73 20 61 20 74 65 73 74 20 6f 66 20 74
68 65 20 65 6d 65 72 67 65 6e 63 79 20 62 72 6f 61 64 63
61 73 74 20 73 79 73 74 65 6d 2e 0a
18. Write a program that asks the user to enter a file name and
then tells the user (1) how many characters are in the file,
and (2) how many of those characters are spaces. If the user
gives a bad file name, then an appropriate error message
should be displayed instead.
Numbering Systems
We use the decimal system, also called base 10, because we have
ten fingers. In decimal, a numeric quantity is represented by a
series of digits from 0 to 9. The rightmost digit represents a
number of units. The digit to the left of that represents the
number of tens of units, the next number to the left is the
number of tens of tens of units (=hundreds of units), the next
is the number of tens of tens of tens of units (=thousands of
units), and so on. The actual quantity expressed by the number
is the sum of all these quantities.
For example, if we had been born with one finger on each hand
(two fingers in total), we might count in binary (or base 2).
Here each digit would range from 0 to 1, and each successive
digit from right to left would represent the number of pairs of
what the previous digit represented.
32 16 8 4 2 1
1 1 0 1 0 0
32 + 16 + 4 = 52 in decimal
52 / 2
= 26 remainder 0 -------------|
26 / 2
= 13 remainder 0 ------------||
13 / 2
= 6 remainder 1 -----------|||
6 / 2
= 3 remainder 0 ----------||||
3 / 2
= 1 remainder 1 ---------|||||
1 / 2
= 0 remainder 1 --------||||||
||||||
The binary equivalent is: 110100
Exercises:
00011 = ?
2 10
287 = ?
10 2
101 = ?
10 2
Other bases are just as easy. For example, hexadecimal (base 16)
uses digits from 0 to 15. Since we can't represent the
quantities ten, eleven, ... , fifteen as a single digit with our
normal digits, we have to make up some symbols to represent
them. Typically, computer people use A for 10, B for 11, ... ,
Equivalence_between_binary_and_hexadecimal
0 0 0000
1 1 0001
2 2 0010
3 3 0011
4 4 0100
5 5 0101
6 6 0110
7 7 0111
8 8 1000
9 9 1001
10 A 1010
11 B 1011
12 C 1100
13 D 1101
14 E 1110
15 F 1111
A 4 0 D
1010 0100 0000 1101
Note in the last example how the binary digits were grouped from
the right to the left, making it necessary to pad with two
leading zeros.
5 7 0
101 111 000
A 0 E
1010 0000 1110
(regrouping from the right...)
101 000 001 110
5 0 1 6
1 7 7
001 111 111
(regrouping...)
0000 0111 1111
0 7 F
Number Base
2 8 10 16
1101
6
71
99
ABC
Exercise:
You just got a marvelous new printer. It has two different fonts
(Pica and Roman), two print qualities (draft and "Near Letter
Quality"), two different horizontal pitches (10 characters per
inch and 12 characters per inch), two different vertical pitches
(6 lines per inch and 8 lines per inch), optional italics,
optional bold, and optional underline.
The manual says that you can change any of these settings by
sending two bytes to the printer. The first byte should be what
the manual calls the escape character, and it says that the
binary pattern for this is 00011011. The second byte should be 8
bits where each bit controls a different feature, as follows:
The manual also says that you can send a byte to the printer in
BASIC by issuing the statement LPRINT CHR$(x); where x is the
byte you want to send. After some experimentation, you discover
that BASIC expects x to be a decimal number! Your problem is to
find the two decimal numbers you need to send the two bytes that
will set the printer for NLQ Roman italics, as small as possible
both vertically and horizontally.
Non-Decimal Arithmetic
that when you carry (or borrow) a 1, you are carrying (or
borrowing) 2, 8 or 16 (depending on what base you're working
with) and not 10.
1 11
A0F A0F A0F
F5 ---> F5 ---> F5
--- --- ---
4 carry 1 04 carry 1 B04
1 1
2 0 7 borrow 1 1 0 7 1 0 7
- B 6 ---> - B 6 ---> - B 6
1 5 1 1 5 1
D21
x 32
---
1A42
2763
1
1 0 0 0 1 1 borrow 1 0 0 0 0 1 1 borrow 1
- 1 1 0 ---> - 1 1 0 --->
0 1 0 1
1 1
0 1 0 0 1 1 borrow 1 0 1 1 0 1 1
- 1 1 0 ---> - 1 1 0
0 1 1 1 1 0 1
Octal
724 3301 41
+555 - 277 x23
---- ---- --
Binary
1100010 1010001 100101
+ 1111 - 111000 x 1100
Exercise:
Two's Complement
their designs.
Example: 26 - 48 = -22
26 in binary: 0000000000011010
2's complement of 48: +1111111111010000
1111111111101010
11001
111111111111111
1111111111111111
The numeric data types so far assume that only integers (whole
numbers) are to be stored. If fractional amounts are required,
one scheme (called fixed point) is to decide how many decimal
places (or "binary places") of accuracy is desired. All numbers
are then calculated to that degree of accuracy, and stored,
without the decimal point (or binary point), as a whole number.
Only when data is output on reports or screens is the decimal
point inserted. As an example, suppose monetary values are being
stored. Then two decimal places of accuracy are what is required
(for dollars and cents). A four byte signed binary value could
be used, storing the number of cents, and would be able to store
values from -21,474,836.48 to +21,474,836.47. Note that special
output routines would be required to display these values
properly.
There are many different possible data formats, and there are
several others in wide use, although they will be most commonly
encountered on EBCDIC-based machines using traditional business
programming languages such as COBOL and RPG.
385 0000385F
+385 0000385C
-385 0000385D
Exercise:
0 00 NUL 43 2B + 86 56 V
1 01 SOH 44 2C , 87 57 W
2 02 STX 45 2D - 88 58 X
3 03 ETX 46 2E . 89 59 Y
4 04 EOT 47 2F / 90 5A Z
5 05 ENQ 48 30 0 91 5B [
6 06 ACK 49 31 1 92 5C \
7 07 BEL (bell) 50 32 2 93 5D ]
8 08 BS (backspace) 51 33 3 94 5E ^
9 09 HT (tab) 52 34 4 95 5F _
10 0A LF (linefeed) 53 35 5 96 60 `
11 0B VT 54 36 6 97 61 a
12 0C FF (formfeed) 55 37 7 98 62 b
13 0D CR (carriage return) 56 38 8 99 63 c
14 0E SO 57 39 9 100 64 d
15 0F SI 58 3A : 101 65 e
16 10 DLE 59 3B ; 102 66 f
17 11 DC1 60 3C < 103 67 g
18 12 DC2 61 3D = 104 68 h
19 13 DC3 62 3E > 105 69 i
20 14 DC4 63 3F ? 106 6A j
21 15 NAK 64 40 @ 107 6B k
22 16 SYN 65 41 A 108 6C l
23 17 ETB 66 42 B 109 6D m
24 18 CAN 67 43 C 110 6E n
25 19 EM 68 44 D 111 6F o
26 1A SUB 69 45 E 112 70 p
27 1B ESC (escape) 70 46 F 113 71 q
28 1C FS 71 47 G 114 72 r
29 1D GS 72 48 H 115 73 s
30 1E RS 73 49 I 116 74 t
31 1F US 74 4A J 117 75 u
32 20 SP (space) 75 4B K 118 76 v
33 21 ! 76 4C L 119 77 w
34 22 " 77 4D M 120 78 x
35 23 # 78 4E N 121 79 y
36 24 $ 79 4F O 122 7A z
37 25 % 80 50 P 123 7B {
38 26 & 81 51 Q 124 7C |
39 27 ' 82 52 R 125 7D }
40 28 ( 83 53 S 126 7E ~
41 29 ) 84 54 T 127 7F DEL
42 2A * 85 55 U
2 ++ pre-increment right-to-left
2 -- pre-decrement right-to-left
2 + (unary) identity right-to-left
2 - (unary) negation right-to-left
2 & (unary) address right-to-left
2 * (unary) pointer resolution right-to-left
2 ! logical not right-to-left
7 == equality left-to-right
7 != inequality left-to-right
9 || logical or left-to-right
10 ?: conditional right-to-left
Operators with the same precedence level number have the same
precedence regardless of their order in this table.
auto
break
case
char
const
continue
default
restrict
do
double
else
enum
extern
float
for
goto
if
inline
int
long
register
return
short
signed
sizeof
static
struct
switch
typedef
union
unsigned
void
volatile
while
_Bool
_Complex
_Imaginary
Conditions Results
---------- -------
Balance - (Cheque + 0.50) >= 0 T F F
Balance - (Cheque + 10) >= -1000 T F
Actions
APPENDIX H. Flowcharts
On-Page Connector
Decision
Off-page Connector
main ()
{ START
int n, i;
do {
printf(“Enter a number:”);
scanf(“%d”, &n);
if (n < 10)
printf(“Must be 10 or more!\n”);
} while (n < 10); DISPLAY:
if (n <= 20) { Enter a number
for (i = 10; i <= n; i++)
printf (“%d ”, i);
printf(“\n”):
}
else {
switch (n) { INPUT:
case 30: n
n = n * 3;
break;
case 40:
while (n < 100)
n = n * 2;
break;
default: Y
n = n / 2; is n<10
?
}
printf(“%d\n”, n);
} DISPLAY:
N Must be
printf(“All done\n”); 10 or more!
}
is n<10
?
Next Page A
Prev
Page
A
Y N
is
n≤20
?
SET
i To 10 SET n
is Y to 3xn
n=30
?
N
is Y
N
is n=40
i≤n ?
?
N N is
Y
SET n
to n<10
DISPLAY: n 0
i 2
?
Y
SET n
SET i to 2xn
To i + 1
DISPLAY
A NEW DISPLAY:
LINE n
DISPLAY:
All done STOP