Clanguage by Pythonlife
Clanguage by Pythonlife
By Pythonlife
Introduction
The C programming language was originally developed by Dennis Ritchie of Bell Laboratories, and
was designed to run on a PDP-11 with a UNIX operating system. Although it was originally
intended to run under UNIX, there was a great interest in running it on the IBM PC and com-
patibles, and other systems. C is excellent for actually writing system level programs, and the
entire Applix 1616/OS operating system is written in C (except for a few assembler routines).
It is an excellent language for this environment because of the simplicity of expression, the
compactness of the code, and the wide range of applicability.
It is not a good "beginning" language because it is somewhat cryptic in nature. It allows the
programmer a wide range of operations from high level down to a very low level approaching
the level of assembly language. There seems to be no limit to the flexibility available. One
experienced C programmer made the statement, "You can program anything in C", and the
statement is well supported by my own experience with the language. Along with the resulting
freedom however, you take on a great deal of responsibility. It is very easy to write a program
that destroys itself due to the silly little errors that, say, a Pascal compiler will flag and call a
fatal error. In C, you are very much on your own, as you will soon find.
Since C is not a beginners language, I will assume you are not a beginning programmer, and I
will not attempt to bore you by defining a constant and a variable. You will be expected to
know these basic concepts. You will, however, not be expected to know anything of the C
programming language. I will begin with the highest level of C programming, including the
usually intimidating concepts of pointers, structures, and dynamic allocation. To fully under-
stand these concepts, it will take a good bit of time and work on your part, because they not
particularly easy to grasp, but they are very powerful tools. Enough said about that, you will
see their power when we get there, just don’t allow yourself to worry about them yet.
Programming in C is a tremendous asset in those areas where you may want to use Assembly
Language, but would rather keep it a simple to write and easy to maintain program. It has been
said that a program written in C will pay a premium of a 50 to 100% increase in runtime, because
no language is as compact or fast as Assembly Language. However, the time saved in coding
can be tremendous, making it the most desirable language for many programming chores. In
addition, since most programs spend 90 percent of their operating time in only 10 percent or
less of the code, it is possible to write a program in C, then rewrite a small portion of the code
in Assembly Language and approach the execution speed of the same program if it were written
entirely in Assembly Language.
Approximately 75 percent of all new commercial programs introduced for the IBM PC have
been written in C, and the percentage is probably growing. Apple Macintosh system software
was formerly written in Pascal, but is now almost always written in C. The entire Applix 1616
operating system is written in C, with some assembler routines.
Since C was designed essentially by one person, and not by a committee, it is a very usable
language but not too closely defined. There was no official standard for the C language, but the
American National Standards Association (ANSI) has developed a standard for the language,
so it will follow rigid rules. It is interesting to note, however, that even though it did not have a
standard, the differences between implementations are usually small. This is probably due to
the fact that the original unofficial definition was so well thought out and carefully planned that
extensions to the language are not needed.
Even though the C language enjoys a good record when programs are transported from one
implementation to another, there are differences in compilers, as you will find any time you try
to use another compiler. Most of the differences become apparent when you use nonstandard
extensions such as calls to the MS-DOS BIOS, or the Applix 1616/OS system calls, but even
these differences can be minimized by careful choice of programming means.
Applix 1616 builders have only the HiTech C compiler available. This version of the tutorialis
customised to suit HiTech C. The original MS-DOS version by Gordon Dodrill was portedto
the Applix 1616 (with great effort) by Tim Ward, and typed up by Karen Ward. The programs
have been converted to HiTech C by Tim Ward and Mark Harvey, while Kathy Morton assisted
greatly in getting Visual Calculator working. All have been tested on the Applix 1616/OS
multitasking operating system. The Applix distribution disks contain the complete original text of
this tutorial, plus all the converted C source code. The second disk contains executable,
relocatable versions of all the programs, ready to run on an Applix 1616. There is also a directory
of the original IBM source code, for those using IBM computers, who may wish to try them
with a different compiler. This printed version has been edited, indexed and pretty printed by
Eric Lindsay, who added the Applix specific material.
This printed version of the tutorial includes copies of all the code, for easier reference. It also
includes a comprehensive table of contents, and index.
1
Getting Started
This tutorial can be read simply as a text, however it is intended to be interactive. That is, you
should be compiling, modifying and using the programs that are presented herein.
All the programs have been tested using the HiTech C compiler, and we assume that you have
a copy of this. In addition, you should have a copy of various updates and header files for the
C compiler, which appear on Applix User disks.
You can use either the builtin Applix 1616/OS editor edit, or the $30 Dr Doc editor in non-
document mode. Dr Doc is somewhat more powerful, however as it loads from disk, it is
slightly slower to get started. The source code has been edited to suit a tab setting of 5, so invoke
your editor with tabs set to a spacing of 5. For example, edit sourcecode.c 5 would let
you edit a file called sourcecode.c.
Before you can really use C, there are certain equipment requirements that must be met. You
must have a disk co-processor card, and at least one disk drive. If your drives are smaller than
800k, you will probably require two disk drives. We assume you either have 1616/OS Version4
multitasking, or else have an assign MRD available on your boot disk.
You should make use of the xpath, and the assign commands to set up your boot disk in a
form suitable for use with C. This should be done in the autoexec.shell file on your boot
disk, as set out below.
1-1
Note that relcc expects by default to find its C library files on the current drive in the /hitech
directory. It also expects to find its include files on the current drive in the /hitech/include
directory. We will explain what this means later, and there is a detailed discussion of the HiTech C
compiler at the end of the tutorial.
If all is correct, you can now compile a C file by typing
relcc -v file.c
The -v flag is to invoke the verbose mode, which produces the maximum information from the
compiler.
If you are experimenting, you may prefer to capture any errors encountered in a file, for later
study. If so, use
relcc -v file.c } errorfile
1-2
As you go through the example programs, you will find that every program is complete. There
are no program fragments that could be confusing. This allows you to see every requirement
that is needed to use any of the features of C as they are presented.
Some tutorials I have seen give very few, and very complex examples. They really serve more
to confuse the student. This tutorial is the complete opposite because it strives to cover each
new aspect of programming in as simple a context as possible. This method, however, leads to
a lack of knowledge in how the various parts are combined. For that reason, the last chapter is
devoted entirely to using the features taught in the earlier chapters. It will illustrate how to put
the various features together to create a usable program. They are given for your study, and are
not completely explained. Enough details of their operation are given to allow you to understand
how they work after you have completed all of the previous lessons.
1.6 List.xrel
This file will list the source files for you with line numbers and filename. To use it, simply type
"LIST" followed by the appropriate filename. Type list firstex.c now for an example.
The C source code is given later in Chapter 14 along with a brief description of its operation.
Applix 1616 users always have the inbuilt edit comand available to them, so this program
isn’t really essential.
1-3
2
Getting started in C
The best way to get started with C is to actually look at a program, so load the file named
trivial.c into edit and display it on the monitor.
The word "main" is very important, and must appear once, and only once, in every C program.
This is the point where execution is begun when the program is run. We will see later that this
does not have to be the first statement in the program, but it must exist as the entry point.
Following the "main" program name is a pair of parentheses, which are an indication to the
compiler that this is a function. We will cover exactly what a function is in due time. For now,
I suggest that you simply include the pair of parentheses.
The two curly brackets { }, properly called braces, are used to define the limits of the program
itself. The actual program statements go between the two braces and in this case, there are no
statements because the program does absolutely nothing. You can compile and run this program,
but since it has no executable statements, it does nothing. Keep in mind however, that it is a
valid C program.
The executable statement is another function. Once again, we will not worry about what a
function is, but only how to use this one. In order to output text to the monitor, it is put within
the function parentheses and bounded by quotation marks. The end result is that whatever is
included between the quotation marks will be displayed on the monitor when the program is
run.
Notice the semi-colon ; at the end of the line. C uses a semi-colon as a statement terminator,
so the semi-colon is required as a signal to the compiler that this line is complete. This programis
also executable, so you can compile and run it to see if it does what you think it should. With
some compilers, you may get an error message while compiling, indicating the printf() should
have been declared as an integer. Ignore this for the moment.
2-1
2.3 Another Program With More Output
Load the program wrtmore.c and display it on your monitor for an example of more output
and another small but important concept. You will see that there are four program statements
in this program, each one being a "printf" function statement. The top line will be executed first then
the next, and so on, until the fourth line is complete. The statements are executed in orderfrom
top to bottom.
main( )
{
printf("This is a line of text to output.\n");
printf("And this is another ");
printf("line of text.\n\n");
printf("This is the third line.\n");
}
Notice the funny character near the end of the first line, namely the backslash. The backslash
is used in the printf statement to indicate a special control character is following. In this case,
the "n" indicates that a "newline" is requested. This is an indication to return the cursor to the
left side of the monitor and move down one line. It is commonly referred to as a carriage
return/line feed. Any place within text that you desire, you can put a newline character and start
a new line. You could even put it in the middle of a word and split the word between two lines.
The C compiler considers the combination of the backslash and letter n as one character. The
exact characters used to indicate a newlin and carriage return are operating system specific.
MS-DOS, Unix, 1616/OS and Macintosh may vary one from the other.
A complete description of this program is now possible. The first printf outputs a line of text
and returns the carriage. The second printf outputs a line but does not return the carriage so the
third line is appended to that of the second, then followed by two carriage returns, resulting in
a blank line. Finally the fourth printf outputs a line followed by a carriage return and the program is
complete.
Compile and run this program to see if it does what you expect it to do. It would be a good idea
at this time for you to experiment by adding additional lines of printout to see if you understand
how the statements really work.
2-2
and is not one of the reserved words for C. Consult your manual for an exact definition of an
identifier for your compiler. In HiTech C, the construction of identifier names is the same as
in UNIX, however 31 characters and both cases are significant. The compiler prepends an
underscore to external references in the assembler pass. The final character on the line, the
semi-colon, is the statement terminator used in C.
We will see in a later chapter that additional integers could also be defined on the same line,
but we will not complicate the present situation.
Observing the main body of the program, you will notice that there are three statements that
assign a value to the variable "index", but only one at a time. The first one assigns the value of
13 to "index", and its value is printed out. (We will see how shortly.) Later, the value 27 is
assigned to "index", and finally 10 is assigned to it, each value being printed out. It should be
intuitively clear that "index" is indeed a variable and can store many different values. Please
note that many times the words "printed out" are used to mean "displayed on the monitor". You
will find that in many cases experienced programmers take this liberty, probably due to the
"printf" function being used for monitor display.
2-3
printf("used in C.\n");
}
/* One more comment for effect */
Comments are added to make a program more readable to you but the compiler must ignore the
comments. The slash star combination is used in C for comment delimiters. They are illustrated
in the program at hand. Please note that the program does not illustrate good commenting
practice, but is intended to illustrate where comments can go in a program. It is a very sloppy
looking program.
The first slash star combination introduces the first comment and the star at the end of the first
line terminates this comment. Note that this comment is prior to the beginning of the program
illustrating that a comment can precede the program itself. Good programming practice would
include a comment prior to the program with a short introductory description of the program.
The next comment is after the "main( )" program entry point and prior to the opening brace for
the program code itself.
The third comment starts after the first executable statement and continue for four lines. This
is perfectly legal because a comment can continue for as many lines as desired until it is ter-
minated. Note carefully that if anything were included in the blank spaces to the left of the three
continuation lines of the comment, it would be part of the comment and would not be compiled. The
last comment is located following the completion of the program, illustrating that comments can go
nearly anywhere in a C program.
Experiment with this program be adding comments in other places to see what will happen.
Comment out one of the printf statements by putting comment delimiters both before and after
it and see that it does not get printed out.
Comments are very important in any programming language because you will soon forget what you
did and why you did it. It will be much easier to modify or fix a well commented programa year
from now than one with few or no comments. You will very quickly develop your ownpersonal
style of commenting.
Some compilers allow you to "nest" comments which can be very handy if you need to "comment
out" a section of code during debugging. Check your compiler documentation for the availability
of this feature with your particular compiler. Compile and run comments.c at this time.
2-4
main( ) /* Main program starts here */{printf("Good form ");printf
("can aid in ");printf(" understanding a program.\n")
;printf("And bad form ");printf("can make a program ");
printf("unreadable.\n");}
How long will it take you to figure out what this program will do? It doesn’t matter to the
compiler which format style you use, but it will matter to you when you try to debug your
program. Compile this program and run it. You may be surprised to find that it is the same
program as the last one, except for the formatting. Don’t get too worried about formatting style
yet. You will have plenty of time to develop a style of your own as you learn the language. Be
observant of styles as you see C programs in magazines, books, and other publications.
This should pretty well cover the basic concepts of programming in C, but as there are many
other things to learn, we will forge ahead to additional program structure.
2-5
3
Program Control
3-1
i = i + 1;
} while (i < 5);
}
This program is nearly identical to the last one except that the loop begins with the reserved
word "do", followed by a compound statement in braces, then the reserved word "while", and
finally an expression in parentheses. The statements in the braces are executed repeatedly as
long as the expression in parentheses is true. When the expression in parentheses becomes false,
execution is terminated, and control passes to the statements following this statement.
Several things must be pointed out regarding this statement. Since the test is done at the end of
the loop, the statements in the braces will always be executed at least once. Secondly, if "i",
were not changed within the loop, the loop would never terminate, and hence the program would
never terminate. Finally, just like the while loop, if only one statement will be executed within
the loop, no braces are required. Compile and run this program to see if it does what you think
it should do.
It should come as no surprise to you that these loops can be nested. That is, one loop can be
included within the compound statement of another loop, and the nesting level has no limit.
3-2
/* This is an example of the if and if-else statements */
main()
{
int data;
for(data = 0;data < 10;data = data + 1) {
if (data == 2)
printf("Data is now equal to %d\n",data);
if (data < 5)
printf("Data is now %d, which is less than 5\n",data);
else
printf("Data is now %d, which is greater than 4\n",data);
} /* end of for loop */
}
Notice first, that there is a "for" loop with a compound statement as its executable part containing
two "if" statements. This is an example of how statement can be nested. It should be clear to
you that each of the "if" statements will be executed 10 times.
Consider the first "if" statement. It starts with the keyword "if" followed by an expression in
parentheses. If the expression is evaluated and found to be true, the single statement following
the "if" is executed. If false, the following statement is skipped. Here too, the single statement
can be replaced by a compound statement composed of several statements bounded by braces.
The expression "data" == 2" is simply asking if the value of data is equal to 2, this will be
explained in detail in the next chapter. (Simply suffice for now that if "data = 2" were used in
this context, it would mean a completely different thing.)
main( )
{
int xx;
for(xx = 5;xx < 15;xx = xx + 1){
if (xx == 8)
break;
printf("in the break loop, xx is now %d\n",xx);
}
for(xx = 5;xx < 15;xx = xx + 1){
if (xx == 8)
continue;
printf("In the continue loop, xx is the now %d\n",xx);
}
}
Notice that in the first "for" there is an if statement that calls a break if xx equals 8. The break
will jump out of the loop you are in and begin executing the statements following the loop,
effectively terminating the loop. This is a valuable statement when you need to jump out of a
loop depending on the value of some results calculated in the loop. In this case, when xx reaches 8,
the loop is terminated and the last value printed will be the previous value, namely 7.
3-3
The next "for" loop, contains a continue statement which does not cause termination of the loop
but jumps out of the present iteration. When the value of xx reaches 8 in this case, the program
will jump to the end of the loop and continue executing the loop, effectively eliminating the
printf statement during the pass through the loop when xx is eight. Compile and run the program to
see if it does what you expect.
3-4
main()
{
int dog,cat,pig;
goto real_start;
some_where:
printf("This is another line of the mess.\n");
goto stop_it;
/* the following section is the only section with a useable goto */
real_start:
for(dog = 1;dog < 6;dog++) {
for(cat = 1;cat < 6;cat++) {
for(pig = 1;pig < 4;pig++) {
printf("Dog = %d Cat = %d Pig = %d\n",dog,cat,pig);
if ((dog + cat + pig) > 8 ) goto enough;
};
};
};
enough: printf("Those are enough animals for now.\n");
/* this is the end of the section with a useable goto statement */
printf("\nThis is the first line out of the spaghetti code.\n");
goto there;
where:
printf("This is the third line of the spaghetti code.\n");
goto some_where;
there:
printf("this is the second line of the spaghetti code.\n");
goto where;
stop_it:
printf("This is the last line of the mess.\n");
}
To use a "goto" statement, you simply use the reserved word "goto", followed by the symbolic
name to which you wish to jump. The name is then placed anywhere in the program followed
by a colon. You are not allowed to jump into any loop, but you are allowed to jump out of a
loop. Also, you are not allowed to jump out of any function into another. These attempts will
be flagged by your compiler as an error if you attempt any of them.
This particular program is really a mess but it is a good example of why software writers are
trying to eliminate the use of the "goto" statement as much as possible. The only place in this
program where it is reasonable to use the "goto" is the one in line 17 where the program jumps
out of the three nested loops in one jump. In this case it would be rather messy to set up a
variable and jump successively out of all three loops but one "goto" statement gets you out of
all three.
Some persons say the "goto" statement should never be used under any circumstances but this
is rather narrow minded thinking. If there is a place where a "goto" will be best, feel free to use
it. It should not be abused however, as it is in the rest of the program on your monitor.
Entire books are written on "gotoless" programming, better known as Structured Programming.
These will be left to your study. One point of reference is the Visual Calculator described in
Chapter 14 of this tutorial. This program is contained in four separately compiled programs
and is a rather large complex program. If you spend some time studying the source code, you
will find that there is not a single "goto" statement anywhere in it. Compile and run gotoex.c
and study its output. It would be a good exercise to rewrite it and see how much more readable
it is when the statements are listed in order.
3-5
/***********************************************************/
/* This is a temperature conversion program written in */
/* the C programming language. This program generates */
/* and displays a table of farenheit and centigrade */
/* temperatures, and lists the freezing and boiling */
/* of water */
/***********************************************************/
main( )
{
int count; /* a loop control variable */
int farenheit; /* the temperature in farenheit degrees */
int centigrade; /* the temperature in centigrade degrees */
printf("Centigrade to farenheit temperature table\n\n");
for(count = -2;count <= 12;count = count + 1 ){
centigrade = 10 * count;
farenheit = 32 + (centigrade * 9)/5;
printf(" C =%4d F =%4d ",centigrade,farenheit);
if (centigrade == 0)
printf(" Freezing point of water");
if (centigrade == 100)
printf(" Boiling point of water");
printf("\n");
} /* end of for loop */
}
Of particular importance is the formatting. The header is simply several lines of comments
describing what the program does in a manner the catches the readers attention and is still
pleasing to the eye. You will eventually develop your own formatting style, but this is a good
way to start.
Also if you observe the for loop, you will notice that all of the contents of the compound statement
are indented a few spaces to the right of the "for" reserved word, and the closing brace is lined
up under the "f" in "for". This makes debugging a bit easier because the construction becomes
very obvious.
You will also notice that the "printf" statements that are in the "if" statements within the big
"for" loop are indented three additional spaces because they are part of another construct. This
is the first program in which we used more than one variable. The three variables are simply
defined on three different lines and are used in the same manner as a single variable was used
in previous programs. By defining them on different lines, we have opportunity to define each
with a comment.
3-6
if (x3 == 100)
printf("Boiling point of water");
printf("\n");
}
}
Compile and run this program to see that it does exactly what the last one did.
3-7
4
Assignment & Logical compares
Three variables are defined for use in the program and the rest of the program is merely a series
of illustrations of various assignments. The first two lines of the assignment statements assign
numerical values to "a" and "b", and the next four lines illustrate the five basic arithmetic
functions and how to use them. The fifth is the modulo operator and gives the remainder if the
two variables were divided. It can only be applied to "int" or "char" type variables, and of course
"int" extensions such as "long", "short", etc. Following these, there are two lines illustrating
how to combine some of the variables in some complex math expressions. All of the above
examples should require no comment except to say that none of the equations are meant to be
particularly useful except as illustrations.
The next two expressions are perfectly acceptable as given, but we will see later in this chapter
that there is another way to write these for more compact code.
This leaves us with the last two lines which may appear to you as being very strange. The C
compiler scans the assignment statement from right to left, (which may seem a bit odd since we
do not read that way), resulting in a very useful construct, namely the one given here. The
compiler finds the value 20, assigns it to "c", then continues to the left finding that the latest
result of a calculation should be assigned to "b". Thinking that the latest calculation resulted in
a 20, it assigns it to "b" also, and continues the leftward scan assigning the value 20 to "a" also.
This is a very useful construct when you are initializing a group of variables. The last statement
illustrates that it is possible to actually do some calculations to arrive at the value which will be
assigned to all three variables.
The program has no output, so compiling and executing this program will be very uninteresting.
Since you have already learned how to display some integer results using the "printf" function,
it would be to your advantage to add some output statements to this program to see if the various
statements do what you think they should do.
4-1
This would be a good time for a preliminary definition of a rule to be followed in C. The data
definitions are always given before any executable statements in any program block. This is
why the variables are defined first in this program and in any C program. If you try to define
a new variable after executing some statements, the compiler will issue an error.
Once again we have defined a few integer type variables which you should be fairly familiar
with by now, but we have added two new types, the "char", and the "float".
The "char" type of data is nearly the same as the integer except that it can only be assigned
values between zero and 255, since it is stored in only one byte of memory. The "char" type of
data is usually used for ASCII data, more commonly known as text. The text you are reading
was originally written on a computer with a word processor that stored the words in the computer
one character per byte. In contrast, the integer data type is stored in two bytes of computer
memory on most 8 bit and MS-DOS based microcomputers.
The Applix 1616 uses a 68000 chip with true 32 bit registers, so integers are 32 bits. This means
the range of an integer is not 32767, but 2147483648, or over two billion! On the Applix, a
short int may be more appropriate in some places.
4-2
4.4 How To Use The New Data Types
The first three lines of the program assign values to all nine of the defined variables so we can
manipulate some of the data between the different types.
Since, as mentioned above, a "char" data type is in reality an "integer" data type, no special
considerations need be taken to promote a "char" to an "int" variable. When going the other
way, there is no standard, so you may simply get garbage if the value is within the range of zero
to 255. In the second line therefore, when attempting to set x (a char) to -27, you may or may
not get a well defined answer, it depends on your particular implementation of C.
The third line illustrates the simplicity of translating an integer into a "float", simply assign it
the new value and the system will do the proper conversion. When going the other way however,
there is an added complication. Since there may be a fractional part of the floating point number,
the system must decide what to do with it. By definition, it will truncate it.
This program produces no output, and we haven’t covered a way to print out "char" and "float"
type variables, so you can’t really get in to this program and play with the results, but the next
program will cover this for you.
This file contains every standard simple data type available in the programming language C.
There are other types, but they are the compound types that we will cover in due time.
Observe the file. First we define a simple "int" followed by a "long int" and a "short int". Consult
your reference manual for an exact definition of these for your compiler, because they are not
consistent from implementation to implementation. The "unsigned" is next and is defined as
the same size as the "int" but with no sign. The "unsigned" then will cover a range of 0 to 65535 on
MS-DOS microcomputers, but as 0 to 4294967296 in HiTech. It should be pointed out that
when "long", "short", or "unsigned" is desired, the "int" is optional and left out by most
experienced programmers. We have already covered the "char" and the "float", which leaves
only the "double". The "double" usually covers a greater range than the "float" and has more
significant digits for more precise calculations. It also requires more memory to store a value
thanthesimple "float". Consult your referencemanual forthe rangeand accuracy of the"double".
Another diversion is in order at this point. Most compilers have provisions for floating point
math, but only double floating math. They will promote a "float" to a "double" before doing
calculations and therefore only one math library will be needed. Of course, this is totally
transparent to you, so you don’t need to worry about it. You may think that it would be best to
simply define every floating point variable as double, since they are promoted before use in any
calculations, but that may not be a good idea. A "float" variable requires 4 bytes of storage and
a "double" requires 8 bytes of storage, so if you have a large volume of floating point data to
store, the "double" will obviously require much more memory. Your compiler may require a
different number of bytes than 4 or 8. Consult your reference manual for the correct number of
bytes used by your compiler.
4-4
After defining the data types, a numerical value is assigned to each of the defined variables in
order to demonstrate the means of outputting each to the monitor.
4-5
x = y = z = 77;
if ((x == y) && (x == 77)) z = 33; /* This sets z = 33 */
if ((x >y) || (z > 12)) z = 22; /* This sets z = 22 */
if ((x && y && z) z = 11; /* This sets z = 11 */
if ((x = 1) && (y = 2) && (z = 3)) r = 12.00; /* This sets
x = 1, y = 2, z = 3, r = 12 .00 */
if ((x == 2) && (y = 3) && (z = 4)) r = 14.56; /* This doesn’t
change anything */
/* Fourth group of compares */
if (x == x); z = 27.345; /* z always gets changed */
if (x != x) z = 27.345; /* Nothing gets changed */
if (x = 0) z = 27.345; /* This sets x = 0, z is unchanged */
}
We begin by defining and initializing nine variables to use in the following compare statements.
This initialization is new to you and can be used to initialize variables while they are defined.
The first group of compare statements represents the simplest kinds of compares since they
simply compare two variables. Either variable could be replaced with a constant and still be
valid compare, but two variables is the general case. The first compare checks to see if "x" is
equal to "y" and it uses the double equal sign for the comparison. A single equal sign could be
used here but it would have a different meaning as we will see shortly. The second comparison
checks to see if "x" is greater than "z".
The third introduces the "NOT" operator, the exclamation, which can be used to invert the resultof
any logical compare. The fourth checks for "b" less than or equal to "c", and the last checks for
"r" not equal to "s". As we learned in the last chapter, if the result of the compare is true, the
statement following the "if" clause will be executed and the results are given in the comments. Note
that "less than" and "greater than or equal to" are also available, but are not illustrated here.
It would be well to mention the different format used for the "if" statement in this example
program. A carriage return is not required as a statement separator and by putting the conditional
readability of the overall program.
4-6
The next example should help clear up some of the above in your mind. In this example, "x" is
assigned the value of "y", and since the result is 11, the condition is non-zero, which is true,
and the variable "z" is therefore assigned 222.
The third example, in the second group, compares "x" to zero. If the result is true, meaning that
if "x" is not zero, then "z" is assigned the value of 333, which it will be. The last example in
this group illustrates the same concept, since the result will be true if "x" is non-zero. The
compare to zero is not actually needed and the result of the compare is true. The third and fourth
examples of this group are therefore identical.
4-7
4.13 This Is A Trick, Be Careful
The last example of the third group contains a bit of a trick, but since we have covered it above,
it is nothing new to you. Notice that the first part of the compare evaluates to "FALSE". The
remaining parts of the compare are not evaluated, because it is an "AND" and it will definitely
be resolved as a "FALSE" because the first term is false. If the program was dependent on the
value of "y" being set to 3 in the next part of the compare, it will fail because evaluation will
cease following the "FALSE" found in the first term. Likewise, "z" will not be set to 4, and the
variable "r" will not be changed.
4-8
a = a + 12; /* This adds 12 to a */
a += 12; /* This adds 12 more to a */
a *= 3.2; /* This multiplies a by 3.2 */
a -= b; /* This subtracts b from a */
a /= 10.0; /* This divides a by 10.0 */
/* conditional expression */
a = (b >= 3.0 ? 2.0 ; 10.5 ); /*This expression */
if (b >= 3.0) /* And this expression */
a = 2.0; /* are identical, both */
else /* will cause the same */
a = 10.5; /* result. */
c = (a > b?a:b); /* c will have the max of a or b */
c = (a > b?b:b); /* c will have the min of a or b */
}
In this program, some variables are defined and initialized in the same statements for use below.
The first should come as no surprise to you. The next two statements also add one to the value
of "x", but it is not intuitive that this is what happens. It is simply be definition that this is true.
Therefore, by definition of the C language, a double plus sign either before or after a variable
increments that variable by 1. Additionally, if the plus signs are before the variable, the variable
is incremented before it is used, and if the plus signs are after the variable, the variable is used,
then incremented. In the next statement, the value of "y" is assigned to the variable "z", then
"y" is incremented because the plus signs are after the variable "y". In the last statement of the
incrementing group of example statements, the value of "y" is incremented then its value is
assigned to the variable "z".
The next group of statements illustrate decrementing a variable by one. The definition works
exactly the same way for decrementing as it does for incrementing. If the minus signs are before the
variable, the variable is decremented, then used, and if the minus signs are after the variable, the
variable is used, then decremented.
4-9
The final two lines of this example program are given to illustrate a very compact way to assign
the greater of two variables "a" or "b" to "c", and to assign the lessor of the same two variables
to "c". Notice how efficient the code is in these two example.
4-10
5
Functions and variables
Actually this is not the first function we have encountered, because the "main" program we have
been using all along is technically a function, as is the "printf" function. The "printf" function
is a library function that was supplied with your compiler.
Notice the executable part of this program. It begins with a line that simply says "header()’,
which is the way to call any function. The parentheses are required because the C compiler uses
them to determine that it is a function call and not simply a misplaced variable. When the
program comes to this line of code, the function named "header" is called, its statements are
executed, and control returns to the statement following this call. Continuing on we come to a
"for" loop which will be executed 7 times and which calls another function named "square" each
time through the loop, and finally a function named "ending "will be called and executed. For
the moment ignore the "index" in the parentheses of the call to "square". We have seen that this
program therefore calls a header, 7 square calls, and an ending. Now we need to define the
functions.
5-1
5.2 Defining The Functions
Following the main program you will see another program that follows all of the rules set forth
so far for a "main" program except that it is named "header()". This is the function which is
called from within the main program. Each of these statements are executed, and when they
are all complete, control returns to the main program.
The first statement sets the variable "sum" equal to zero because we will use it to accumulate a
sum of squares. Since the variable "sum" is defined as an integer type variable prior to the main
program, it is available to be used in any of the following functions. It is called a "global"
variable, and it’s scope is the entire program and all functions. More will be said about the
scope of variables at the end of this chapter. The next statement outputs a header message to
the monitor. Program control then returns to the main programs in case there are no additional
statements to execute in this function.
It should be clear to you that the two executable lines from this function could be moved to the
main program, replacing the header call, and the program would do exactly the same thing that
it does as it is now written. This does not minimize the value of functions, it merely illustrates
the operation of this simple function is a simple way. You will find functions to be very valuable
in C programming.
5-2
we get to arrays and another method when we get to pointers. Until then the only way you will
be able to communicate back to the calling function will be with global variables. We have
already hinted at global variables above, and will discuss them in detail later in this chapter.
Continuing in the main program, we come to the last function call, the call to "ending". This
call simply calls the last function which has no local variables defined. It prints out a message
with the value of "sum" contained in it to end the program. The program ends by returning to
the main program and finding nothing else to do. Compile and run this program and observe
the output.
In the main program, we define two integers and begin a "for" loop which will be executed 8
times. The first statement of the for loop is "y = squ(x);", which is a new and rather strange
looking construct. From past experience, we should have no trouble understanding that the
"squ(x)" portion of the statement is a call to the "squ" function taking along the value of "x" as
a variable. Looking ahead to the function itself we find that the function prefers to call the
variable "in" and it proceeds to square the value of "in" and call the result "square". Finally, a
new kind of a statement appears, the "return" statement. The value within the parentheses is
assigned to the function itself and is returned as a usable value in the main program. Thus, the
function call "squ(x)" is assigned the value of the square and returned to the main program such
that "y" is then set equal to that value. If "x" were therefore assigned the value 4 prior to this
call, "y" would then be set to 16 as a result of this line of code.
Another way to think of this is to consider the grouping of characters "squ(x)" as another variable
with a value that is the square of "x", and this new variable can be used any place it is legal to
use a variable of its type. The value of "x" and "y" are then printed out.
To illustrate that the grouping of "squ(x)" can be thought of as just another variable, another
"for" loop is introduced in which the function call is placed in the print statement rather than
assigning it to a new variable.
5-3
One last point must be made, the type of variable returned must be defined in order to make
sense of the data, but the compiler will default the type to integer if none is specified. If any
other type is desired, it must be explicitly defined. How to do this will be demonstrated in the
next example program.
Compile and run this program.
It begins be defining a global floating point variable we will use later. Then in the "main" part
of the program, an integer is defined, followed by two floating point variables, and then by two
strange looking definitions. The expressions "sqr()" and "glsqr()" look like function calls and
they are. This is the proper way in C to define that a function will return a value that is not of
the type "int", but of some other type, in this case "float". This tells the compiler that when a
value is returned from either of these two functions, it will be of type "float".
Now refer to the function "sqr" near the center of the listing and you will see that the function
name is preceded by the name "float". This is an indication to the compiler that this function
will return a value of type "float" to any program that calls it. The function is now compatible
with the call to it. The line following the function name contains "float inval;", which indicates
to the compiler that the variable passed to this function from the calling program will be of type
"float".
The next function, namely "glsqr", will also return a "float" type variable, but it uses a global
variable for input. It also does the squaring right within the return statement and therefore has
no need to define a separate variable to store the product.
The overall structure of this program should pose no problem and will not be discussed in any
further detail. As is customary with all example programs, compile and run this program.
5-4
There will be times that you will have a need for a function to return a pointer as a result of
some calculation. There is a way to define a function so that it does just that. We haven’t studied
pointers yet, but we will soon. This is just a short preview of things to come.
The first variable defined is a global variable "count" which is available to any function in the
program since it is defined before any of the functions. In addition, it is always available because
it does not come and go as the program is executed. (That will make sense shortly.) Farther
down in the program, another global variable named "counter" is defined which is also global
but is not available to the main program since it is defined following the main program. A global
variable is any variable that is defined outside of any function. Note that both of these variables
are sometimes referred to as external variables because they are external to any functions.
Return to the main program and you will see the variable "index" defined as an integer. Ignore
the word "register" for the moment. This variable is only available within the main program
because that is where it is defined. In addition, it is an "automatic" variable, which means that
it only comes into existence when the function in which it is contained is invoked, and ceases
to exist when the function is finished. This really means nothing here because the main program
is always in operation, even when it gives control to another function. Another integer is defined
5-5
within the "for" braces, namely "stuff". Any pairing of braces can contain a variable definition
which will be valid and available only while the program is executing statements within those
braces. The variable will be an "automatic" variable and will cease to exist when execution
leaves the braces. This is convenient to use for a loop counter or some other very localized
variable.
5-6
Register variables are only available for use with integer and character type variables. This may
or may not include some of the other integer-like variables such as unsigned, long, or short.
Check the documentation for your compiler.
Register variables are allowed in HiTech C, with up to four non-pointer register variables (in
68000 registers D4 to D7), and up to three pointer register variables (in A3 to A5). This usage
does not conflict with the 1616/OS usage of D0 to D2 and A0 to A2). As MS-DOS compilers
typically only allow two register variables, many programmers do not make extensive use of
register variables, so you can often gain a little extra speed when converting programs by a
wideruse of register variables (the compiler will ignore the register variable request if no registers
are available, and treat the variable as an ordinary one).
5-7
count_dn(count);
printf("Now the count is %d\n",count);
}
Recursion is nothing more than a function that calls itself. It is therefore in a loop which must
have a way of terminating. In the program on your monitor, the variable "index" is set to 8, and
is used as the argument to the function "count_dn". The function simply decrements the variable,
prints it out in a message, and if the variable is not zero, it calls itself, where it decrements it
again, prints it, etc. etc. etc. Finally, the variable will reach zero, and the function will not call
itself again. Instead, it will return to the prior time it called itself, and return again, until finally
it will return to the main program and will return to DOS.
For purposes of understanding you can think of it as having 8 copies of the function "count_dn"
available and it simply called all of them one at a time, keeping track of which copy it was in
at any given time. That is not what actually happened, but it is a reasonable illustration for you
to begin understanding what it was really doing.
main( )
{
char line_of_char[80];
int index = 0;
strcpy(line_of_char," this is a string.\n");
/* the leading space in this */
/* string is required so the */
/* the last character "t" in */
/* "this" is printed when */
/* the string is printed */
/* backwards due to the */
/* index being incremented */
5-8
/* to 1 before the the */
/* printf statement for */
/* printing the line back- */
/* wards */
forward_and_backwards(line_of_char,index);
}
forward_and_backwards(line_of_char,index)
char line_of_char[];
int index;
{
if (line_of_char[index]) {
printf("%c",line_of_char[index]);
index++;
forward_and_backwards(line_of_char,index);
}
printf("%c",line_of_char[index]);
}
Each successive call to the function named "forward_and_backward" causes one character of
the message to be printed. Additionally, each time the function ends, one of the characters is
printed again, this time backwards as the string of recursive function calls is retraced.
Don’t worry about the character array defined in line 3 or the other new material presented here.
After you complete chapter 7 of this tutorial, this program will make sense. It was felt that
introducing a second example of recursion was important so this file is included here.
One additional feature is built into this program in the IBM PC version. If you observe the two
calls to the function, and the function itself, you will see that the function name is spelled three
different ways in the last few characters in the original IBM version. The IBM compiler doesn’t
care how they are spelled because it only uses the first 8 characters of the function name so as
far as it is concerned, the function is named "forward_". The remaining characters are simply
ignored. If your compiler uses more than 8 characters as being significant, as does Hi-Tech,
you will need to change two of the names so that all three names are identical, as we have done.
Compile and run this program and observe the results.
Notice the first four lines of the program each starting with the word "#define". This is the way
all defines and macros are defined. Before the actual compilation starts, the compiler goes
through a preprocessor pass to resolve all of the defines. In the present case, it will find every
place in the program where the combination "START" is found and it will simply replace it with
the 0 since that is the definition. The compiler itself will never see the word "START", so as
far as the compiler is concerned, the zeros were always there. It should be clear to you by now
that putting the word "START" in your program instead of the numeral 0 is only a convenience
to you and actually acts like a comment since the word "START" helps you to understand what
the zero is used for.
In the case of a very small program, such as that before you, it doesn’t really matter what you
use. If, however, you had a 2000 line program before you with 27 references to the START, it
would be a completely different matter. If you wanted to change all of the STARTS in the
program to a new number, it would be simple to change the one #define, but difficult, and
possible disastrous if you missed one or two of the references.
In the same manner, the preprocessor will find all occurrence of the word "ENDING" and change
them to 9, then the compiler will operate on the changed file with no knowledge that "ENDING"
ever existed.
It is a fairly common practice in C programming to use all capital letters for a symbolic constant
such as "START" and "ENDING" and use all lower case letters for variable names. You can
use any method you choose since it is mostly a matter of personal taste.
6-1
6.3 What Is A Macro?
A macro is nothing more than another define, but since it is capable of at least appearing to
perform some logical decisions or some math functions, it has a unique name. Consider the
third line of the program on your screen for an example of a macro. In this case, anytime the
preprocessor finds the word "MAX" followed by a group in parentheses, it expects to find two
terms in the parentheses and will do a replacement of the terms into the second definition. Thus the
first term will replace every "A" in the second definition and the second term will replace every
"B" in the second definition. When line 12 of the program is reached, "index" will be
substituted for every "A", and "count" will be substituted for every "B". Remembering the
cryptic construct we studied a couple of chapters ago will reveal that "mx" will receive the
maximum value of "index" or "count". In like manner, the "MIN" macro will result in "mn"
receiving the minimum value of "index" or "count". The results are then printed out. There are
a lot of seemingly extra, they are essential. We will discuss the extra parentheses in our next
program.
Compiler and run define.c.
The first line defines a macro named "WRONG" that appears to get the cube of "A", and indeed
it does in some cases, but it fails miserably in others. The second macro named "CUBE" actually
does get the cube in all cases.
Consider the program itself where the CUBE of i+offset is calculated. If i is 1, which it is the
first time through, then we will be looking for the cube of 1+5 = 6, which will result in 216.
When using "CUBE", we group the values like this, (1+5)*(1+5)*(1+5) = 6*6*6 = 216.
However, when we use WRONG, we group them as 1+5*1+5*1+5 = 1+5+5+5 = 16 which is
a wrong answer. The parentheses are therefore required to properly group the variables together.
It should be clear to you that either "CUBE" or "WRONG" would arrive at a correct answer for
a single term replacement such as we did in the last program. The correct values of the cube
and the square of the numbers are printed out as well as the wrong values for your inspection.
The remainder of the program is simple and will be left to your inspection and understanding.
6-2
6.5 Programming Exercise
1. Write a program to count from 7 to -5 by counting down. Use *define statements to define
the limits. (Hint, you will need to use a decrementing variable in the third part of the "for" loop
control.
6-3
7
Strings and Arrays
First we define four strings. Next we come to a new function that you will find very useful, the
"strcpy" function, or string copy. It copies from one string to another until it comes to the NULL
character. It is easy to remember which one gets copies to which is you think of them like an
assignment statement. Thus if you were to say, for example, "x = 23;", the data is copied from
the right entity to the left one. In the "strcpy" function, the data is also copied from the right
entity to the left, so that after execution of the first statement, name1 will contain the string
"Rosalinda", but without the double quotes, they are the complier’s way of knowing that you
are defining a string.
Notice that the array is defined in much the same way we defined an array of char in order to
do the string manipulations in the last section. We have 12 integer variables to work, with not
counting the one named "index". The names of the variables are "values[0]", "values[1]", ... ,
and "values[11]". Next we have a loop to assign nonsense, but well defined, data to each of the
12 variables, then print all 12 out. You should have no trouble following this program, but be
sure you understand it. Compile and run it to see if it does what you expect it to do.
In this program, we define an array of 20 variables named "matrix", then assign some nonsense
data to the variables, and print out the first five. Then we call the function "dosome" taking
along the entire array by putting the name of the array in the parentheses.
The function "dosome" has a name in its parentheses also but it prefers to call the array "list".
The function needs to be told that it is really getting an array passed to it and that the array is
of type "int". The following line, prior to the bracket which starts the program, does that by
defining "list" as an integer type variable and including the square brackets to indicate an array.
It is not necessary to tell the function how many elements are in the array, but you could if you
so desired. Generally a function works with an array until some end-of-data marker is found,
such as a NULL for a string, or some other previously defined data or pattern. Many times,
another piece of data is passed to the function with a count of how many elements to work with. On
our present illustration, we will use a fixed number of elements to keep it simple.
So far nothing is different from the previous functions we have called except that we have passed
more data points to the function this time than we ever have before, having passed 20 integer
values. We print out the first 5 again to see if they did indeed get passed here. Then we add ten
to each of the elements and print out the new values. Finally we return to the main programand
print out the same 5 data points. We find that we have indeed modified the data in the function,
and when we returned to the main program, we brought the changes back. Compileand run this
program to verify this conclusion.
The variable "big" is an 8 by 8 array that contains 8 times 8 or 64 elements total. The first
element is "big[0][0]", and the last is "big[7][7]". Another array named "huge" is also defined
which is not square to illustrate that the array need not be square. Both are filled up with data,
one representing a multiplication table and the other being formed into an addition table.
To illustrate that individual elements can be modified at will, one of the elements of "big" is
assigned the value from one of the elements of "huge" after being multiplied by 22. Next
"big[2][2]" is assigned the arbitrary value of 5, and this value is used for the subscripts of the
next assignment statement. The third assignment statement is in reality "big[5][5] = 177"
because each of the subscripts contain the value 5. This is only done to illustrate that any valid
expression can be used for a subscript. It must only meet two conditions, it must be an integer
(although a "char" will work just as well), and it must be within the range of the subscript it is
being used for.
The entire matrix variable "big" is printed out in a square form so you can check the values to
see if they did get set the way you expected them to.
For the moment, ignore the declaration statement where we define "index" and two other fields
beginning with a star. It is properly called an asterisk, but for reasons we will see later, let’s
agree to call it a star. If you observe the first statement, it should be clear that we assign the
value of 39 to the variable "index". This is no surprise, we have been doing it for several
programs now. The next statement however, says to assign to "pt1" a strange looking value,
namely the variable "index" with an ampersand in front of it. In this example, pt1 and pt2 are
pointers, and the variable "index" is a simple variable. Now we have problem. We need to learn
how to use pointers in a program, but to do so requires that first we define the means of using
the pointers in the program.
The following two rules will be somewhat confusing to you at first but we need to state the
definitions before we can use them. Take your time, and the whole thing will clear up very
quickly.
8-1 Pointers
Assume for the moment that "pt1" and "pt2" are pointers (we will see how to define them shortly).
As pointers, they do not contain a variable value but an address of a variable and can be used
to point to a variable. Line six of the program assigns the pointer "pt1" to point to the variable
we have already defined as "index" to "pt1". Since we have a pointer to "index", we can
manipulate the value of "index" by using either the variable name itself, or the pointer.
Line nine modifies the value by using the pointer. Since the pointer "pt1" points to the variable
"index", then putting a star in front of the pointer name refers to the memory location to which
it is pointing. Line nine therefore assigns to "index" the value of 13. Anyplace in the program
where it is permissible to use the variable name "index", it is also permissible to use the name
"*pt1" since they are identical in meaning until the pointer is reassigned to some other variable.
Pointers 8-2
8.7 The Second Program With Pointers
In these few pages so far on pointers, we have covered a lot of territory, but it is important
territory. We still have a lot of material to cover so stay in tune as we continue this important
aspect of C. Load the next file named pointer2.c and display it on your monitor so we can
continue our study.
main( )
{
char strg[40],*there,one,two;
int *pt,list[100],index;
strcpy(strg,"This is a character string.");
one = strg[0]; /* one and two are identical */
two = *strg;
printf("The first output is %c %c\n",one,two);
one = strg[8]; /* one and two are identical */
two = *(strg+8);
printf("The second output is %c %c %c\n",one,two);
there = strg+10; /* strg+10 is identical to strg[10] */
printf("The third output is %c\n",strg[10]);
printf("The fourth output is %c\n",*there);
for (index = 0;index < 100;index++)
list[index] = index + 100;
pt = list + 27;
printf("The fifth output is %d\n",list[27]);
printf("The sixth output is %d\n",*pt);
}
In this program we have defined several variables and two pointers. The first pointer named
"there" is a pointer to a "char" type variable and the second named "pt" points to an "int" type
variable. Notice also that we have defined two array variable named "strg" and "list". We will
use them to show the correspondence between pointers and array names.
8-3 Pointers
The C programming language takes care of indexing for us automatically by adjusting the
indexing for the type of variable the pointer is pointing to. In this case, the index of 8 is simply
added to the pointer value variable before looking up the desired result because a "char" type
variable is one byte long. If we were using a pointer to an "int" type variable, the index would
be doubled and added to the pointer before looking up the value because an "int" type variable
uses two bytes per value stored. When we get to the chapter on structures, we will see that a
variable can have many, even into the hundreds or thousands, of characters per variable, but the
indexing will be handled automatically for us by the system.
Since "there" is already a pointer, it can be assigned the value of the eleventh element of "strg"
by the statement in line 16 of the program. Remember that since "there" is a true pointer, it can
be assigned any value as long as that value represents a "char" type of address. It should be
clear that the pointers must be "typed" in order to allow the pointer arithmetic described in the
last paragraph to be done properly. The third and fourth outputs will be the same, namely the
letter "c".
Pointers 8-4
fixup(nuts,fruit) /* nuts is an integer value */
int nuts,*fruit; /* fruit points to an integer */
{
printf("The value are %d %d\n",nuts,*fruit);
nuts = 135;
*fruit = 172;
printf("The values are %d %d\n",nuts,*fruit);
}
In twoway.c, there are two variables defined in the main program "pecans" and "apples".
Notice that neither of these if defined as a pointer. We assign values to both of these and print
them out, then call the function "fixup" taking with us both of these values. The variable "pecans" is
simply sent to the function, but the address of the variable "apples" is sent to the function. Now
we have a problem. The two arguments are not the same, the second is a pointer to a variable.
We must somehow alert the function to the fact that it is supposed to receive an integervariable
and a pointer to an integer variable. This turns out to be very simple. Notice that theparameter
definitions in the function define "nuts" as an integer, and "fruit" as a pointer to an integer. The
call in the main program therefore is now in agreement with the function heading and the
program interface will work just fine.
In the body of the function, we print the two values sent to the function, then modify them and
print the new values out. This should be perfectly clear to you by now. The surprise occurs
when we return to the main program and print out the two values again. We will find that the
value of pecans will be restored to its value before the function call because the C language
makes a copy of the item in question and takes the copy to the called function, leaving the
original intact. In the case of the variable "apples", we made a copy of a pointer to the variable
and took the copy of the pointer to the function. Since we had a pointer to the original variable,
even though the pointer was a copy, we had access to the original variable and could change it
in the function. When we returned to the main program, we found a changed value in "apples"
when we printed it out.
By using a pointer in a function call, we can have access to the data in the function and change
it in such a way that when we return to the calling program, we have a changed value of data.
It must be pointed out however, that if you modify the value of the pointer itself in the function,
you will have restored pointer when you return because the pointer you use in the function is a
copy of the original. In this example, there was no pointer in the main program because we
simply sent the address to the function, but in many programs you will use pointers in function
calls. One of the places you will find need for pointers in function calls will be when you request
data input using standard input/output routines. These will be covered in the next two chapters.
Compile and run twoway.c and observe the output.
8-5 Pointers
2. Modify the program to print out the string backwards by pointing to the end and using a
decrementing pointer.
Pointers 8-6
9
Standard Input/Output
The first thing you notice is the first line of the file, the #include "stdio.h" line. This is very
much like the #define we have already studied, except that instead of a simple substitution, an
entire file is read in at this point. The system will find the file named "stdio.h" and read its entire
contents in, replacing this statement. Obviously then, the file named "stdio.h" must contain
valid C source statements that can be compiled as part of a program. This particular file is
composed of several standard #defines to define some of the standard I/O operations. The file
is called a header file and you will find several different header files on the source disks that
came with your compiler. Each of the header files has a specific purpose and any or all of them
can be included in any program.
Most C compilers use the double quote marks to indicate that the "include" file will be found
in the current directory. A few use the "less than" and "greater than" signs to indicate that the
file will be found in a standard header file. Nearly all MSDOS C compilers use the double
quotes, and most require the "include" file to be in the default directory. All of the programs in
this tutorial have the double quotes in the "include" statements. If your compiler uses the other
notation, you will have to change them before compiling.
Instead of reading in a character at a time, as we have in the last three files, we read in an entire
integer value with one call using the function named "scanf". This function is very similar to
the "printf" that you have been using for quite some time by now except that it is used for input
instead of output. Examine the line with the "scanf" and you will notice that it does not ask for
the variable "valin" directly, but gives the address of the variable since it expects to have a value
returned for the function. Recall that a function must have the address of a variable in order to
return the value to the calling program. Failing to supply a pointer in the "scanf" function is
probably the most common problem encountered in using this function.
The function "scanf" scans the input line until it finds the first data field. It ignores leading
blanks and in this case, it reads integer characters until it finds a blank or an invalid decimal
character, at which time it stops reading and returns the value.
Remembering our discussion above about the way the 1616/OS input buffer works, it should
be clear that nothing is actually acted on until a complete line is entered and it is terminated by
a carriage return. At this time, the buffer reading is input, and our program will search across
the line reading all integer values it can find until the line is completely scanned. This is because
we are in a loop and we tell it to find a value, print it, find another, print it, etc. If you enter
The variable in the "scanf" does not need an & because "big" is an array variable and by definition
it is already a pointer. This program should require no additional explanation. Compile and run
it to see if it works the way you except.
You probably got a surprise when you ran it because it separated your sentence into separate
words. When used in the string mode of input, "scanf" reads characters into the string until it
comes to either the end of a line or a blank character. Therefore, it reads a word, finds the blank
following it, and displays the result. Since we are in a loop, this program continues to read
words until it exhausts the DOS input buffer. We have written this program to stop whenever
it finds a capital X in column 1, but since the sentence is split up into individual words, it will
stop anytime a word begins with capital X. Try entering a 5 word sentence with a capital X as
the first character in the third word. You should get the first three words displayed, and the last
two simply ignored when program stops.
Try entering more than 24 characters to see what the program does. It should generate an error,
but that will be highly dependent on the system you are using. In an actual program, it is your
responsibility to count characters and stop when the input buffer is full. You may be getting
the feeling that a lot of responsibility is placed on you when writing in C. It is, but you also get
a lot of flexibility in the bargain too.
In inmem.c, we define a few variables, then assign some values to the ones named "numbers"
for illustrative purposes and then use a "sprintf" function except that instead of printing the line
of output to a device, it prints the line of formatted output to a character string in memory. In
this case the string goes to the string variable "line", because that is the string name we inserted
as the first argument in the "sprintf" function. The spaces after the 2nd %d were put there to
illustrate that the next function will search properly across the line. We print the resulting string
an+find that the output is identical to what it would have been by using a "printf" instead of the
"sprintf" in the first place. You will see that when you compile and run the program shortly.
Since the generated string is still in memory, we can now read it with the function "sscanf". We
tell the function in its first argument that "line" is the string to use for its input, and the remaining
parts of the line are exactly what we would use if we were going to use the "scanf" function and
read data from outside the computer. Note that it is essential that we use pointers to the data
because we want to return data from a function. Just to illustrate that there are many ways to
declare a pointer several methods are used, but all are pointers. The first two simply declare
do {
c = fscanf(fp1,"%s",oneword); /* got one word from the file */
printf("%s\n",oneword); /* display it on the monitor */
} while (c != EOF); /* repeat until EOF */
fclose(fp1);
}
This program is nearly identical as the last except that this program uses the "fscanf" function
to read in a string at a time. Because the "fscanf" function stops reading when it finds a space
or a newline character, it will read a word at a time, and display the results one word to a line.
You will see this when you compile and run it, but first we must examine a programming
problem.
Once again, we open TENLINES.TXT for reading and we open PRN for writing. Printing is
identical to writing data to a disk file except that we use a standard name for the filename. There are
no definite standards as far as the name or names to be used for the printer, but the 1616/OS
names are, "CENT:", "SA:", and "SB:". Check your documentation for your particular
implementation.
Some of the newest MS-DOS compilers use a predefined file pointer such as "stdprn" for the
print file. Once again, check your documentation.
The program is simply a loop in which a character is read, and if it is not the EOF, it is displayed
and printed. When the EOF is found, the input file and the printer output files are both closed.
You can now erase TENLINES.TXT from your disk. We will not be using it in any of the later
chapters.
The first difference shows up in the definition of variables following the structure definition.
In this program we define a pointer named "point" which is defined as a pointer that points to
The functions in this group of functions are used to do bitwise operations, meaning that the
operations are performed on the bits as though they were individual bits. No carry from bit to
bit is performed as would be done with a binary addition. Even though the operations are
performed on a single bit basis, an entire byte or integer variable can be operated on in one
instruction. The operators and the operations they perform are given in the following table;
& Logical AND, if both bits are 1, the result is 1.
| Logical OR, if either bit is one, the result is 1.
^ Logical XOR, (exclusive OR), if one and only one bit is 1, the result is 1.
~ Logical invert, if the bit is 1, the result is 0, and if the bit is 0, the result is 1.
The example program uses several fields that are combined in each of the ways given above.
The data is in hexadecimal format. It will be assumed that you already know hexadecimal format
if you need to use these operations. If you don’t, you will need to study it on your own. Teaching
the hexadecimal format of numbers is beyond the scope of this tutorial.
Run the program and observe the output.
Once again the operations are carried out and displayed using the hexadecimal format. The
program should be simple for you to understand on your own, there is no tricky code.
/* ******************************************************* open_file */
/* This functions opens input file on the command line,if there was */
/* one defined. Otherwise, requests a file to open and opens the */
/* requested file. */
/* ***************************************************************** */
open_file(no,name)
int no; /* number of arguments on command line */
char *name; /* first argument from command line */
{
strcpy(filename,name); /* copy name for printing header */
file_point = NULL; /* if no name was given in command */
if (no == 2) { /* 2nd field in command is filename */
file_point = fopen(name,"r"); /* open requested file */
if (file_point == NULL) /* NULL if file doesn’t exist */
printf("File name on command line doesn’t exist!\n");
}
do {
if (file_point == NULL) { /* no filename yet */
printf("Enter filename -> ");
scanf("%s",filename);
file_point = fopen(filename,"r"); /* open file */
if (file_point == NULL) /* NULL if file no exist */
printf("Filename doesn’t exist,try again.\n");
}
} while (file_point == NULL); /* continue until good filename */
}
/* ****************************************************** open_print_file
*/
/* This function opens the printer file to the standard printer.
*/
/* **********************************************************************
*/
open_print_file()
{
print_file_point = fopen("PRN","w"); /* open printer file */
}
/* ********************************************************* print_a_line
*/
/* This routine prints a text line and checks to see if there is room for
*/
/* another on the page. If not,it starts a new page with a new header.
*/
/* This routine calls several other local routines.
*/
/* **********************************************************************
*/
18.1 Relcc.c
This is the original HiTech c.c modified by Colin McCormack and Andrew Morton so that it
produces relocatable .xrel files straight off, instead of .exec fixed position files. The -r
flag will produce an .exec file. This flag needs to be near the front of the relcc command
line, as there is a bit of an error in the coding of the flag.
The compiler temporary files are placed in the directory /temp. You should assign /temp
/rd or assign /temp . or some other safe place before using relcc. The compiler has
been altered to define the include paths /hitech and /hitech/include to the prepro- cessor.
The identifier applix1616 is defined to the preprocessor, rather than applix, whichcaused
problems substituting #include <applix>.
Relcc fires off the compiler passes using the exec system call, rather than searching for them.
The compiler passes should reside somewhere in your normal search path (see the xpath
command in your Users Manual.)
Relcc has a peek at the Alt C flag, and terminates with an exit code of -1 if an Alt C is detected.
You may have to lean on the Alt C to ctach it; the exec system call clears the flag at the start
of each pass.
Each compiler pass closes the standard output, input and error file descriptors upon exit, so they
are not available when the next pass is invoked. Relcc modifies the close system call to keep
these files open.
The standard directory for include files is /hitech/include. Assign this to wherever you
actually keep the include files before using the compiler. Specify the flag -I/hitech/in-
clude to relcc (actually the preprocessor) to find everything.
The standard directory for the libraries and the runtime startup code (crtapp, etc) is /hitech.
Assign this as assign /hitech /f0/hitech or wherever the libraries really are.
Relcc.xrel is relocatable code, so it hangs about in the top of memory when the compiler passes
are crunching. Possibly you will have make.xrel somewhere above it also. If the compiler
crashes (probably during the assembly pass), you will have to shrink the RAM disk and the
stack space to around 100k-150k in total. Sorry about that. Details of how to use buildmrd
to alter the mrdrivers file on your boot disk are given in the Technical ReferenceManual, and
in the new User Programs Manual.
Index i
dumbconv.c, 3-6 K & R, 9-1
dynamic allocation, 12-1
dynamic variables, 12-2 library functions, 5-7
dynlink.c, 12-5 linefeed, 9-4
dynlist.c, 12-1 linked list, 12-5
list.c, 14-1, 14-6
edit, 1-1 local variables, 5-6
else, 3-3 logical compares, 4-5
end-of-marker, 7-5 logical evaluation, 4-7
EOF problem, 10-4 logical functions, 13-3
long int, 4-1
false, 4-6 loop using while, 3-1
fgets(), 10-5 loops, nesting limits, 3-2
file input output, 10-1 lottypes.c, 4-3
float, 4-2 lower case, 13-1
floating point array, 7-4
floating point functions, 5-4 macro, 6-2
floatsq.c, 5-4 macro.c, 6-2
fnction keys, 9-9 main, 2-1
fopen(), 10-3 malloc(), 12-2, 12-3
for loop, 3-2 memory allocate, 12-2
forloop.c, 3-2 mixing data types, 4-2
formatting style, 2-4, 3-6 modulo, 4-1
formout.c, 10-1 mortypes.c, 4-2
fprintf(), 9-8 multi dimension arays, 7-6
free(), 12-4 multiary.c, 7-6
fscanf(), 10-4
function, 2-1 named structures, 11-4
Functions and Variables, 5-1 nested.c, 11-4
nested loops, 3-2
global variables, 5-5 nested structures, 11-4
goodform.c, 2-4 newline \n, 2-2
goto, 3-4 not, 4-6
gotoex.c, 3-4 NULL character, 7-1
Index ii
printing a file, 10-6 struct, 11-1
problem compares, 4-8 struct1.c, 11-1
program control, 3-1 struct2.c, 11-2
promote char to int, 4-2 struct3.c, 11-3
putc(), 10-3 structured programming, 3-5
structures, 11-1
read a file, 10-3 structures and unions, 11-1
read a line, 10-5 style in formatting, 2-4
read a word, 10-4 subscripted arrays, 7-6
readchar.c, 10-3 sumsqres.c, 5-1
readline.c, 10-5 switch, 3-4
readtext.c, 10-4 switch.c, 3-4
recursion, 5-7 switching variable, 3-4
recurson.c, 5-7 symbolic constant, 6-1
register variables, 5-6
relcc.xrel, 1-1 tempconv.c, 3-5
return, 5-3 temporary files, 1-1
return a value, 5-3 tenlines.txt, 10-2
returning data in arrays, 7-4 trivial.c, 2-1
true, 4-6
scanf(), 9-5 twoway.c, 8-4
scope.c, 5-5 typedef, 11-8
scope of variables, 5-5
segments, 12-2 uglyform.c, 2-4
semi-colon ;, 2-1 underline, 1-2
shift instruction, 13-4 union1.c, 11-6
shifter.c, 13-4 union2.c, 11-7
short int, 4-1 unions, 11-6
simpleio.c, 9-1 uplow.c, 13-1
single character outpt, 10-2 upper and lower case, 13-1
singleio.c, 9-3 user defined functions, 5-1
sorting strings, 7-3 user defined type, 11-1
special.c, 9-8
sprintf(), 9-7 value passing, 5-2
square brackets [ ] array, 7-1 variable characteristics, 4-3
squares.c, 5-3 variable filename, 10-5
stack, 5-8 variable output %, 2-3
stack overflow, 11-6 variables scope, 5-5
standard function libraries, 5-7 vc.c, 14-9
standard input output, 9-1 visual calculator, 14-9, 15-1
star * pointer, 8-1 visual calculator tutorial, 15-1
star slash /* comments, 2-4
statement terminator ;, 2-1 whatnext.c, 14-4
static variables, 5-6 while.c, 3-1
stdio.h header file, 9-1 while loop, 3-1
store *, 8-1 wrtmore.c, 2-2
strcat function, 7-3 wrtsome.c, 2-1
strcmp function, 7-3
strcpy(), 10-3 XOR ^, 13-3
strcpy function, 7-2 xpath, 1-1
string ends in null, 7-1
string variable as pointer, 8-3 zero, null character, 7-1
stringin.c, 9-6
strings, 7-1
strings.c, 7-2
Strings and Arrays, 7-1
Index iii
Table of Contents
1 Getting Started ..................................................................................... 1-1
1.1 C Boot Disk ....................................................................................................1-1
1.2 What Is An Identifier? ...................................................................................1-2
1.3 What About The Underline? .........................................................................1-2
1.4 How This Tutorial Is Written ........................................................................1-2
1.5 A Discussion Of Some Of The Files ...............................................................1-3
1.6 List.xrel...........................................................................................................1-3
i
5 Functions and variables ....................................................................... 5-1
5.1 Our First User Defined Function ..................................................................5-1
5.2 Defining The Functions..................................................................................5-2
5.3 Passing A Value To A Function .....................................................................5-2
5.4 More About Passing A Value To A Function................................................5-2
5.5 Now To Confess A Little Lie ..........................................................................5-3
5.6 Floating Point Functions ................................................................................5-4
5.7 Scope Of Variables .........................................................................................5-5
5.8 More On "Automatic" Variables ..................................................................5-6
5.9 What Are Static Variables? ...........................................................................5-6
5.10 Using The Same Name Again ......................................................................5-6
5.11 What Is A Register Variable? ......................................................................5-6
5.12 Where Do I Define Variables? .....................................................................5-7
5.13 Standard Function Libraries .......................................................................5-7
5.14 What Is Recursion? ......................................................................................5-7
5.15 What Did It Do? ...........................................................................................5-8
5.16 Another Example Of Recursion ..................................................................5-8
5.17 Programming Exercises ...............................................................................5-9
ii
8.13 Programming Exercises ...............................................................................8-5
iii
12 Dynamic Allocation ............................................................................ 12-1
12.1 What Is Dynamic Allocation? ......................................................................12-1
12.2 Dynamic Variable Creation .........................................................................12-2
12.3 What Is A Heap? ..........................................................................................12-2
12.4 More About Segments..................................................................................12-2
12.5 Back To The "Malloc" Function .................................................................12-3
12.6 What Is A Cast? ...........................................................................................12-3
12.7 Using The Dynamically Allocated Memory Block ......................................12-3
12.8 Getting Rid Of The Dynamically Allocated Data .......................................12-4
12.9 That Was A Lot Of Discussion ....................................................................12-4
12.10 An Array Of Pointers .................................................................................12-4
12.11 A Linked List ..............................................................................................12-5
12.12 The Data Definitions ..................................................................................12-6
12.13 The First Field ............................................................................................12-7
12.14 Filling Additional Structures .....................................................................12-7
12.15 Printing The Data Out ...............................................................................12-7
12.16 More About Dynamic Allocation And Linked Lists .................................12-8
12.17 Another New Function - Calloc .................................................................12-8
12.18 Programming Exercises .............................................................................12-8
iv
Table of Figures
Trivial.c ......................................................................................................................2-1
Wrtsome.c ...................................................................................................................2-1
Wrtmore.c ...................................................................................................................2-2
Oneint.c ......................................................................................................................2-2
Comments.c ................................................................................................................2-3
Goodform.c .................................................................................................................2-4
Uglyform.c ..................................................................................................................2-5
While.c .......................................................................................................................3-1
Dowhile.c....................................................................................................................3-1
Forloop.c.....................................................................................................................3-2
Ifelse.c ........................................................................................................................3-3
Breakcon.c ..................................................................................................................3-3
Switch.c ......................................................................................................................3-4
Gotoex.c .....................................................................................................................3-5
Tempconv.c ................................................................................................................3-5
Dumbconv.c ................................................................................................................3-7
Intasign.c ....................................................................................................................4-1
Mortypes.c ..................................................................................................................4-2
Lottypes.c ...................................................................................................................4-4
Compares.c .................................................................................................................4-6
Cryptic.c .....................................................................................................................4-9
Sumsqres.c ..................................................................................................................5-1
Squares.c.....................................................................................................................5-3
Floatsq.c .....................................................................................................................5-4
Scope.c .......................................................................................................................5-5
Recurson.c ..................................................................................................................5-8
Backward.c .................................................................................................................5-8
Define.c ......................................................................................................................6-1
Macro.c .......................................................................................................................6-2
Chrstrg.c .....................................................................................................................7-1
Strings.c ......................................................................................................................7-2
Intarray.c .....................................................................................................................7-3
Bigarray.c ...................................................................................................................7-4
Passback.c ...................................................................................................................7-5
Multiary.c ...................................................................................................................7-6
Pointer.c......................................................................................................................8-1
pointer2.c ....................................................................................................................8-3
twoway.c .....................................................................................................................8-5
simpleio.c....................................................................................................................9-1
singleio.c .....................................................................................................................9-3
betterin.c .....................................................................................................................9-4
intin.c ..........................................................................................................................9-5
stringin.c .....................................................................................................................9-6
special.c ......................................................................................................................9-8
formout.c ....................................................................................................................10-1
charout.c .....................................................................................................................10-2
readchar.c....................................................................................................................10-3
readtext.c ....................................................................................................................10-4
readline.c ....................................................................................................................10-5
anyfile.c ......................................................................................................................10-6
printdat.c .....................................................................................................................10-6
struct1.c ......................................................................................................................11-1
struct2.c ......................................................................................................................11-2
v
struct3.c ......................................................................................................................11-3
nested.c .......................................................................................................................11-5
union1.c ......................................................................................................................11-7
union2.c ......................................................................................................................11-8
dynlist.c ......................................................................................................................12-1
bigdyn1.c ....................................................................................................................12-5
dynlink.c .....................................................................................................................12-6
uplow.c .......................................................................................................................13-1
charclas.c ....................................................................................................................13-2
bitops.c .......................................................................................................................13-3
shifter.c .......................................................................................................................13-4
dosex_1616.c ..............................................................................................................14-4
whatnext.c ...................................................................................................................14-6
list.c ............................................................................................................................14-6
vc.c .............................................................................................................................14-15
vi