C Tutorial
C Tutorial
C Tutorial
A Tutorial
Gary D. Snyder
PEARSON
Boston Columbus Indianapolis New York San Francisco Upper Saddle River
Amsterdam Cape Town Dubai London Madrid Milan Munich Paris Montreal Toronto
Delhi Mexico City Sao Paulo Sydney Hong Kong Seoul Singapore Taipei Tokyo
Contents
Contents
Preface ..................................................................................................................... v
1. Hello, World .................................................................................................... 1
2. Elementary Data Types .................................................................................. 5
3. Input and Output ............................................................................................ 7
4. Arithmetic Operators ................................................................................... 13
5. Logic Operators ............................................................................................ 21
6. Relational Operators .................................................................................... 33
7. Decisions and Conditional Execution.......................................................... 37
8. Loop Statements............................................................................................ 45
9. Derived and Defined Data Types................................................................. 49
10. Strings ............................................................................................................ 61
11. Pointers .......................................................................................................... 69
12. Functions in C ............................................................................................... 75
13. In Conclusion................................................................................................. 87
iii
Preface
Preface
About This Tutorial
Background
The rapid advances in computers and computer technology have a great impact on electronics studies. Computers
and software are standard electronics tools just as the calculators that preceded them were forty years ago. One type
of software consists of application programs, such as SPICE and MultiSIM that allow users to design, simulate, and
evaluate circuit designs before committing them to hardware. Another type of software includes programming
software for languages such as VHDL and C that allow users to customize and configure electronics systems as
specific applications require.
The earliest high-level programming languages, which included Fortran, Algol, COBOL, and BASIC, were closer to
natural language than assembly language and isolated programs from the operating details of the underlying
hardware. This allowed programmers to write code that would run on more than one type of computer but limited
their access to and control of the system hardware. For example, a Fortran program written for an Apple II could be
compiled to run on a TI-99/4 as well, but could not access the individual bits of a decoder.
The C programming language, or simply C, was developed in the early 1970s by Dennis Ritchie to develop the
UNIX operating system on a DEC PDP-11 mainframe. Because an operating system works very intimately with the
system hardware, the C language contains many features that allow the programmer to interact with the system
hardware. Some have called C a “portable assembly language” because it allows programmers to write high-level
programs that can function very much like low-level assembly programs. This makes C a very powerful
programming language, but can create serious problems if programmers are careless.
Purpose
The intent of Programming in C is to introduce the basic concepts of the C programming language. It does so by
discussing features of the C language and providing representative examples of how these features are used in C
functions and programs.
What Programming in C Is
Programming in C is a tutorial. It presents and discusses introductory topics in C programming and the C
programming language. You should (but need not) use it in conjunction with an ANSI C or C-compatible
programming package so that you can further explore and reinforce the topics presented in this tutorial. There are a
number of freeware and academic applications that support the ANSI C standard and can be used with this tutorial.
Tutorial Organization
This tutorial consists of 12 chapters, each of which presents a specific C programming topic, and a conclusion. Each
chapter contains sample C program code that the reader can compile to verify and further explore the presented
topic.
vi
Section 1 - Hello, World
1. Hello, World
1.1 Overview
The HELLO.C program discussed in this chapter is traditionally the first C program that students write and study.
Although the program does very little it provides a good introduction to the basic structure and features of every C
program that you will write. Because this is only an introduction to C programs some of these features are covered
only briefly and will be discussed more fully in later chapters.
1
//
HELLO.C
2
3
/*
Your
first
C
program
*/
4
5
#include
<stdio.h>
6
7
void
main
()
8
{
9
printf
("Hello,
world\n");
10
}
When HELLO.C is compiled and run, the program outputs to your monitor the message
Hello,
world
The following sections will examine each part of the program and discuss the purpose of each.
1
Programming in C: A Tutorial
/******************************************************************************
*
A
common
use
for
comments
is
to
add
“headers”
like
this
to
the
start
of
a
*
*
program
or
function
to
document
when
the
code
was
written,
who
wrote
it,
*
*
and
how
the
code
works.
*
******************************************************************************/
A word of caution is in order regarding the second method for placing comments in a C program. These types of
comments cannot be nested. In other words, you cannot have a block comment inside another block comment. This
is because the “/*” for the inner comment is considered part of the first comment rather than another start of
comment sequence, and the “*/” of the inner comment will end the first comment. The compiler will attempt to
process any lines between the first and second “*/” sequences and probably generate an error. The nested comment
below illustrates this.
/*
This
is
the
start
of
the
outer
comment.
The
compiler
will
ignore
these
lines.
/*
These
lines
are
the
intended
inner
comment,
but
are
considered
part
of
the
outer
comment
because
the
end
of
comment
sequence
for
the
outer
comment
has
not
yet
occurred.
Consequently
the
start
of
comment
sequence
for
the
inner
comment
will
be
ignored.
*/
This
is
supposed
to
be
part
of
the
outer
comment,
but
the
end
of
comment
sequence
on
the
previous
line
has
ended
the
outer
comment.
The
compiler
will
try
to
process
these
lines
and
the
end
of
comment
sequence
on
the
next
line.
*/
A full explanation of this prototype is beyond the scope of this tutorial. For now, all you need to know is that the
function printf() can return an integer (int) value and can accept a string (const
char
*), among other things, as a
parameter to process.
For the example above, because printf() returns an integer value retval also must be an integer data type. You will
learn more about data types and functions in later chapters.
A point to note is that the line of code in Line 9 ends with a semicolon. Every line of code in C must end with a
semicolon, although as HELLO.C shows not every program line requires one. Lines that do NOT end in a semicolon
include comments, preprocessor directives, and function bodies like main(). In later chapters you will learn more
about the rules for writing C code.
3
Programming in C: A Tutorial
1
//
HELLO.C
2
3
/*
Your
first
C
program
*/
4
5
#include
<stdio.h>
6
7
void
main
(){
8
printf
("Hello,
world\n");}
As you can see, the left curly bracket appears at the end of the function statement and the right curly bracket appears
at the end of the last line of the function body, rather than each curly bracket having a line of its own. This reduces
the number of lines needed for the program listing, but can make the code harder to read.
For clarity this tutorial will use the structured programming style. Which coding style you chose to use is a matter of
personal preference, but you should attempt to be consistent with whichever style you choose.
1.3 Conclusion
The HELLO.C program is a very simply program, but contains a good deal of information about the nature and
characteristics of C programs. The printf() function allows programs to output information. In the next section you
will learn about the data types, or the types of information with which C can work.
4
Section 2 - Elementary Data Types
2.2.1 Integers
The integer data type deals with numbers that have no fractional portion. For example, 5 and 1000000 are integers,
but 0.5 and 3.1416 are not. In C there are three basic types of integer data types: short
int, int and long
int.
The short
int, or short, data type, is rarely used in C because it is often the same as the int data type. A short
int
variable named short_var is declared using a statement similar to that below.
On most computer systems the int data type represents numbers that are 16 bits long although this can vary with the
compiler. The int type can be either signed or unsigned, depending upon whether the data can be both positive and
negative (signed) or positive only (unsigned). The value of an unsigned
int can be from 0 to +65,535 and that of a
signed
int can be from –32,768 to +32,767. An int variable named int_var is declared using a statement similar to that
below.
int int_var;
The long
int, or long, data type typically represents numbers that are 32 bits long. As with the int data type the long
int can be signed (positive or negative) or unsigned (positive only). The value of an unsigned
long
int can be from 0
to 4, 294,967,296 and that of a signed long int can be from –2,147,483,648 to +2,147,483,647. A long
int variable
named long_var is declared using a statement similar to that below.
One useful characteristic of integers is that C can readily copy values between different integer types provided that
the variable to which the data is being passed has more bits than the variable from which the data is being passed. In
other words, the value in int_var can be copied to long_var because long_var is 32-bits long and int_var is only 16 bits
long. Attempting to copy the value in long_var into int_var will create problems because a 16-bit variable cannot hold
a 32-bit value.
5
Programming in C: A Tutorial
2.2.2 The char Data Type
The char data type, which represents 8-bit values, is actually an integer data type but is rarely used as an integer. As
the name suggests, variables of type char usually hold ASCII characters. This is useful for programs to store and
operate on single key presses, such as when a user must select an item from a number list or respond to a question
that requires an answer of Y (for yes) or N (for no). C uses single quotes to identify a char value, so the value ‘5’ is
the character for five whereas 5 is the numeric value five. A char variable named keypress is declared using a
statement similar to that below.
Because the char data type has fewer bits than any other integer data type, char values can be copied into other
variables of other integer data types, but other integer value can be copied into a variable of char data type.
The double data type represents floating point values as 64-bit double-precision numbers defined by the IEEE
floating point standard, in which one bit represents the sign, eleven bits represents the binary exponent, and the
remaining fifty-two bits represents the 53-bit mantissa (the most significant bit is always assumed to be 1). The
additional exponent bits increase the range and the additional mantissa bits increase the accuracy of the 64-bit
double-precision value compared to the 32-bit single-precision numbers. 64-bit double values range from about
±2.2 × 10–308 to ±1.8 × 10+308. A double value named double_var is declared using a statement similar to that below.
6
Section 3 - Input and Output
where <variable
type> is a character that specifies the data type of the variable and <variable> specifies the name of
the variable into which the scanf() function will place the input data. Some of basic allowed variable types for
scanf() are
Note that the data type of the specified variable must be of the same as that of the variable type.
7
Programming in C: A Tutorial
The scanf() function can input more than one input value at a time, which can be the same or different data types.
The program below shows how scanf() inputs two values into the variables number1 and number2.
This program allows the user to input an integer, but at this point have no practical value. First, the programs do
nothing with the information the user enters. Second, they don’t allow the user even to verify that the program input
the data correctly. In the next section you will learn more about the printf() function that can address both these
issues.
where <variable
type> is a character that specifies the data type of the variable and <variable> specifies the name of
the variable into which the printf() function will place the input data. Some of basic allowed variable types for
printf(), which are similar to those for the scanf() function, are
8
Section 3 - Input and Output
d
Decimal integer value
e Float value using exponents
f
Float value
o Octal integer value
u
Unsigned decimal integer value
x
Hexadecimal integer value using lower case characters for a, b, c, d, e, and f
X Hexadecimal integer value using upper case characters for A, B, C, D, E, and F
Note that the data type of the specified variable must be of the same as that of the variable type.
Because printf() is an output function it offers options for formatting how values are displayed on the screen. These
include whether it displays the sign of the value, the number of decimal places for float values, whether the value is
right or left justified, and the number of characters used to print the value including leading zeros or trailing blanks
if required. Some examples of these are shown below.
d
Use as many spaces as necessary to print the integer, printing the sign only if the integer is
negative.
6d
Use six spaces to print a right-justified signed integer, printing the sign only if the integer is
negative
–6d
Use six spaces to print a left-justified signed integer, printing the sign only if the integer is
negative
+6d Use six spaces to print a right-justified signed integer, printing the sign for both positive and
negative values
–+6d Use six spaces to print a left-justified signed integer, printing the sign for both positive and
negative values
6.2f Use six spaces to print a right-justified float value, using two digits for the fractional portion.
–6.2e Use six spaces to print a left-justified float value using exponential notation, using two digits for
the fractional portion.
The printf() function also recognizes certain special characters to help format lines. One of these is the \n (newline)
character, which moves the cursor to the beginning of the next line on the screen. Another is the \t (tab) character,
which acts like the tab character on a computer keyboard and moves the cursor a fixed number of spaces to the right.
9
Programming in C: A Tutorial
If the program must output signed values and use exponential notation for the float value, it can be modified as
shown below.
#include
<stdio.h>
/*
Required
for
printf()functions
*/
void
main
(void)
{
int
int_var
=
123;
float
float_var
=
123.4
printf
(“Integer
=
%+4d
and
float
=
%+6.1e\n”,
int_var,
float_var);
}
When compiled and run, the program will output the following:
Note that the number of spaces for the integer and float values were both increased by one to add a space for
printing the sign.
10
Section 3 - Input and Output
3.4.2 Using scanf() and printf() to Convert Numbers
A somewhat creative use of the scanf() and printf() functions is to convert numbers between decimal, octal, and
hexadecimal by inputting one variable type and printing anther. As an example, a program to convert integers from
decimal to octal is shown below. Recall that the int data type is a 16-bit signed value. This means that number must
be between -32,768 and +32,767 (the range for signed 16-bit values). To convert larger integer values number must
be declared as a long data type.
If the decimal number entered is 100 the program will output the following:
If the hexadecimal number ffff is entered the program will output the following:
11
Programming in C: A Tutorial
Please
enter
a
hexadecimal
value
(Use
lower
case
for
a
through
f):
ffff
The
decimal
value
is
-1
Note that in this case the hexadecimal value ffff (or 1111 1111 1111 11112) is the 16-bit 2’s complement form of
–1, which is why the program displays the decimal value as –1. This shows that C represents negative numbers in
2’s complement form.
The programs to perform other conversions, such as hexadecimal to octal or decimal to hexadecimal, are very
similar to those above. The only changes that need be made are to the variable type in the scanf() and printf()
statements.
12
Section 4 - Arithmetic Operators
4. Arithmetic Operators
4.1 Overview
Functions are one way of processing data in C. Another way is to use C operators, which are part of the C language.
Operators are similar to functions, but are much more basic. Functions, such as printf(), consist of a series of
program statements that achieve some specific purpose such as outputting a string of text to the screen. Operators
and the values on which they operate form expressions that evaluate to some value. One operator with which you are
familiar from arithmetic classes is the addition (+) operator, which adds two numbers. The addition operator plus
two numbers evaluate to the sum of the tow numbers.
will print
13
Programming in C: A Tutorial
The
number
is
8.
The
new
number
is
8.
This is because the pre-increment operation causes C to increment the value of int_var before
passing it to the first printf() function. Conversely,
will print
This is because the post-increment operation causes C to increment the value of int_var after
passing it to the first printf() function.
Although the increment by one operator can be used with float values, rounding errors with float
values will affect the result. For example, incrementing the float value 8.0 could give a result of
8.99999 or 9.00001 rather than exactly 9.0. Consequently the increment by one operator should be
used only with integer values.
-‐-‐ Decrement by one
The decrement by one operator is similar to the increment by one operator except that this operator
will decrease the value of an integer variable by one. This operator also can be placed immediately
before the operand for a pre-decrement operation, or immediately after the operand for a post-
decrement operation.
As with the increment by one operator, the decrement by one operator should be used only with
integer values.
int_var + 3
14
Section 4 - Arithmetic Operators
The modulus operation will give the remainder when of one integer is divided into another. As an
example, the value of
14 % 5
Expression can use parentheses to alter the default order of operations. For example, C would normally evaluate the
expression
2 * 3 + 4 * 5
from left to right and complete the multiplication operations before the addition operation. This would give
2
*
3
+
4
*
5
6
+
4
*
5
6
+
20
26
However, C would evaluate the expression
2 * (3 + 4) * 5
by first completing the addition in parentheses and then evaluating the expression from left to right. This would give
2
*
(3
+
4)
*
5
2
*
7 *
5
14
*
5
70
16
Section 4 - Arithmetic Operators
Assume that the value of int_1 is 2, the value of int_2 is 3, the value of float_1 is 2.0, and the value of float_2 is 3.0
the program will output the following:
17
Programming in C: A Tutorial
scanf
(“%f”,
&b);
printf
(“Please
enter
coefficient
c:
”);
scanf
(“%f”,
&c);
/*
Input
values
of
variable
*/
printf
(“Please
enter
the
value
of
variable
x:
”);
scanf
(“%f”,
&x);
/*
Calculate
the
result
of
the
quadratic
equation
*/
answer
=
a*x*x
+
b*x
+
c;
printf
(“\n\nThe
answer
is
%f\n”,
answer);
}
Assume that a = –1.5, b = 3.1, c = 0.7, and x = 5.0. The program will output the following:
int_val * 6
evaluates 72, but this value doesn’t change the value of int_val. In fact, the expression doesn’t change anything
unless an = assignment operator copies the value of the expression into some variable or a function accesses the
value. If the value of int_val actually must be multiplied by 6, the C expression to do so is
18
Section 4 - Arithmetic Operators
These kinds of operation, in which the value of an expression is copied into one of the operands, occur very often in
C so C provides special shortcut operators for them. These operators append the = assignment operator to the
arithmetic binary operators, so the shortcut operator for addition is +=, the shortcut operator for division is /=, and so
forth. Expressions that use these special operators place the operand to be changed on the left of the operator and the
operand that will change them on the right. As an example, an equivalent C expression uses the shortcut operator to
multiply the value of int_val by 6 is
Similarly, an expression that is equivalent to loop++ and increases the value of loop by one is
One useful expression that multiplies a variable (called squared in this example) by itself is
Shorthand operators are not limited to just binary arithmetic operators. As the next chapter shows shorthand
operators exist for binary logical operators as well.
19
Section 5 - Logic Operators
5. Logic Operators
5.1 Overview
The last section discussed the basic arithmetic operators in C that allow programs to work with numbers and
equations. Another set of operators are the logic operators. These operators allow programs to work with Boolean
values and expressions. They also allow programs to examine and manipulate the individual bits in binary values.
This powerful C feature is quite important for allowing processors to test and control individual control lines in
digital systems.
!logic_var
Note that, as with arithmetic operators, this does not change the value of the operand. An
assignment operator must be used to copy the value of the evaluated expression into another
variable. A statement that will copy the inverse of logic_var into a variable called bool_var is shown
below:
bool_var = !logic_var;
The logic NOT is always evaluated first in an expression, unless parentheses change the standard order of
operations. The following program demonstrates how the logical NOT operator functions.
include
<stdio.h>
/*
Required
for
printf()
function
*/
void
main
(void)
{
int
false_val
=
0,
true_val
=
1;
printf
(“false_val
=
%d,
!false_val
=
%d\n”,
false_val,
!false_val);
printf
(“true_val
=
%d,
!true_val
=
%d\n”,
true_val,
!true_val);
21
Programming in C: A Tutorial
}
logic_var && 3
• If both operands are TRUE (not 0), the value of the expression is TRUE.
• If either or both operands are FALSE (0), the value of the expression is FALSE.
is FALSE, because one of the variables (bool_var) is FALSE. On the other hand,
is TRUE because both logic_var and !bool_var (the inverse of bool_var) are TRUE.
The following program demonstrates how the logic AND operator functions.
#include
<stdio.h>
/*
Required
for
printf()
function
*/
void
main
(void)
{
int
false_val
=
0,
true_val
=
!false_val;
printf
(“A
=
%d,
B
=
%d,
A
&&
B
=
%d\n”,
false_val,
false_val,
false_val
&&
false_val);
printf
(“A
=
%d,
B
=
%d,
A
&&
B
=
%d\n”,
false_val,
true_val,
false_val
&&
true_val);
22
Section 5 - Logic Operators
printf
(“A
=
%d,
B
=
%d,
A
&&
B
=
%d\n”,
true_val,
false_val,
true_val
&&
false_val);
printf
(“A
=
%d,
B
=
%d,
A
&&
B
=
%d\n”,
true_val,
true_val,
true_val
&&
true_val);
}
When run, this program will output the following truth table for the logical AND function:
Note that true_val was initialized as !false_val when it was declared as an int variable rather than as the literal value
of 1. This is acceptable in C provided that false_val has been properly initialized already.
|| Logic OR
The logic OR operator will produce a TRUE or FALSE value according to the following rules.
• If both operands are FALSE (0), the value of the expression is FALSE.
• If either or both operands are TRUE (not 0), the value of the expression is TRUE.
is FALSE, because both variables (logic_var and bool_var) are FALSE. On the other hand,
is TRUE because at least one of the operands (!logic_var, the inverse of logic_var) is TRUE.
23
Programming in C: A Tutorial
true_val
||
false_val);
printf
(“A
=
%d,
B
=
%d,
A
||
B
=
%d\n”,
true_val,
true_val,
true_val
||
true_val);
}
When run, this program will output the following truth table for the logic OR function:
Bits 7: 0 OR 1 = 1
Bits 6: 0 OR 0 = 0
Bits 5: 0 OR 1 = 1
Bits 4: 1 OR 0 = 1
Bits 3: 0 OR 0 = 0
Bits 2: 0 OR 1 = 1
Bits 1: 1 OR 0 = 1
Bits 0: 0 OR 1 = 1
The result of bitwise ORing 0x12 and 0x5A is binary 10110111, or hexadecimal 0xB7. Note that cause bitwise logic
operations operate on corresponding bits of the operands, the size of the bitwise logic operands in C should be the
same integer size. Although C can apply bitwise logic operators to operands of different sizes, such as an int and a
long, this can produce unexpected results and should be avoided.
Bitwise expressions in C always place the operator between the two operands, just as with logical expressions in C.
As an example, an expression to bitwise AND the variable logic_var and the decimal number 3 would be
logic_var & 3
Assume, for example, that op_1 = 0xF0 and op_2 =
0x55, or binary
11110000 and 01010101,
respectively. Then
Bits 7: 1 AND 0 = 0
Bits 6: 1 AND 1 = 1
Bits 5: 1 AND 0 = 0
Bits 4: 1 AND 1 = 1
Bits 3: 0 AND 0 = 0
Bits 2: 0 AND 1 = 0
Bits 1: 0 AND 0 = 0
Bits 0: 0 AND 1 = 0
The result of bitwise ANDing 0xF0 and 0x55 gives the result 01010000 binary, or 0x50.
Note from the above example that ANDing a bit with 1 will preserve the value of a bit and ANDing a bit with 0 will
clear that bit. By using the correct bit mask, or pattern of 0s and 1s, programs can isolate specific bits in an integer
value and determine whether the bit is a 0 or 1 by removing, or masking off, all the other bits. One application of
masking off bits is to convert an ASCII value into a numeric value. When the ‘3’ key on a computer keyboard is
pressed, the keyboard does not send the number 3 to the computer. Instead, it sends the ASCII code for ‘3’. The part
of the operating system that processes number keys masks off bits in the ASCII code so that bits that remain are the
binary value of the number. As it happens, the ASCII value for the digits ‘0’ through ‘9’ are the hexadecimal values
0x30 through
0x39, or 00110000 through 00111001 respectively. To convert a number keypress from ASCII to an
integer value all that is necessary is to remove the upper four bits and preserve the lower four bits. The program
below shows how this can be done.
#include
<stdio.h>
/*
Required
for
printf()
and
scanf()
function
*/
void
main
(void)
{
char
key;
int
bitmask
=
0x0F;
//
Get
a
keypress
printf
(“Enter
a
number
key:
”);
scanf
(“%c”,
&key);
//
Show
the
key
that
was
entered.
printf
(“You
pressed
the
‘%c’
key.\n”,
key);
printf
(“The
ASCII
value
is
0x%2x\n”,
key);
25
Programming in C: A Tutorial
//
Show
the
value
of
the
key
that
was
entered.
printf
(“The
value
of
the
key
is
%d.\n”,
key
&
bitmask);
}
Assume that you enter the value “4”. When run, this program will output the following:
Note that the printf() statement that outputs key uses %c to output the value as an ASCII character, whereas the
printf() statement that outputs key
&
bitmask uses %2x to output the integer value as a 2-digit hexadecimal integer.
| Bitwise OR
The bitwise OR operator will OR the corresponding bits of two operands together and produce bits
according to the following rules.
Assume, for example, that op_1 = 0x81 and op_2 =
0x5A, or binary
10000001 and 01011010,
respectively. Then
op_1 | op_2
Bits 7: 1 OR 0 = 1
Bits 6: 0 OR 1 = 1
Bits 5: 0 OR 0 = 0
Bits 4: 0 OR 1 = 1
Bits 3: 0 OR 1 = 1
Bits 2: 0 OR 0 = 0
Bits 1: 0 OR 1 = 1
Bits 0: 1 OR 0 = 1
The result of bitwise ANDing 0x81 and 0x5A gives the result 11011011 binary, or 0xDB
hexadecimal.
Note from the above example that ORing a bit with 0 will preserve the value of a bit and ORing a bit with 1 will set
that bit. By using the correct bit mask programs can set, or mask on, specific bits in an integer value. One
application of masking on bits is to convert a numeric value into an ASCII value. Suppose, for example, that you
add two integer values and wish output the sum as an ASCII value. To convert an integer value between 0 and 9 all
that is necessary is to set the upper four bits to a hexadecimal 3, or 0011 binary. The program below shows how to
convert the sum of two integers to an ASCII value.
26
Section 5 - Logic Operators
#include
<stdio.h>
/*
Required
for
printf()
and
scanf()
function
*/
void
main
(void)
{
char
key1,
key2,
sum;
char
or_mask
=
0x30,
and_mask
=
0xF0;
//
Get
the
two
numbers
to
add.
printf
("Enter
the
first
number:
");
scanf
("%c",
&key1);
key1
=
key1
&
and_mask;
printf
("Enter
the
second
number:
");
scanf
("%c",
&key2);
key2
=
key2
&
and_mask;
//
Add
the
two
numbers.
sum
=
key1
+
key2;
//
Show
the
addition
using
integers.
printf
("The
addition
is
%d
+
%d
=
%d.\n",
key1,
key2,
sum);
//
Convert
the
sum
to
ASCII.
sum
=
sum
|
bitmask;
//
Show
the
value
of
the
sum
that
was
entered.
printf
("The
sum
is
'%c'.\n",
sum);
}
Assume that you enter the values “3” and “5”. When run, this program will output the following:
You may have noticed, that contrary to earlier warnings, the numbers for this program are entered as 16-bit integers
but that the 16-bit value sum is output as an 8-bit ASCII character. When the C program is compiled and run, it
treats values of key1, key2, and sum as 16-bit integers, but when the printf() statement uses %c to reference sum as an
8-bit character it ignores the upper 8 bits and outputs only the lower 8 bits. In this example only the lower 8 bits are
important, so this is not a problem. However, the upper 8 bits may be significant in other applications so that
program would need to deal with them accordingly.
27
Programming in C: A Tutorial
• If both bits are different Boolean values, the value of the result bit is 1.
• If both bits are the same Boolean value, the value of the result bit is 0.
Assume, for example, that op_1 = 0x0F and op_2 =
0xAA, or binary
00001111 and 10101010,
respectively. Then
op_1 ^ op_2
Bits 7: 0 XOR 1 = 1
Bits 6: 0 AND 0 = 0
Bits 5: 0 AND 1 = 1
Bits 4: 0 AND 0 = 0
Bits 3: 1 AND 1 = 0
Bits 2: 1 AND 0 = 1
Bits 1: 1 AND 1 = 0
Bits 0: 1 AND 0 = 1
The result of bitwise ANDing 0x0F and 0xAA gives the result 10100101 binary, or 0xA5
hexadecimal.
Note from the above example that XORing a bit with 0 will preserve the value of a bit and XORing a bit with 1 will
invert that bit. By using the correct bit mask, or pattern of 0s and 1s, programs can selectively invert bits in an
operand. One use of the bitwise logic XOR operation is to produce the 1’s complement of an integer value. XORing
an integer with itself will always result in 0, so using the XOR operator is one way to initialize a variable to 0. The
bitwise XOR is often used in generating checksums and in cyclic redundancy checks, which are ways for software to
verify that data has not been corrupted. The following program illustrates a particularly interesting use of the bitwise
XOR function.
#include
<stdio.h>
/*
Required
for
printf()
and
scanf()
function
*/
void
main
(void)
{
int
number1,
number2;
//
Input
two
numbers.
printf
(“Enter
a
number:
”);
scanf
(“%d”,
&number1);
printf
(“Enter
another
number:
”);
scanf
(“%d”,
&number2);
//
Show
the
numbers
that
were
entered.
printf
(“Number
1
=
%d,
Number
2
=
%d.\n\n”,
number1,
number2);
//
Hocus-‐pocus
and
alakazam...
number1
=
number1
^
number2;
28
Section 5 - Logic Operators
number2
=
number2
^
number1;
number1
=
number1
^
number2;
//
Presto!
printf
(“Number
1
=
%d,
Number
2
=
%d.\n”,
number1,
number2);
printf
(“Your
numbers
have
traded
places!\n”);
}
Assume that you enter the numbers 17 and 36. When run, this program will output the following:
While this may not seem impressive, the bitwise XOR allows programs to perform what is called an in-place
exchange. That its, a program can exchange two integer values without requiring a third variable as a temporary
storage location. This is a useful technique, especially in low-level programming, for swapping the contents of two
storage locations when an extra memory location or processor register is available.
will shift the value of op_1 to the left by three places as shown:
The result of shifting 15 to the left by three places gives a result of 01111000 binary, or 120. Note
that because C shifts a 0 into the least significant bit position, continuing to shift an operand left
will eventually result in a value of 0.
One application of shifting left is to multiply a number by two. Notice for that above example that
shifting 15 left by three places gives the same result as doubling 15 three times:
29
Programming in C: A Tutorial
15 × 2
= 30
30 × 2
= 60
60 × 2 = 120
Another use is to shift bits in an operand so that they sequentially appear in a specific bit position
and the program can use a single bit mask to evaluate them. The following program shows how
shifting and a bit mask can convert a decimal number into binary.
#include
<stdio.h>
/*
Required
for
printf()
and
scanf()
function
*/
void
main
(void)
{
int
number;
int
bitmask
=
0x80;
//
Get
a
number.
printf
("Enter
a
number
between
0
and
255:
");
scanf
("%d",
&number);
printf
("%d
decimal
=
",
number);
/*
Shift
the
number
left
by
one.
The
value
of
the
most
significant
bit
is
128,
so
divide
the
masked
value
by
128
to
get
a
1
or
0
for
that
bit.
*/
printf
("%d",
(number
&
bitmask)
/
128);
number
=
number
<<
1;
printf
("%d",
(number
&
bitmask)
/
128);
number
=
number
<<
1;
printf
("%d",
(number
&
bitmask)
/
128);
number
=
number
<<
1;
printf
("%d",
(number
&
bitmask)
/
128);
number
=
number
<<
1;
printf
("%d",
(number
&
bitmask)
/
128);
number
=
number
<<
1;
printf
("%d",
(number
&
bitmask)
/
128);
number
=
number
<<
1;
printf
("%d",
(number
&
bitmask)
/
128);
number
=
number
<<
1;
printf
("%d
binary\n",
(number
&
bitmask)
/
128);
}
Assume that you enter the value “100”. When run, the program will output the following:
30
Section 5 - Logic Operators
>> Bitwise Shift Right
The bitwise shift right operator is similar to the bitwise shift left operator and will shift the first
integer operand to the left by the number of bits indicated by the second operand. The main
difference is that the value of the bit shifted into the most significant bit position is not always the
same. Assume, for example, that op_1 = +240 and op_2 =
4, or binary 11110000 with a sign bit of
0 and 00000100, respectively. Then
will shift the value of op_1 to the right by four places as shown:
The result of shifting 240 to the right by four places gives a result of 00001111 binary, or 15.
Continuing to shift to the value of the op_1 to the right will eventually result in a value of 0.
However, assume now that op_1 = –240, or binary 00010000 with a sign bit of 1. Then the shift
operation will give the following.
The result of shifting –240 to the right by four places gives a result of 11110001, or –15
in 2’s
complement form. Note that because the sign bit is 1, continuing to shift a negative operand left
will eventually result in a value of all 1s.
is the same as
and
number |= bit_mask;
is the same as
The one exception to this rule is for the logical NOT, or invert, operator. The statement
val_1 != val_2;
This should not be a surprise, as the ! operator is a unary operator, and shortcut operators apply only to binary
operations. However, != is an operator and does have a specific meaning in the C language, which the next chapter
on relational operators will cover.
32
Section 6 - Relational Operators
6. Relational Operators
6.1 Overview
Relational operators, like logic operators, produce Boolean results. That is, the result of a relational operator is either
FALSE (0) or TRUE (not 0). Programs can use relational operators simply to generate Boolean values, just as they
can use logic operators to generate Boolean values. However, C programs typically use relational operators to
control how programs operate.
== Equals Operator
The equals operator indicates whether or not two operands have the same value. If the two
operands have the same value the result is TRUE, and if not the result is FALSE. Sometimes the
result of an equals operator not what you might expect. Consider the results of the following
program.
#include
<stdio.h>
/*
Required
for
printf()
and
scanf()
functions
*/
void
main
(void)
{
int
result;
result
=
(1.0
/
2.0)
==
0.5;
printf
("(1.0
/2.0)
==
0.5
=>
%d\n",
result);
result
=
(1.0
/
10.0)
==
0.1;
printf
("(1.0
/10.0)
==
0.1
=>
%d\n",
result);
}
This program determines whether the value of (1.0
/
2.0) is equal to 0.5, and whether the value of
(1.0
/
10.0) is equal to 0.1. When run, this program will output the following:
Although mathematically both statements are TRUE, computers must work with binary numbers and the precision
of floating point arithmetic in C is limited. The floating point value 0.5 (21/2) is an exact power of 2, so the
calculation (1.0
/
2.0) and the value 0.5 are equal and the expression (1.0
/
2.0)
==
0.5 evaluates to a TRUE result.
However, 0.1 is not an exact power of 2, so the calculation (1.0
/
10.0) and the value 0.1 are not equal and the
expression (1.0
/
10.0)
==
0.1 evaluates to a FALSE result.
33
Programming in C: A Tutorial
Because English and arithmetic use “equals” and “=” to represent both equality and assignment, one common
mistake that beginning C programmers make is to use the assignment operator “=” instead of the equals operator
“==”. Most C compilers will issue a warning, such as “Possibly
incorrect
assignment
in
statement” when the context
of the statement calls for the equals operator rather than the assignment operator.
#include
<stdio.h>
/*
Required
for
printf()
and
scanf()
functions
*/
void
main
(void)
{
int
result;
result
=
(1.0
/
2.0)
!=
0.5;
printf
("(1.0
/2.0)
!=
0.5
=>
%d\n",
result);
result
=
(1.0
/
10.0)
!=
0.1;
printf
("(1.0
/10.0)
!=
0.1
=>
%d\n",
result);
}
34
Section 6 - Relational Operators
The greater than or equal operator indicates whether or not the first operand is greater than or
equal to the second operand. If the first operand is greater than or equal to the second operand the
result is TRUE, but if the first operand is less than the second operand the result is FALSE.
35
Section 7 - Decisions and Conditional Execution
if
(Boolean
expression)
C
statement
1;
else
C
statement
2;
C
statement
3;
If Boolean
expression is TRUE the program executes C
statement
1 and proceeds to C statement 3. If Boolean
expression is FALSE the program skips over C
statement
1 to C
statement
2 and proceeds to
C
statement
3. The program below illustrates how the IF...ELSE statement works. Recall that % is the MOD operator.
#include
<stdio.h>
/*
Required
for
the
printf()
and
scanf()
functions.
*/
void
main
(void)
{
int
number;
printf
(“Enter
an
integer:
”);
scanf
(“%d”,
&number);
if
((number
%
2)
==
0)
printf
(“The
number
%d
is
even.\n”,
number);
else
printf
(“The
number
%d
is
odd.\n”,
number);
printf
(“End
of
program.\n”);
}
Assume that the number entered is 24. When the program is run the output will be:
37
Programming in C: A Tutorial
Suppose, however, that the number entered is 7. When the program is run the output will be:
When the number entered is 24 (or any even number) the expression (number % 2) == 0 is TRUE because the
remainder of any even number divided by 2 is 0. The program executes the printf() statement in the IF portion of the
IF...ELSE statement to print “The
number
24
is
even.” and then skip the ELSE portion of the IF...ELSE statement to
print “End
of
program.”. When the number entered is 7 (or any odd number), the expression (number
%
2)
==
0 will
be FALSE, because the remainder of any odd number divided by 2 will be 1 and not 0. The program skips over the
IF portion of the IF...ELSE statement to execute the printf() statement in the ELSE portion of the IF...ELSE statement
and then print “End
of
program.”.
The Boolean expression in the program above uses both an arithmetic and relational operator to generate a Boolean
value. The program below shows another IF...ELSE statement that uses a relational operator.
#include
<stdio.h>
/*
Required
for
the
printf()
and
scanf()
functions.
*/
void
main
(void)
{
int
number1,
number2;
printf
(“Enter
an
integer:
”);
scanf
(“%d”,
&number1);
printf
(“Enter
another
integer:
”);
scanf
(“%d”,
&number2);
if
(number1
<
number2)
printf
(“%d
is
less
than
%d.\n”,
number1,
number2);
else
printf
(“%d
is
greater
than
%d.\n”,
number1,
number2);
printf
(“End
of
program.\n”);
}
Assume that the number1 is 9 and number2 is 5. When the program is run the output will be:
Suppose now that number1 is 99 and number2 is 100. When the program is run the output will be:
38
Section 7 - Decisions and Conditional Execution
In some cases a program may not require the ELSE portion of the IF...ELSE statement. In these cases, the ELSE
portion of the IF...ELSE statement is simply omitted. The following is an example of such a program.
#include
<stdio.h>
/*
Required
for
the
printf()
and
scanf()
functions.
*/
void
main
(void)
{
int
number;
printf
(“Enter
an
integer:
”);
scanf
(“%d”,
&number);
if
(number)
printf
(“%d
is
non-‐zero.\n”,
number);
number++;
printf
(“The
incremented
value
is
%d.\n”,
number);
}
Assume that the number entered is 3. When the program is run the output will be:
As you can see, the ELSE portion is included when the program must make a choice between and execute one of two
statement. The ELSE portion is omitted when the program simply executes an additional statement when it detects
some specified condition (for the above program, it indicates when a non-zero value is entered).
The case portions of the switch statement are not themselves executed. Instead, they are simply entry points that
determine where the program will enter the switch program and begin execution. Note that each case statement ends
with a break statement. When the program encounters and executes the break statement, it exits the switch statement.
Most case statements use break statement, but as you will see sometimes omitting the break statement can simplify a
switch statement.
The number of alternatives (indicated by the number of case statements) theoretically is limited only by the number
of values that integer
value can have. A char value is limited to 256 case statements plus the default statement,
whereas an unsigned
long
int value can have over 4 billion. The program below is an example of a SWITCH
statement.
#include
<stdio.h>
/*
Required
for
the
printf()
and
scanf()
functions.
*/
void
main
(void)
{
char
keypress;
printf
(“Enter
a
letter:
”);
scanf
(“%c”,
&keypress);
printf
(“The
character
‘%c’
is
a
”,
keypress);
switch
(keypress)
{
case
‘a’:
printf
(“vowel”);
break;
case
‘e’:
printf
(“vowel”);
break;
case
‘i’:
printf
(“vowel”);
break;
case
‘o’:
printf
(“vowel”);
break;
case
‘u’:
printf
(“vowel”);
break;
case
‘y’:
printf
(“vowel
or
consonant”);
break;
default:
printf
(“consonant”);
}
printf
(“.\n”);
}
Assume that the letter entered is ‘e’. When the program is run the output will be:
Enter
a
letter:
e
The
character
‘e’
is
a
vowel.
40
Section 7 - Decisions and Conditional Execution
If the letter entered is ‘y’, the program will output
Enter
a
letter:
y
The
character
‘y’
is
a
vowel
or
consonant.
Enter
a
letter:
z
The
character
‘z’
is
a
consonant.
When the letter ‘e’ is entered, the program outputs “The
character
‘e’
is
a
” because that portion of the output is the
same regardless of the key pressed. The program then executes the switch statement and compares the value of
keypress (e) with the possible switch cases. The value of keypress is not ‘a’ so program skips the printf() and break
statements associated with that case statement and proceeds to the next case statement. This time the value of
keypress equals the value for the case ‘e’ statement, so the program executes the printf() statement to output “vowel”.
It then executes the break statement to exit the switch statement and outputs the ‘.’ to complete the output sentence.
When the letter ‘z’ is entered, the program outputs “The
character
‘z’
is
a
” that is output regardless of the key
pressed. The program then executes the switch statement and compares the value of keypress (e) with the possible
switch cases. The value of keypress does not match any of the case values, so the program skips the printf() and
break statements associated with each of them. After passing by all the case statements it then executes the printf()
associated with the default statement and outputs “consonant”. This takes it to the end of the switch statement, so it
exits the switch statement and outputs the ‘.’ to complete the output sentence.
One thing to notice about the program above is that the output for each case statement is the same. Rather than using
individual statements for each case statement, the program can use a single statement by eliminating the break
statements for the first four cases. The program shows how this is done.
#include
<stdio.h>
/*
Required
for
the
printf()
and
scanf()
functions.
*/
void
main
(void)
{
char
keypress;
printf
(“Enter
a
letter:
”);
scanf
(“%c”,
&keypress);
printf
(“The
character
‘%c’
is
a
”,
keypress);
switch
(keypress)
{
case
‘a’:
case
‘e’:
case
‘i’:
case
‘o’:
case
‘u’:
printf
(“vowel”);
break;
case
‘y’:
printf
(“vowel
or
consonant”);
break;
default:
printf
(“consonant”);
}
printf
(“.\n”);
41
Programming in C: A Tutorial
}
This program produces the same output as for the previous program. If the keypress value is ‘a’, ‘e’, ‘i’, or ‘o’ the
program will enter the switch statement through case entry point and “fall through” to execute the statements
associated with the case
‘u’ statement because there is no break statement to force the program out of the switch
statement. If the keypress value is ‘u’ or ‘y’ the program will execute the printf() and break statements associated
with them as it normally would. If keypress has any other value the program will execute the default statement.
#include
<stdio.h>
/*
Required
for
the
printf()
and
scanf()
functions.
*/
void
main
(void)
{
char
keypress;
printf
(“Enter
a
letter:
”);
scanf
(“%c”,
&keypress);
printf
(“The
character
‘%c’
is
a
”,
keypress);
if
(keypress
==
‘a’)
printf
(“vowel”);
else
if
(keypress
==
‘e’)
printf
(“vowel”);
else
if
(keypress
==
‘i’)
printf
(“vowel”);
else
if
(keypress
==
‘o’)
printf
(“vowel”);
else
if
(keypress
==
‘u’)
printf
(“vowel”);
else
if
(keypress
==
‘y’)
printf
(“vowel
or
consonant”);
else
printf
(“consonant”);
printf
(“.\n”);
42
Section 7 - Decisions and Conditional Execution
}
Just as the first SWITCH program could be and was modified to use a single printf() function for vowels, the nested
IF...ELSE program can be modified to reduce the number of program lines. The program below is one possible
approach.
#include
<stdio.h>
/*
Required
for
the
printf()
and
scanf()
functions.
*/
void
main
(void)
{
char
keypress;
printf
(“Enter
a
letter:
”);
scanf
(“%c”,
&keypress);
printf
(“The
character
‘%c’
is
a
”,
keypress);
if
((keypress
==
‘a’)
||
(keypress
==
‘e’)
||
(keypress
==
‘i’)
||
(keypress
==
‘o’)
||
(keypress
==
‘u’))
printf
(“vowel”);
else
if
(keypress
==
‘y’)
printf
(“vowel
or
consonant”);
else
printf
(“consonant”);
printf
(“.\n”);
}
The program uses the logical OR operator in the Boolean expression of the top-level IF...ELSE statement to check for
‘a’, ‘e’, ‘i’, ‘o’, or ‘u’. If a vowel is not found, the program proceeds to the second-level IF...ELSE statement.
Of course, you can also place SWITCH statements in an IF...ELSE statement or vice versa although there are not
examples of nesting because nesting requires statements to contain statements of the same kind.
#include
<stdio.h>
/*
Required
for
the
printf()
and
scanf()
functions.
*/
void
main
(void)
{
43
Programming in C: A Tutorial
int
number1,
number2;
printf
(“Enter
an
integer:
”);
scanf
(“%d”,
&number1);
printf
(“Enter
another
integer:
”);
scanf
(“%d”,
&number2);
if
(number1
<
number2)
{
printf
(“%d
is
less
than
%d.\n”,
number1,
number2);
printf
(“In
other
words,
%d
is
greater
than
%d\n”,
number2,
number1);
}
else
if
(number1
>
number2)
{
printf
(“%d
is
greater
than
%d.\n”,
number1,
number2);
printf
(“In
other
words,
%d
is
less
than
%d\n”,
number2,
number1);
}
else
{
printf
(“You
entered
the
same
number
both
times.\n”);
printf
(“How
lazy
can
you
get?\n”);
}
printf
(“End
of
program.\n”);
}
In this program, the block statements are used to place two printf() statements in the IF and ELSE portions of the
nested IF...ELSE statements. The block statements could also have used multiple statements to perform calculations or
anything else that the programmer wished the program to do for the condition associated with the block statement.
44
Section 8 - Loop Statements
8. Loop Statements
8.1 Overview
The sample programs in previous chapters have had one weakness that you may have noticed. The programs could
perform a specific task only once each time the program was run. Much of the power of computers is because
programs can repeat the same tasks over and over. For example, a word processor that allows only the user to type
only one key or even one sentence each time it runs isn’t much use. Executing the same statements more than once
is called looping. C provides three loop statements that allow programs to repeat a set of program statements: the
FOR statement, WHILE statement, and DO...WHILE statement. Each of these statements set up a specific type of loop
construct.
The start
condition specifier initializes the loop index, which should always be an integer to avoid issues with
floating point precision. The loop
condition specifier is a Boolean expression that indicates the conditions for which
the loop should continue to execute. As long as loop condition is TRUE the program remain in the loop and executes
the C statement associated with the loop. The index
adjustment specifier determines how the FOR statement will
automatically adjust the index variable at the end of each loop. Usually programs will increment the loop index by
one each time the loop executes, but programs can adjust the loop index in any other way the may be required. The C
statement that makes up the body of the loop can be a single statement or a block statement. The following program
shows a program that uses the FOR statement to calculate the factorial of a number.
#include
<stdio.h>
/*
Required
for
the
printf()
and
scanf()
functions.
*/
void
main
(void)
{
int
number,
index;
unsigned
long
total
=
1;
printf
("Enter
an
integer
(0
to
12):
");
scanf
("%d",
&number);
if
((number
>=
0)
&&
(number
<=
12))
{
for
(index
=
number;
index
>
0;
index-‐-‐)
total
*=
index;
printf
("%d!
=
%lu\n",
number,
total);
}
else
printf
("Invalid
entry
‘%d’.\n",
number);
}
45
Programming in C: A Tutorial
Note that the before executing the FOR loop the program uses an IF...ELSE statement discussed in the last chapter to
ensure that the entered value is between 0 and 12. This is because factorials are defined only for non-negative
numbers and 12! is the largest factorial that a long integer can represent. Assume that the number 8 is entered. When
the program is run the program will output the following:
The WHILE statement evaluates the Boolean
expression specifier and executes the C
statement associated with the
WHILE statement if Boolean
expression is TRUE. If the Boolean
expression specifier is FALSE to begin with, the
WHILE loop will never execute. As with the FOR statement the C statement in a WHILE statement can be a single
statement or a block statement.
The following program demonstrates how a WHILE statement allows a user to run the factorial program from above
as many times as they wish. When the user wishes to end the program, all they need do is enter a negative number.
#include
<stdio.h>
/*
Required
for
the
printf()
and
scanf()
functions.
*/
void
main
(void)
{
int
number
=
0,
index;
unsigned
long
total;
while
(number
>=
0)
{
printf
("Enter
an
integer
(0
to
12
or
a
negative
value
to
exit):
");
scanf
("%d",
&number);
if
((number
>=
0)
&&
(number
<=
12))
{
total
=
1;
for
(index
=
number;
index
>
0;
index-‐-‐)
total
*=
index;
printf
("%d!
=
%lu\n",
number,
total);
}
else
46
Section 8 - Loop Statements
{
printf
("Invalid
entry
‘%d’.\n",
number);
}
}
printf
(“Exiting
program.\n”);
}
Assume now that the numbers entered, in order, are 5, 6, 15, and –3. When the program is run, the program will
output the following.
Enter
an
integer
(0
to
12
or
a
negative
value
to
exit):
5
5!
=
120
Enter
an
integer
(0
to
12
or
a
negative
value
to
exit):
6
6!
=
720
Enter
an
integer
(0
to
12
or
a
negative
value
to
exit):
15
Invalid
entry
‘15’.
Enter
an
integer
(0
to
12
or
a
negative
value
to
exit):
–3
Invalid
entry
‘3’.
Exiting
program.
do
C
statement;
while
(Boolean
expression);
The program below shows one way to implement the factorial program using a DO...WHILE statement.
#include
<stdio.h>
/*
Required
for
the
printf()
and
scanf()
functions.
*/
void
main
(void)
{
int
number,
index;
unsigned
long
total
=
1;
printf
("Enter
an
integer
(0
to
12):
");
scanf
("%d",
&number);
total
=
1;
if
((number
>
0)
&&
(number
<=
12))
{
index
=
number;
do
total
*=
index-‐-‐;
47
Programming in C: A Tutorial
while
(index
>
0);
printf
("%d!
=
%lu\n",
number,
total);
}
else
if
(number
==
0)
printf
("0!
=
1\n");
else
printf
("Invalid
entry
%d\n",
number);
}
Notice that because the DO...WHILE loop always executes at least once special code must be added to handle the case
when the number entered is 0. Loops in C programs often require careful coding and checking to ensure that the
minimum or maximum allowable values, called the boundary conditions, will not cause problems when the program
executes.
48
Section 9 - Defined and Derived Data Types
9.2.1 Arrays
An array is a storage unit that consists of multiple identical data units. One way to think of an array is as a container
with multiple spaces for objects of the same size, like a CD storage cabinet. Each storage space in the array is called
an element. Arrays can consist of any type of elementary data type, but each element must be the same. An array
declaration has the form
The declaration for a 10-element array of int with the name int_array would be:
int int_array[10];
As you can see, the only difference between declaring an integer array and an integer variable is the addition of the
number of elements in brackets. Similarly, the declaration of an 80-element array of char named message would be:
char message[80];
Arrays can be initialized at the time they are declared by providing a list of the array data separated by commas in
curly brackets ({}). If this method is used to initialize an array, the list must initialize every element in the array. For
example, an array with five elements must have a list that contains values for all five elements. The following shows
how to declare and initialize five-element array of int values with the values 0 through 4:
The name of the array variable and the element number in brackets are used to access a specific element in the array.
One thing to remember, however, is that numbering of elements in an array begins with 0 and not 1. Therefore, the
nth element in an array has the element number n
–
1. For example, a statement to store the number 23 in the fifth
element of int_array[] would be:
49
Programming in C: A Tutorial
int_array[4]
=
23;
Similarly, a statement to print message[0], which contains the first character in message[], would be:
The for loop, which loops for values of 0 through 10, initializes each element of the array. Note that because the
array elements begin with 0 while the values in the array begin with the square of 1, the program loads element
number n with the square of (n
+
1). Once the table is loaded the program prompts the user for a number between 1
and 10, or 0 to exit the program. The program performs range checking to ensure the input is between 1 and 10 and
returns the square of numbers between 1 and 10, exits on 0, and ignores all other input values. Assume that a user
enters the values 20, 5, 3, –1, and 0 when the program is run. The program output will be:
The range checking that the program performs on the input does more than simply ensure that the user enters valid
data for the stored values in the look-up table. It prevents the program from accessing memory outside the memory
locations defined for the array. If a program attempts to access memory other than that allocated for the program
data, the program could lock up (stop responding), crash (end unexpectedly), corrupt data used by other programs,
or lock up the computer.
While this example program may seem a trivial use for an array, programs often use look-up tables to speed up
execution. For example, rather than calculating the sine or cosine of an angle a program might preload tables once
with values of these functions in 0.1° increments and then simply access the values rather than spend the time
needed to recalculate them.
struct
struct_name
{
elementary_data_type_1
field_name_1;
elementary_data_type_2
field_name_2;
...
elementary_data_type_n
field_name_n;
}
Structure variables can be declared in one of two ways. The most obvious way is to first define the structure and
then declare the variable. The second way is to declare the variable at the time the structure is defined. The
following examples show two ways to declare a variable named impedance for a struct type called complex that
consists of a real float field and an imaginary float field. The first method to declare a structure is:
struct
complex
{
float
real;
float
imaginary;
};
struct
complex
{
float
real;
51
Programming in C: A Tutorial
float
imaginary;
}
impedance;
The method used to declare a struct variable is a matter of personal preference, but in the interests of clarity it’s best
to be consistent when doing so.
Structures can be initialized at the time they are declared by initializing fields with a list of values in curly brackets
({}) similar to the way that arrays are initialized. The following illustrates how to initialize both the real and
imaginary fields of impedance with the values 1.0 and –2.0:
struct
complex
{
float
real;
float
imaginary;
}
impedance
=
{1.0,
–2.0};
The first value in the list (1.0) initializes the first declared field (real) and the second value in the list (-‐2.0) initializes
the second declared field (imaginary). Note that the statement must initialize all fields of a structure.
Accessing data in a structure consists of specifying the structure and the field within it that contains the data. This
can be done in more than one way, but the most common form for specifying a structure field is:
struct_name.field_name
The second way of specifying fields, which uses pointers, will be discussed in a later chapter.
The program below illustrates how struct variables can be used to multiply complex numbers in rectangular form.
52
Section 9 - Defined and Derived Data Types
the
product
from
the
FOIL
(first,
outer,
inner,
last)
expansion
rule
is:
(R1*R2
–
I1*I2)
+
j
((R1
*
I2)
+
(R2
*
I1))
*/
product.real
=
number1.real
*
number2.real
–
number1.imag
*
number2.imag;
product.imag
=
number1.real
*
number2.imag
+
number2.real
*
number1.imag;
printf
(“The
product
is
%d
+
j(%d).\n”,
product.real,
product.imag);
}
Note that in this program each scanf() has the user input two values at time. The scanf() function can theoretically
input as many values at a time at once, but the user must enter the values so that the input matches the format of the
scanf() function. In this case the user must enter two integers separated by at least one space. Note also that the
address of the field is specified as &(structure_name.field_name). C will also allow &structure_name.field_name
without the parentheses, but in this case the parentheses were included for clarity.
Assume that the user enters the values 3 and –4 for the first complex number and 13 and 5 for the second complex
number. The program output will be:
Enter
the
real
and
imaginary
parts
of
Number
1:
3
–4
Enter
the
real
and
imaginary
parts
of
Number
2:
13
5
The
product
is
59
+
j(–37).
In this example the fields of the structures were both int data types (so that arrays could have been used), but
structures typically have different data types for fields. For example, the declaration of a struct data type called
float_struct that holds the integer and fractional portion of a number in fields called whole and fract, respectively,
would be:
struct
float_struct
{
int
whole;
float
fract;
};
53
Programming in C: A Tutorial
The declaration of an enum data type called rainbow with the enumeration constants red, orange, yellow, green, blue,
indigo, and violet would be:
As with structures, enumerated variables can be declared in one of two ways. The following examples show two
ways to declare a variable named part_list for an enum type called components that contains the enumeration
constants resistor, capacitor, inductor, diode, and transistor. The first method to declare the enum variable is:
Once an enum variable is declared it can assume any of the enumeration constant values. By default C assigns the
enumeration constants consecutive integer values to represent their position in the list, beginning with 0 for the first
constant up through n
–
1 for the nth constant. For the components data type the default position of resistor would be
0, capacitor would be 1, and transistor would be 4. The programmer can change these values by explicitly assigning
an integer value to one or more of the enumeration constants as shown below:
For the example above C will assign the specified position values to the enumeration constants resistor, diode, and
transistor respectively, and position values one higher than that of the preceding enumeration constant to the
unassigned constants capacitor and inductor. This gives the following values for the enumeration constants:
Note that different enumeration constants in an enum data type can share the same position value, and that the
position value is not the same as enumeration constant’s position in the list declaration. To keep things simple, it’s
generally best to let C assign default values to the enumeration constants unless there is a very compelling reason to
assign different values.
Because the enum data type is based on the int data type, the enum data type has operators similar to int arithmetic,
logic, and relational operators. These operators are:
= Assignment Operator
54
Section 9 - Defined and Derived Data Types
This operator is similar to the integer assignment operator and stores a specific enumeration
constant in an enum variable. An enum variable can be assigned only enumeration constants
defined for the specific enumeration data type of that enum variable.
== Equals Operator
The equals operator compares two enum operands and determines whether or not they have the
same position value. If the two operands have the same value the result is TRUE, and if not the
result is FALSE.
!= Not Equals Operator
The not equals operator compares two enum operands and determines whether or not they have
different position values. If the two operands have different values the result is TRUE, and if not
the result is FALSE.
<<= Lower Relational Operator
The lower relational operator compares two enum operands and determines whether or not the
position of the operand to the left of the operator is lower in the enumeration constant list than that
of the operand to the right of the operator. If the left operand has a lower position value than the
right operand the result is TRUE, and if not the result if FALSE.
>>= Higher Relational Operator
The higher relational operator compares two enum operands and determines whether or not the
position of the operand to the left of the operator is higher in the enumeration constant list than
that of the operand to the right of the operator. If the left operand has a higher position value than
the right operand the result is TRUE, and if not the result if FALSE.
Enumeration variables are very useful in switch statements. The following program demonstrates how programs can
use enum variables. One enum variable represents the days of the week. The other implements a Boolean data type.
Both make the program much easier to understand and analyze than a program using integer values.
When run, this program will enter and remain in a while loop until the value of the enum variable quit becomes
TRUE. Each time the loop executes, the switch statement will print out a line based upon the value of the enum
variable today. For the first iteration, the value of today is Monday, so the program executes the code associated with
the case statement for Monday. This prints “Monday’s
child
is
fair
of
face,” and changes the value of today to Tuesday.
The variable quit is still FALSE so the while loop repeats with today equal to Tuesday. This process continues until
the case statement for Sunday sets quit to TRUE, and the program exits the while loop. The program then prints “End
of
program.” and ends. The program output will be:
The program above may seem somewhat convoluted and trivial, but it embodies the basic structure of a software
state machine that embedded C programs often use and that occurs in software digital design tools like VHDL. Each
case statement specifies the actions that should take place in a specific state, and specifies the state to which the
machine should transition next. The following shows the C state machine for a Gray code sequence:
In this example the program does very little, but each case statement can contain statements to perform whatever
tasks the program should perform in that state.
that tests the value of the variable and its intent easier to understand than the integer expression
C provides another way for programmers to define program data types. This is the typedef expression which allows
programmer to assign a name to data types and use that name when declaring variables of that type. A typedef
statement has the form:
A typedef statement to set up a Boolean data type using an enum data type with enumeration constants FALSE and
TRUE would be:
typedef enum
58
Section 9 - Defined and Derived Data Types
{FALSE
=
0,
TRUE}
Boolean;
Similarly, a typedef statement to set up a polar data type using a struct data type with the fields magnitude and angle
would be:
typedef
struct
{
float
magnitude;
float
angle;
}
polar;
Once defined, the program can use the defined data type to declare variables in exactly the same way as for standard
C elementary and derived data types. For example, the C declaration for a Boolean variable named valid would be:
Boolean valid;
Similarly, the C declaration for a polar variable named admittance would be:
polar admittance;
At first defining a data type and declaring variables for that type doesn’t seem much different or any more useful
than using standard C data types. However, using typedef statements offers some significant advantages.
A basic advantage of typedef is that it helps make C code more readable. C allows programs to be very cryptic, so
programmer should attempt to make source code as readable as possible.
Another advantage of typedef is that simplify names of data types can help prevent coding errors. For example,
using long data type names like unsigned
long
int in a program is prone to typing errors and can become tedious.
However, the statement
allows a programmer to use uint32 to declare unsigned
long
int variables more simply and with less chance of errors.
Another advantage of typedef
is that a defined type allows programmers to make corrections throughout the program
by changing a defined data type in one place in the program. Suppose, for example, that a program contained the
following typedef statement:
After declaring numerous my_int variables throughout a program, the programmer discovered that the my_int
variables must be signed
long
int data types rather than unsigned
int. Without the my_int defined data type every
instance of unsigned
int in the program must be located and individually changed to signed
long
int. With the defined
my_int data type only the unsigned
int in the typedef statement need be changed.
59
Section 10 - Strings
10. Strings
10.1 Overview
Strings in C are sequences of characters. One difficulty in discussing strings is that the C language originally did not
define a string data type. Instead, it allowed C programs to work with character arrays. Unfortunately character
arrays and strings are not quite the same because the arrays fundamentally treat elements as independent values and
strings consist of characters that are treated as a whole. Fortunately many C compilers provide support for strings
because strings are used so often in programs. Although you may not have realized it, the printf() function has been
using strings. In the “Hello, world” program “Hello,
world\n” is a string. This chapter will examine the strings and
how to work with them in more detail.
char hello[12] = {'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd'};
This method has two drawbacks. The first is that initializing a character array with a string is extremely tedious. The
second is that it treats a string as collection of individual characters rather than an actual data type. However, just as
C considers any character between single quotes to be a char value, it considers any sequence of characters between
double quotes to be a string. Although C does not have a specific string data type, it does allow programs to
initialize character arrays with strings. The program below illustrates how this is done.
Hello, world
You should note two things about this program. The first is that although “Hello,
world” consists of only 12
characters, the character array to hold it has 13 elements. This is because a string is NOT just a sequence of
characters. When designation a sequence of characters as a string C uses a special format called ASCIIZ that adds a
zero, or NULL, after the last character in the string. The following program illustrates this.
62
Section 10 - Strings
10.3 Working with Strings
The scanf() and printf() functions specified in stdio.h can input and output strings. The previous section
demonstrated how to use the printf() function to print strings. Programs often use scanf() and printf() together so that
they can interact with the user. The program below is a simple example of how programs do so.
#include
<stdio.h>
/*
Required
for
printf()
and
scanf()
functions.
*/
void
main
(void)
{
char
first[80],
last[80];
printf
("Hello!
What
is
your
first
name?
");
scanf
("%s",
first);
printf
("Hello,
%s.
Nice
to
meet
you.
What
is
your
last
name?
",
first);
scanf
("%s",
last);
printf
("I
hope
that
the
%s
family
is
well,
%s.",
last,
first);
}
Assume that the user enters “John” and “Doe” when prompted for a first and last name. When the program is run it
will output the following:
Something you might have noticed is that the scanf() function, which requires the address of the variable to store
entered information, does not use the & symbol with the name of the character arrays. This is because C does not
treat array names (including names of character arrays used to store strings) as it does other variable names. When a
program uses the name of a variable it means the value contained in the variable, so that programs must use the &
symbol with the variable name to specify the address of the value. However, arrays contain multiple values so that
the name of the array cannot (and therefore does not) refer to the “value” of the array. Instead, C uses the name of an
array to refer to the starting address of an array, which is also the address of the first element of the array. For the
character array first[80] and last[80], for example, C considers first and &first[0] as well as last and &last[0] to be the
same. The following program demonstrates this.
#include
<stdio.h>
/*
Required
for
printf()
and
scanf()
functions.
*/
void
main
(void)
{
char
first[80],
last[80];
printf
("Hello!
What
is
your
first
name?
");
scanf
("%s",
&first[0]);
printf
("Hello,
%s.
Nice
to
meet
you.
What
is
your
last
name?
",
first);
scanf
("%s",
last);
printf
("I
hope
that
the
%s
family
is
well,
%s.",
&last[0],
&first[0]);
63
Programming in C: A Tutorial
}
C treats strings in the same way as it treats arrays. C refers to a string, such as “This
is
a
string.”, using the starting
address of memory that contains the sequence of characters.
There is no trick to this. Although the contents of buffer match the string, the program evaluates the Boolean
expression
to determine whether or not the address of buffer is the same as the address of “This
is
a
string.” and prints “The
strings
do
not
match.” because the addresses are not the same. Similarly, suppose a program cannot use the “=”
assignment operator to copy the contents of one character array into another because one address cannot be copied
into another address. One approach to accomplish these operations is to write your own code to do so. For example,
the following program copies the contents of string1 into string2:
64
Section 10 - Strings
char
string1[80]
=
“This
is
a
string.”,
string2[80]
=
“This
string
is
different”;
int
index
=
0;
printf
("Original:\n");
printf
("String
1
=
'%s'
and
String
2
=
'%s'\n",
string1,
string2);
printf
("\nNow
copying
String
1
to
String
2\n\n");
/*
Use
a
loop
to
copy
the
characters
from
string1
to
string2,
incrementing
the
index
with
each
loop.
End
the
loop
when
either
the
last
valid
element
is
copied
or
the
or
the
null
terminator
is
reached.
*/
while
((index
<
79)
&&
(string1[index]
!=
0))
string2[index]
=
string1[index++];
string2[index]
=
0;
printf
("New:\n");
printf
("String
1
=
'%s'
and
String
2
=
'%s'\n",
string1,
string2);
}
Original:
String
1
=
‘This
is
a
string’
and
String
2
=
‘This
string
is
different’
Now
copying
String
1
to
String
2
New:
String
1
=
‘This
is
a
string’
and
String
2
=
‘This
is
a
string’
Similarly, the following program determines whether two strings are identical.
65
Programming in C: A Tutorial
*/
index
=
0;
same
=
1;
while
((same
==
1)
&&
(index
<
79)
&&
(string1[index]
!=
0)
&&
(string2[index]
!=
0))
if
(string1[index]
!=
string2[index++])
same
=
0;
if
(same
==
1)
printf
("'%s'
is
the
same
as
'%s'\n",
string1,
string2);
else
printf
("'%s'
is
not
the
same
as
'%s'\n",
string1,
string2);
index
=
0;
same
=
1;
while
((same
==
1)
&&
(index
<
79)
&&
(string1[index]
!=
0)
&&
(string3[index]
!=
0))
if
(string1[index]
!=
string3[index++])
same
=
0;
if
(same
==
1)
printf
("'%s'
is
the
same
as
'%s'\n",
string1,
string3);
else
printf
("'%s'
is
not
the
same
as
'%s'\n",
string1,
string3);
}
While writing your own code to handle strings is adequate, C has library functions for working with strings. One of
these is the strcpy() function that does the same thing as the while loop in the above program. The following
program rewrites the program to use the strcpy() function:
66
Section 10 - Strings
printf
("New:\n");
printf
("String
1
=
'%s'
and
String
2
=
'%s'\n",
string1,
string2);
}
Note that the program must include the string.h reference so that the compiler can access the strcpy() function. Note
also that the strcpy() function specifies the destination as the first parameter, and the source as the second parameter.
When this program is run it will output the same information as before:
Original:
String
1
=
‘This
is
a
string’
and
String
2
=
‘This
string
is
different’
Now
copying
String
1
to
String
2
New:
String
1
=
‘This
is
a
string’
and
String
2
=
‘This
is
a
string’
Another string function that string.h allows programs to access is the strcmp() function. This function compares the
corresponding characters in two strings and uses and integer value to indicate whether or not the strings are
identical. If the strings are identical, the strcmp() function returns a value of 0. If the strings are not identical the
function returns a value of 1 or –1. A 1 indicates that the value of the first differing character in the first parameter is
greater than that of the corresponding character in the second parameter. A –1 indicates that the value of the first
differing character in the first parameter is less than that of the corresponding character in the second parameter.
Programs typically use strcmp() only to determine whether two strings are identical or not, although knowing the
relative value of two strings can simplify programs that must sort lists and other text values. The following program
demonstrates the basic use of strcmp() to determine whether or not two strings are identical:
67
Programming in C: A Tutorial
}
Note that the program stores the value returned by strcmp() in an integer variable. The if statement could have used
strcmp(string1,
string2) directly but programs often store the return value of a function in case the value must be
accessed more than once and this takes less time than evaluating the function each time.
When the program is run it will output the following:
Comparing
'This
is
a
string.'
with
'This
string
is
different.'
'This
is
a
string.'
is
not
the
same
as
'This
string
is
different.'
Comparing
'This
is
a
string.'
with
'This
is
a
string.'
'This
is
a
string.'
is
the
same
as
'This
is
a
string.'
68
Section 11 - Pointers
11. Pointers
11.1 Overview
A pointer contains a memory address that programs can use to access or store information at that memory address.
Because C does not restrict these addresses pointer can theoretically read from or write to any location in memory
(including the internal registers of processors that memory map their register set). This ability also makes pointers
very dangerous because accessing restricted memory locations can cause the program or computer system to crash.
This chapter examines the basics of how pointers are declared, initialized, and used in programs.
allocates two regions of memory of two bytes each. The program then associates the names int_val1 and int_val2
with the addresses of that allocated memory. When the program stores data in a variable the processor writes that
data to that variables’ memory address, and when the program obtains data from int_val the processor reads the data
at int_val’s address. The compiler and operating system work together so that once a program declares a variable
that variable name will always reference the same address, and other declared variables do not refer to the same
address. For the example above the compiler will not give int_val1 and int_val2 the same address.
A pointer is a data type that contains the address of a data type (including other pointers). In C pointers are four-byte
integer values, similar to the long
int data type. Programs typically work with the data at the address contained in the
pointer, although sometimes programs do work with the contents of the pointer. Because changing the contents of a
pointer changes the memory location that the pointer references, programs can use the same pointer to access
different memory locations simply by changing the contents of the pointer. For example, incrementing or
decrementing a pointer allows programs to access consecutive data locations in memory, like incrementing or
decrementing the index of an array.
Note that these two variables could also have been declared as follows:
Declaring a pointer is not the same as initializing a pointer, just as declaring a double variable is not the same as
assigning a double value to it. Compilers typically will generate a warning if a program attempts to use a variable
before initializing that variable. The big difference is that the consequences of using an uninitialized pointer can be
far more drastic than for other uninitialized data types, because the value of an uninitialized pointer can potentially
reference (and consequently change the data in) any location in the computer system.
69
Programming in C: A Tutorial
11.4 Initializing Pointers
There are several ways to initialize pointers in C. Which method is used depends upon the purpose of the pointer and
available memory information.
As the comments indicate, the first statement declares the pointer to float variable float_ptr. The second statement
then initializes the pointer to the hexadecimal 32-bit value (indicated by the hexadecimal prefix 0x and the long
suffix “L”) 0x00000010. While this is the simplest way it potentially is also the most dangerous because there is no
guarantee that it is safe to use the assigned memory address. Programmers use this method only when they have a
system memory map that ensures they safely can use specific memory regions.
An important note here is that the “L” suffix must be used when the value itself cannot guarantee that it is a 32-bit
value. For the example above the hexadecimal value 0x10, or decimal value 16, can be stored in one, two , or four
bytes. Using 0x00000010 will not help, as the compiler will see no difference between 0x10 or 0x00000010
because
they both represent the decimal value 16. To be safe, always add the “L” suffix when specifying a 32-bit value.
int
int_val,
*int_ptr;
/*
Declare
the
int
and
pointer
to
int
variables
*/
int_ptr
=
&int_val;
/*
Initialize
int_ptr
with
the
address
of
int_val
*/
As the comments indicate the first statement declares the int variable int_val and the pointer to int variable int_ptr.
The second statement then copies the address of int_val (indicated by the address prefix “&”) into int_ptr. This
method is safe, but you might wonder why you would need a pointer to variable when you can access the variable
directly. The next chapter will answer this question more fully in the next section, but consider the following
program:
1
4
9
16
25
36
49
64
81
End
of
program.
This may not seem like a very useful technique, because the program could have used a for loop similar to the one
that initialized the array to print the contents. There are some subtle advantages to using a pointer, however, One is
that the array required an extra variable called index to loop through the elements, whereas the pointer did not.
Another is that initializing a pointer once and then incrementing it usually is somewhat faster than using an index as
the offset from the starting address of an array to calculate an address each time the program must access the next
element.
Using a pointer with an array type is especially useful when working with strings. The following program
demonstrates how a pointer can be used with strings.
The program begins by declaring a char array initialized with the string “This
is
a
string.”, declaring a pointer to char
called char_ptr, and initializing the pointer to the starting address of the array. After printing the original string, the
program executes a while loop that uses the pointer to check each character in the string to determine whether the
character is between ‘a’ and ‘z’ (i.e., it identifies the lower case characters in the string). If the character is lower
case, it adjusts the character value in the string by the ASCII offset between “A” and “a”, effectively changing any
lower case character to an upper case character. When the pointer reaches the end of the string (indicated by a value
of 0) the program exits the loop and prints the new string. The output of the program is as follows:
Again, the program could have processed buffer as an array but this probably would not have been as efficient.
Programs often process strings, so programmers try to make programs that scan and modify them as efficient as
possible.
As the statement indicates, the program passes the function the number of bytes of memory it requires, and the
function returns a pointer value to the start of the allocated memory. If the operating system cannot allocate the
requested amount of memory it returns a NULL (or 0) value. Programs must always check the returned pointer value
to ensure that it is not NULL before attempting to use the pointer. The following program demonstrates the use of
malloc() for setting up an 80-byte character buffer.
72
Section 11 - Pointers
printf
(“What
is
your
name?
”);
scanf
(“%s”,
buffer);
printf
(“Hello,
%s!\n”,
buffer);
}
else
{
printf
(“Unable
to
allocate
memory.\n”);
}
printf
(“End
of
program.\n”);
}
This program is very similar to the early input and output programs that earlier chapters covered. However, rather
than using a pre-defined character array to store the input string this program uses malloc() to allocate 80 bytes of
memory and initializes the buffer pointer to char to the starting address of the allocated memory. If the starting
address returned by malloc() is not NULL, indicating that the operating system could allocate the requested memory,
the program proceeds to request the user’s name, store the response in allocated memory, and output the stored
string as part of a printf() message. If the starting address returned by malloc() is NULL, indicating that the operating
system could not allocate the requested memory, the program outputs a message to that effect and terminates.
Assume that the user’s name is Aubrey. If the program is able to obtain the requested memory when it is run, the
output will be as follows:
If the program was unable to obtain the requested memory when it is run, the output will be as follows:
For this example the malloc() program was used to allocate 80 bytes of memory. This is because the program was
written to set up the same amount of space as that for an 80-character array and each character requires one byte of
memory. This is an easy to calculate, but suppose a program required memory for a complicated structure consisting
of char, int, double, other struct, and array types? Calculating the required number of bytes could be quite difficult
and allocating the wrong amount of memory could cause serious problems. To solve this problem C provides a
special function called sizeof() that returns the number of bytes in a specified data type. The form of the sizeof()
function is:
The sizeof() function can be used in conjunction with the malloc() function to ensure that the program attempts to
allocate the correct number of bytes. The following statement shows how this is done:
The following program shows the program above modified to use the sizeof() function to allocate memory for 80
char values:
Here the parameter for the malloc() function is given as 80
*
sizeof
(char), which allocates memory for 80 char
values. This method can be used to allocate memory for any number of locations for any type of data.
74
Section 12 - Functions in C
12. Functions in C
12.1 Overview
Except for very simple programs or programming examples like those in this tutorial very few C programs are
written as a single piece of code. Most programs consist of multiple code sections, each of which accomplish some
specific task. These code sections in C are called functions. The main() portion of every C program is a function,
which differs from other functions only in that it is the entry point into the program. Other chapters have discussed
standard C functions like printf() and strcpy() that perform common program tasks. This chapter examines how to
write and use functions in C programs.
75
Programming in C: A Tutorial
The return_type indicates the data type that the function returns, such as int, float, or struct. If the function does not
return a value, the return type is void.
The function_name identifies the function and is used to call the function. Function names must be unique. In other
words, C programs cannot have two functions with the same name or have a function with the same name as a
function specified by an included header file. There are restrictions on function names just as with variables. In
general, function names that begin with an upper- or lower-case letter and contain only upper- or lower-case letters,
numbers, or underscores (_)are valid.
The parameter
list identifies the data types and names of data, or parameters, that will be passed to the function. If
the function has no parameters then the parentheses can be empty or contain the word void. Parameters in the list are
separated by commas. These parameters are used in the function body.
The local
variables identify the data types and names of variables that are used in the function in addition to the
parameters in the parameter list. Only the function has access to these variables unless they are passed to another
function.
The function
body consists of standard C statements just like those used in main(). Statements can be calls to other
functions (or even to the same function although this technique, called recursion, is beyond the scope of this
tutorial).
The return
return_value statement is the last statement executed by the function, although for functions returning
void this statement is omitted. The return_value must be the same type as the return_type. Typically return_value is
one of the local variables, although return can also specify a literal, such as
return 5;
return ‘c’;
for a char return value. Functions can even return values by specifying an expression such as
The function will evaluate the expression and return that value to the calling statement.
76
Section 12 - Functions in C
case
SEVERE_ERR
:
printf
("a
severe
error.\n");
break;
}
}
The above function assumes that the program using the function has defined error_type as an enum type using a
statement similar to the following:
The operation of the function is straightforward. Depending upon the value of the error_type variable error passed to
the function, it would print the type of error indicated by error. If the value of error is MINOR_ERR, the function
output will be as follows:
Programs often use a function of this sort to convert an enumerated type into a form that the user can understand.
Earlier demonstration programs could not directly output the values of enumerated Boolean type variables because
the 0 and 1 integer values the Boolean variables actually contained would be meaningless to most users. The
programs could have used an if or switch statement to output TRUE or FALSE as required, but this would have been
awkward. The following function would have been much more convenient:
Neither of the above functions returned a value. The following is an example of a function that returns a value to the
statement calling it.
For this example the calling statement passes the float value base to cube(). The function then calculates the cube of
base by multiplying base by itself three times, assigns the cubed value to result, and returns the value of result to the
calling statement. Note that this function could also be written as:
77
Programming in C: A Tutorial
float
cube
(float
base)
{
return
base
*
base
*
base;
}
In this example the function will evaluate the expression base
*
base
*
base and return that value directly to the
calling statement.
A final example that uses multiple parameters is the program below, which calculates the hypotenuse of a right
triangle using the Pythagorean theorem. Because this function uses the C sqrt() function, the program containing the
function must include a reference the math.h which contains information about the sqrt() function.
The return_type specifies the data type that the function will return.
The function_name identifies the name of the function associated with the function prototype.
The parameter
data
types comprise a list of the data types for all the parameters that will be passed to the function.
Data types in the list are separated by commas. The order of the data types must match the order of data types in the
function’s parameter list.
For the function examples shown in Section 12.3.2 the prototypes would be:
78
Section 12 - Functions in C
12.4 Calling Functions
Programs call function by including a statement that specifies the function, the values passed to the function, and
(optionally) a variable into which to copy the return value. The form of a function call is:
The return_variable is the variable into which the statement copies the return value of function_name. Because C can
use the return value directly this variable is not required, although it makes the code easier to understand and allows
other statements to use the return value. The following example use the Pythagoras() to demonstrate this:
is functionally identical to
#include
<stdio.h>
/*
Required
for
printf()
and
scanf()
functions.
*/
typedef
enum
error_type
{NO_ERR
=
0,
MINOR_ERR,
MODERATE_ERR,
SEVERE_ERR};
void
error_msg
(enum
error_type);
void
main
(void)
{
enum
error_type
error_level;
for
(error_level
=
NO_ERR;
error_level
<=
SEVERE_ERR;
error_level++)
{
error_msg
(error_level);
}
printf
("\nEnd
of
program\n");
}
79
Programming in C: A Tutorial
void
error_msg
(enum
error_type
error)
{
printf
("There
is
");
switch
(error)
{
case
NO_ERR
:
printf
("no
error.\n");
break;
case
MINOR_ERR
:
printf
("a
minor
error.\n");
break;
case
MODERATE_ERR
:
printf
("a
moderate
error.\n");
break;
case
SEVERE_ERR
:
printf
("a
severe
error.\n");
break;
}
}
This program tests the error_msg() function by looping through the various enumerated error_type values. Each loop
will call the error_msg() function and pass the current error_type value to it. The error_msg() function will then print
a message based upon the error_type value passed to it. When this program is run the output will be as follows:
The following program exercises the Boolean_msg() function in a manner similar to the previous program, looping
through the enumerated Boolean values and passing them to the Boolean_msg() function.
#include
<stdio.h>
/*
Required
for
printf()
and
scanf()
functions.
*/
typedef
enum
Boolean
{FALSE
=
0,
TRUE};
void
Boolean_msg
(enum
Boolean);
/*
Prototype
for
Boolean_msg()
function
*/
void
main
(void)
{
enum
Boolean
logic_state;
for
(logic_state
=
FALSE;
logic_state
<=
TRUE;
logic_state++)
{
Boolean_msg
(logic_state);
}
printf
("\nEnd
of
program\n");
}
80
Section 12 - Functions in C
void
Boolean_msg
(enum
Boolean
bool_val)
{
printf
("The
logic
state
is
");
switch
(bool_val)
{
case
FALSE
:
printf
("FALSE.\n");
break;
case
TRUE
:
printf
("TRUE.\n");
break;
}
}
The program below uses the cube() function to calculate the cube of an entered value. The program first requests the
user to enter a number. It then passes the value of the entered number to the cube() function, copies the return value
into the float variable cubed, outputs the result, and terminates the program.
#include
<stdio.h>
/*
Required
for
printf()
and
scanf()
functions.
*/
float
cube
(float);
/*
Prototype
for
cube()
function.
*/
void
main
(void)
{
float
number,
cubed;
printf
("Enter
a
number:
");
scanf
("%f",
&number);
cubed
=
cube
(number);
printf
("The
cube
of
%f
is
%f\n",
number,
cubed);
printf
("\nEnd
of
program\n");
}
float
cube
(float
base)
{
float
result
=
0;
result
=
base
*
base
*
base;
return
result;
}
Assume that the number 1.2 is entered. When the program is run the output will be:
81
Programming in C: A Tutorial
The program below uses the Pythagoras() function to calculate the hypotenuse of a right triangle from the values of
two sides entered by the user. Because the function uses the C sqrt() function the program includes the math.h
reference so that the compiler can access the function and compile the program.
#include
<stdio.h>
/*
Required
for
printf()
and
scanf()
functions.
*/
#include
<math.h>
/*
Required
for
sqrt()
function.
*/
float
Pythagoras
(float,
float);
/*
Prototype
for
Pythagoras()
function.
*/
void
main
(void)
{
float
x,
y,
z;
printf
("Enter
the
length
of
side
1:
");
scanf
("%f",
&x);
printf
("Enter
the
length
of
side
2:
");
scanf
("%f",
&y);
z
=
Pythagoras
(x,
y);
printf
("The
hypotenuse
of
%f
and
%f
is
%f\n",
x,
y,
z);
printf
("\nEnd
of
program\n");
}
float
Pythagoras
(float
x_side,
float
y_side)
{
float
hypotenuse;
hypotenuse
=
sqrt
(x_side
*
x_side
+
y_side
*
y_side);
return
hypotenuse;
}
Assume that the values 1.2 and 0.5 are entered at the prompts. When the program is run the output will be:
82
Section 12 - Functions in C
12.5 Using Pointers with Functions
The function examples discusses have returned a single value, which is all that C permits. Suppose, however, that
the function must return more than one value? One solution would seem to be modifying and passing back values in
the parameter list, as shown below:
At first it would seem that this would square the values of number1 and number2, but unfortunately (as the name of
the function indicates) this method does not work. When the program is run, the output will be:
The program output is exactly as desired until the last line. Although the output clearly shows that the values passed
to the doomed_to_fail() function were squared, the value passed to it by main() were not. This is precisely because
values in the variables, and not the variables themselves were passed to the function . When the program passed the
values of num1 and num2 it created copies of those values called number1 and number2, and those copies are all
that the doomed_to_fail() function can access or affect. The originals, which were declared in main(), is accessible
only within main. Similarly, variables declared within other functions and their parameter lists exist only within
those functions. On the most basic level you can think of the beginning and end braces of a function as boundaries
beyond which function variables do not exist and functions cannot reach. This creates a basic problem with
functions modifying values that are passed to them.
Fortunately C has a solution to this difficulty, which was actually revealed when the scanf() function was first
introduced. The solution is to pass the addresses of variables to the function, which can then use these addresses as
pointers back to the values. Even though C still creates copies of the addresses when the program passes the values
83
Programming in C: A Tutorial
to a function these addresses will still reference the same location. Using a copy of the address of a variable will still
modify that variable. The following example demonstrates how to modify the doomed_to_fail() program to use
addresses, which can be passed to functions and used as pointers. Note that the data types for the modified
this_works() function and prototype parameter list must be changed from int to int
*, or pointer to int.
Another example illustrates passing addresses to a function to copy the contents of one structure to another. In
reality a functions is not really necessary, because the assignment operator (=) can be used to copy the contents of
one structure to another structure of the same time.
(*struct_ptr).field
85
Programming in C: A Tutorial
programs can use the alternate notation
struct_ptr-‐>field
when using pointers to structures. The code below shows alternate form of polar_cpy() using this pointer notation.
86
Section 13 - In Conclusion
13. In Conclusion
C is a very complex language, far too complex for a short tutorial such as this to cover more than the essentials. This
tutorial has attempted to introduce and cover enough features in C for the user to write functionally useful programs
and provide enough information so that the reader can further explore C independently, both through other
references on C and hands-on experimentation writing, compiling, and running C programs. The best approach for
become more proficient with C programming is to begin with basic functional code, selectively modify the code,
and see how those code modifications affect how the program operates. You will probably find that each new
concept and feature you learn will make other features even easier to learn and apply. Writing programs will also
improve your understanding of other programming languages and how software and hardware interact.
87