You are on page 1of 93

Programming in C

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.

What Programming in C Is Not


Programming in C is not a software programming course. It does not attempt to cover programming concepts,
programming techniques, data structures, software design, or other programming topics beyond those necessary to
present C programming concepts.

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.

How to Use This Tutorial


Programming in C (as in any compiled programming language) consists of three basic steps.
1) Writing the program code.
2) Compiling and linking (building) the program code.
3) Verifying the program code.
v
Programming in C: A Tutorial
The chapters in this tutorial provide examples of C programming code that have been tested to verify that they will
compile and execute properly. However, you should use your programming software to enter, compile, and execute
the examples on you own system. This will allow you to familiarize yourself with your programming software and
personally verify that the code works as intended, reinforcing the presented concepts. Some chapters also contain
some suggested programming exercises for you to explore so that you can gain a better understanding of the C
programming language and C programming.

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.2 The HELLO.C Program


The source code, or listing, for the HELLO.C program is shown below. Line numbers are shown for reference only
and are not part of the C program.

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.2.1 Program Comments


Comments are an important part of programming. Comments serve to document the program by explaining how the
program works (or is supposed to work) and providing historical information about the program, such as the
programmers and dates and details of any program changes. Another use for comments is to temporarily remove
sections of code from the program for debugging without actually removing it from the source file. Because the
compiler ignores comments when generating the final executable code, the programmer can essentially remove, or
“comment out”, any part of the program by marking those lines of code as comments.
Line 1 in the HELLO.C listing shows the first way to include a comment in a C program. Any line that begins with
two forward slashes (//) in a C program is a comment. This method applies only to the line that begins with the two
forward slashes.
Line 3 in the HELLO.C listing shows the second way to include a comment in a C program. While the “//”
sequence can be placed at the start of each comment line, this method can be inconvenient (and time consuming) for
comments that require multiple lines. The second method identifies a comment by using a “/*” to indicate the start
of a comment and allows programmers to easily comment blocks (or multiple lines) of code in a program. In this
method a “/*” sequence identifies the start of a comment and a “*/” sequence to identify the end of a comment.
These two sequences and anything between them is considered a comment and ignored by the compiler. Although
Line 3 of HELLO.C places the start and end sequences on the same line they can be placed on separate lines to
create comments that are several lines long. The example below shows how a multiline comment can be used to
create a framed header for a program or C function.

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.  
*/  

1.2.2 Blank Lines


Lines 2, 4 and 6 are blank lines. The C compiler ignores blank lines, which programmers can use freely to make
program listings easier to read. Blank lines, as well as spaces and tabs, are examples of what is called “white space”.
White space is used to separate elements of the program so that the compiler can correctly interpret the code and
generate the executable file.

1.2.3 Preprocessor Directives


Compiling a C program consists of multiple steps. The basic steps to creating an executable file are as follows:
1. The preprocessor prepares the source file so that the compiler can process the source code.
2. The compiler converts the source code to assembly language.
3. The assembler converts the assembly language to executable object code.
4. The linker integrates the executable object code modules into a single executable program file.
As step 1 indicates the preprocessor ensures that the compiler can process the source code. Preprocessor directives
provide the preprocessor with information that it needs to properly prepare the source file for the compiler.
Line 5 is one example of a preprocessor directive. The #include statement directs the preprocessor to include
information that is not contained within the HELLO.C file itself but that the executable program will require.

1.2.4 Header Files


Many of the functions used in C are contained in library modules. The #include preprocessor directive is used to
specify the header files required to access and use these functions. In Line 5, the #include directive specifies that the
preprocessor is to access and include information in the stdio.h file so that HELLO.C can access and use the printf()
2
Section 1 - Hello, World
function. Information that a header file provides are the names of C functions that the program can access, the values
(if any) that the functions return, and the parameters (if any) that are passed so that the function can process them.
Information about functions in a header file are called function prototypes. The following shows the prototype for
the printf() function in a stdio.h header file.

int          _RTLENTRY  _EXPFUNC  printf(const  char  *  __format,  ...);  

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.

1.2.5 The Main Function


Lines 7 through 10 comprise the main body of the program, called (logically enough) main(). The main function is
the entry point into the C program, and when it finishes the C program ends. As with any other function main() can
return a value and accept parameters to process, although many C programs use neither. In HELLO.C the value void
at the beginning of Line 7 indicates that the program will ignore any value that the HELLO.C program returns.
Similarly, the empty parentheses in main  ()indicates that no parameters are being passed to the program for it to
process. Placing the word void within the parentheses would indicate the same thing as leaving the parentheses
empty.
Lines 8 and 10 contain curly brackets (or braces) that contain the body of code for the main() function. The left curly
bracket (“{“) indicates the start of code, and the right curly bracket (“}“) indicates the end of code. In this case the
only line of code for main() is the printf() statement on Line 9. When the HELLO program runs, it enters the main()
function, executes the printf() function, and ends.

1.2.6 The printf() Function


Line 9 contains the printf() function, which is the only real line of code in the entire HELLO.C program. Although
printf() can return a value, the HELLO.C program ignores it simply by not assigning it to anything. If you ever wish
to use the return value of printf() (or any other function), you would assign it to a variable as shown below.

retval  =  printf  (“Hello,  world\n”);  

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.

1.2.7 Coding Style


Every programmer has his or her own coding style, and C is extremely tolerant about how you chose to structure
your code. In general, there are two basic coding styles.
The first style is the structured style that is widely used in structured programming languages like Pascal and
Modula 2. The HELLO.C listing uses the structured style. An advantage of the structured style is that it is very easy
to match the beginning and end of code blocks because they are equally indented, as are Lines 8 and 10 of the
HELLO.C listing. This helps to ensure that curly brackets are always paired and makes blocks of code, especially
nested blocks of code, easier to identify. A disadvantage of this method is that each new level of indentation leaves
less space for each line of code so that code can become harder to read and edit.
The second style, sometimes called the C style, is a more compact form. The HELLO.C listing in this style would
look similar to the following.

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. Elementary Data Types


2.1 Overview
The first computers were designed to process numerical data, such as calculating tables of values or finding
solutions to very complicated calculations. It was only natural that many of the first programming languages, such as
FORTRAN (for “FORmula TRANslation”), were designed primarily to work with numbers. The introduction of the
personal computer changed the role of computers, allowing users to work not just with numbers but text, graphics,
and other types of information as well. Programming languages like BASIC and C also changed to support these
data types and allow programmers to more easily work these types of information. In this chapter you will learn
about the basic data types supported in C. In later chapters you will learn about other data types that are based on
these basic data types.

2.2 Elementary Data Types in C


All computers ultimately work with binary data values, so it should be no surprise that numeric types make up a
large part of the data types in C. Even data types that are not strictly numeric, such as characters, are stored as binary
values and can be represented in C as numbers. Because all data is ultimately binary in nature data types in C really
deal more with how programs represent and operate on data rather than the actual nature of the data.
Note that C has restrictions on valid variable names. As a rule, any variable name that begins with an upper- or
lower-case letter and contains only upper- or lower-case letters, numbers, and underscores (_) are valid.

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.

short  int   short_var;   /*  “short  short_var;”  is  also  acceptable  */  

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.

long  int   long_var;   /*  “long  long_var;”  is  also  acceptable  */  

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.

char   keypress   /*  A  variable  to  hold  a  single  keypress  */  

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.

2.2.3 Floating Point Values


The floating point data type deals with numbers that have a fractional portion. For example, 2.7183 and 1.414 are
floating point values, but 111111111 and 123456 are not. Note that the fractional portion of a floating point value
can be zero, so that 2 is an integer value but 2.0 is a floating point value. In the real world the two values may be the
same, but they are different in C and are not interchangeable. In addition to representing numbers with fractional
parts, floating point values greatly extend the range of numbers that computers can process. In C there are two basic
types of floating point data types: float and double.
The float data type represents floating point values as 32-bit single-precision numbers defined by the ANSI floating
point standard, in which one bit represents the sign, eight bits represents the binary exponent, and the remaining
twenty-three bits represents the 24-bit mantissa (the most significant bit of the mantissa is always assumed to be 1).
32-bit float values range from about ±1.8 × 10–38 to ±3.4 × 10+38, compared to approximately 0 to ±2.1 × 109 for 32-
bit long  int values. A float value named float_var is declared using a statement similar to that below.

float   float_var   /*  Single-­‐precision  floating  point  value  */  

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.

double   double_var   /*  Double-­‐precision  floating  point  value  */  

2.3 Other Data Types


In addition to the elementary data types, C supports extended data types that will be introduced and discusses in later
chapters. These include arrays (collections of identical data types treated as single entities), structures (collections of
different data types treated as single entities), and enumerated types (variables that can assume a limited set of user-
specified values). A special type of array, called the string data type, was not part of the original C standard but is
supported by many popular compilers and will be covered separately.

6
Section 3 - Input and Output

3. Input and Output


3.1 Overview
For computers to process data their users must have some means of supplying information for the computer to
process (called the input) and retrieving the processed data (called the output). C provides a number of functions for
program input and output. The printf() function, which you saw in Chapter 1, is an example of an output function
and is typically used to output information to the computer monitor. In this chapter you will learn more about
printf() and scanf(), the corresponding output function.

3.2 The scanf() Function


Just as the printf() function outputs data to the computer’s standard output device (the monitor) the scanf() function
inputs data from the computer’s standard input device (the keyboard). As its name suggests, the scanf() function
“scans” the stream of input entered from the keyboard and places that input in variables that the computer can
process. Because the keyboard input is a sequence of binary values, the scanf() function must specify the meaning of
that data – such as whether it is an char, int, float, etc. – and the variable into which that data should be placed so that
the program can properly process it. The scanf() function has a large number of options, called format specifiers, to
do so but for simplicity this chapter will present only the most basic operation of the scanf() function.

3.2.1 The Basic scanf() Format


The most basic format of the scanf() function is

scanf  (“%<variable  type>”,  &<variable>)  

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

c   ASCII character value  


d   Signed decimal integer value
D Signed decimal long integer value
e   Float value using exponents
f   Float value
lf Double value
o Octal integer value
O Octal long integer value
u   Unsigned decimal integer value
U Unsigned decimal long integer value
x   Hexadecimal integer value
X Hexadecimal long integer value

Note that the data type of the specified variable must be of the same as that of the variable type.

3.2.2 Using the scanf() Function


Information for the scanf() function is located in the stdio.h file, so programs that use the scanf() function must use a
#include statement to specify this. In addition, the program must declare a variable into which the scanf() statement
will place the information. The following is an example of a program that will allow the user to enter an integer into
the variable number. Note that the program also uses the basic printf() function.

7
Programming in C: A Tutorial

#include  <stdio.h>   /*  Required  for  printf()  and  scanf()  functions  */  


 
void  main  (void)  
   {  
       int  number;  
 
       printf  (“Please  enter  an  integer  value:  ”);  
       scanf  (“%d”,  &number);  
   }  

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.

#include  <stdio.h>   /*  Required  for  printf()  and  scanf()  functions  */  


 
void  main  (void)  
   {  
       int  number;  
       char  keypress;  
 
       printf  (“Please  enter  an  integer  value  and  a  character:  ”);  
       scanf  (“%d  %c”,  &number,  &keypress);  
   }  

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.

3.3 The printf() Function


Although Chapter 1 introduced the printf() function, which the program example also used in Section 3.2.2, this was
the most basic form of the function that did nothing more than output a fixed string of characters. The printf()
function can also output variable values in much the same way as the scanf() function inputs them. In addition,
however, it can mix those output variables with fixed strings of characters to help clarify the output.

3.3.1 The Basic printf() Format


You have already seen the most basic format of the printf() function that outputs a string of characters. The most
basic format of the printf() function that output the value of a variable is

printf  (“%<variable  type>”,  <variable>)  

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

c   ASCII character value  

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.

3.3.2 Using the printf() Function


Information for the printf() function is located in the stdio.h file, so programs that use the printf() function must use a
#include statement to specify this. In addition, the program must declare and initialize the variable that the printf()
statement will print to the screen. The following is an example of a program that initializes the integer and float
variables int_var  and  float_var and prints them to the screen.
 
#include  <stdio.h>   /*  Required  for  printf()functions  */  
 
void  main  (void)  
   {  
       int  int_var  =  123;  
       float  float_var  =  123.4  
 
       printf  (“Integer  =  %3d  and  float  =  %5.1f\n”,  int_var,  float_var);  
   }  
When this program is compiled and run it will output the following:

9
Programming in C: A Tutorial

Integer  =  123  and  float  =  123.4  

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:

Integer  =  +123  and  float  =  +123.4  

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.

3.4 Combining the scanf() and printf() Functions


The scanf() and printf() functions can be used together to verify with the printf() function that values entered into a
program with the scanf() function yield the correct values. Because these functions also permit data to be entered
and displayed in different formats, they can also be combined to create an interesting utility to convert values
between number systems.

3.4.1 Verifying scanf() with printf()


To use the printf() function to verify values entered with the scanf() function, the printf() function simply specifies
the scanf() variables as the printf() variables with any string of characters to clarify the output. As an example, the
scanf() program to enter an integer and character could be modified as shown below.

#include  <stdio.h>   /*  Required  for  printf()  and  scanf()  functions  */  


 
void  main  (void)  
   {  
       int  number;  
       char  keypress;  
 
       printf  (“Please  enter  an  integer  value  and  a  character:  ”);  
       scanf  (“%d  %c”,  &number,  &keypress);  
       printf  (“The  integer  you  entered  was  %d\n”,  number);  
       printf  (“The  character  you  entered  was  %c\n,  keypress”);  
   }  

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.

#include  <stdio.h>   /*  Required  for  printf()  and  scanf()  functions  */  


 
void  main  (void)  
   {  
       int  number;  
         
       printf  (“Please  enter  a  decimal  integer  value:  ”);  
/*  
       Use  %d  to  enter  the  integer  as  decimal  with  scanf()  and  %o  to  print  
       the  value  as  octal  with  printf().  
*/  
       scanf  (“%d”,  &number);  
       printf  (“\nThe  octal  value  is  %o\n”,  number);  
   }  

If the decimal number entered is 100 the program will output the following:

Please  enter  a  decimal  integer  value:  100  


The  octal  value  is  144  

Similarly, a program to convert hexadecimal values into decimal is show below.

#include  <stdio.h>   /*  Required  for  printf()  and  scanf()  functions  */  


 
void  main  (void)  
   {  
       int  number;  
         
       printf  (“Please  enter  a  hexadecimal  integer  value\n”);  
       printf  (“(Use  lower  case  for  a  through  f):  “);  
/*  
       Use  %x  to  enter  the  integer  as  hexadecimal  with  scanf()  and  %d  to  print  
       the  value  as  decimal  with  printf().  
*/  
       scanf  (“%x”,  &number);  
       printf  (“\nThe  decimal  value  is  %d\n”,  number);  
   }  

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.

4.2 Arithmetic Operators in C


Arithmetic operations can operate on both integer and floating point values and use one or two operands. Operators
that use a single operand are called unary operators. Operators that use two operands are called binary operators.
When an operation, such as addition, uses two operands it is best always to use operands of the same data type.
Although C can and will evaluate expressions that contain different operand data types the results may not be what
you expected and cause your program to malfunction.

4.2.1 Unary Arithmetic Operators


The unary arithmetic operators in C consist of the following:
= Assignment
The assignment operator copies the value of the operand to the right of the = operator into the
operand to the left. The operand on the right can be a literal, variable, or expression, but the
operand on the left must be a variable. This means that a C statement that copies the sum of 2 and
3 into the variable sum is written
sum  =  2  +  3;  
and not
2  +  3  =  sum;  
the way arithmetic expressions are normally written.
-­‐ Change sign
The change sign operator will convert a positive signed number to a negative signed number and
vice versa. The change sign operator operates on both actual numbers (called literals) and
variables. Note that although -­‐5 indicates “negative five”, the expression –int_var does not
necessarily indicate a negative value. The expression –int_var means “the opposite of int_var”. If
the value of “int_var” is positive then the value of “-­‐int_var” is negative, but if the value of
“int_var” is negative then the value of “-­‐int_var” is positive.
++ Increment by one
The increment by one operator will increase the value of an integer variable by one. This operator
can be placed immediately before the operand for a pre-increment operation, or immediately after
the operand for a post-increment operation. A pre-increment operation changes the value of the
operand before the operand is used in a program statement, and a post-increment operation
changes the value of the operand after the operand is used in a program statement. As an example,
assume that the value of int_var is 7. Then

printf  (“The  number  is  %d.\n”,  ++int_var);  


printf  (“The  new  number  is  %d.\n”,  int_var);  

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,

printf  (“The  number  is  %d.\n”,  int_var++);  


printf  (“The  new  number  is  %d.\n”,  int_var);  

will print

The  number  is  7.  


The  new  number  is  8.  

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.

4.2.2 Binary Arithmetic Operators


Binary arithmetic operators in C use two operands. Arithmetic expressions in C always places the operator between
the two operands, just as standard arithmetic expressions do. As an example, the expression to add the variable
int_var and the number 3 would be

int_var  +  3  

The binary arithmetic operators in C consist of the following:


+ Addition
The addition operator will give the sum of the two operands.
-­‐ Subtraction
The subtraction operation will give the difference of the two operands.
* Multiplication
The multiplication operation will give the product of the two operands.
/ Division
The division operation will give the quotient of two floating point operands or the integer portion
of the quotient of two integer operands.
% Modulus

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  

is 4, because 5 divides evenly into 14 twice with a remainder of 4.

4.3 Order of Arithmetic Operations in C


C evaluates expressions from left to right using the following default order of operations:

++, -­‐-­‐ Increment and decrement by one operators


*, /, % Multiplication, division, and modulus operators
+, -­‐ Addition and subtraction operators

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

4.4 Some Programming Examples with Arithmetic Operators


The following program demonstrates the results for the various arithmetic operators on both integers and floating
point values to illustrate how the operators function.

4.4.1 Demonstration of Arithmetic Operations


This program demonstrates the operation of the C unary and binary arithmetic operations.

#include  <stdio.h>   /*  Required  for  printf()  and  scanf()  functions  */  


 
void  main  (void)  
15
Programming in C: A Tutorial
   {  
       int  int_1,  int_2,  result;  
       float  float_1,  float_2;  
 
/*  
       Input  integer  values  
*/          
       printf  (“Please  enter  integer  1:  ”);  
       scanf  (“%d”,  &int_1);  
       printf  (“\nPlease  enter  integer  2:  ”);  
       scanf  (“%d”,  &int_2);  
       result  =  int_1  +  int_2;  
       printf  (“\nThe  sum  is  %d:  ”,  result);  
       result  =  int_1  -­‐  int_2;  
       printf  (“\nThe  difference  is  %d:  ”,  result);  
       result  =  int_1  *  int_2;  
       printf  (“\nThe  product  is  %d:  ”,  result);  
       result  =  int_1  /  int_2;  
       printf  (“\nThe  integer  quotient  is  %d:  ”,  result);  
       result  =  int_1  %  int_2;  
       printf  (“\nThe  integer  remainder  is  %d:  ”,  result);  
       result  =  int_1++;  
       printf  (“\nThe  incremented  value  of  integer  1  is  %d:  ”,  result);  
       result  =  int_2-­‐-­‐;  
       printf  (“\nThe  decremented  value  of  integer  2  is  %d:  ”,  result);  
 
/*  
       Skip  two  lines  for  spacing  
*/  
       printf  (“\n\n”);  
 
 
/*  
       Input  floating  point  values  
*/          
       printf  (“Please  enter  floating  point  1:  ”);  
       scanf  (“%f”,  &float_1);  
       printf  (“Please  enter  floating  point  2:  ”);  
       scanf  (“%f”,  &float_2);  
       printf  (“\nThe  sum  is  %f:  ”,  float_1  +  float_2);  
       printf  (“\nThe  difference  is  %f:  ”,  float_1  –  float_2);  
       printf  (“\nThe  product  is  %f:  ”,  float_1  *  float_2);  
       printf  (“\nThe  quotient  is  %f:  ”,  float_1  /  float_2);  
       printf  (“\nThe  incremented  value  of  floating  point  1  is  %f:  ”,  float_1++);  
       printf  (“\nThe  decremented  value  of  floating  point  2  is  %f:  ”,  float_2-­‐-­‐);  
}  

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:

Please  enter  integer  1:  2  


Please  enter  integer  2:  3  
 
The  sum  is  5  
The  difference  is  -­‐1  
The  product  is  6  
The  integer  quotient  is  0  
The  integer  remainder  is  2  
The  incremented  value  of  integer  1  is  3  
The  decremented  value  of  integer  2  is  2  
 
Please  enter  integer  1:  2.0  
Please  enter  integer  2:  3.0  
 
The  sum  is  5.00000  
The  difference  is  -­‐1.000000  
The  product  is  6.000000  
The  integer  quotient  is  0.666667  
The  incremented  value  of  integer  1  is  3.000000  
The  decremented  value  of  integer  2  is  2.000000  
 
One item to note is how the value of the expressions are handled for the integer and floating point sections of the
program. In the integer section of the program the value of the expression first is assigned to a variable named result
and the printf() function then prints the value of result. In the floating point section the printf() functions print the
value of the expressions directly. This is because every expression evaluates to some value, and that value can be
assigned to a variable or C can use the value directly in functions or other expressions.

4.4.2 Quadratic Formula Evaluation


The second program evaluates the quadratic formula ax2 + bx + c by having the user first input the values of the
coefficients a, b, and c and then having the user input a value of x.

#include  <stdio.h>   /*  Required  for  printf()  and  scanf()  functions  */  


 
void  main  (void)  
   {  
       float  a,  b,  c,  x;  
       float  answer;  
 
/*  
       Input  floating  point  value  
*/          
       printf  (“Please  enter  coefficient  a:  ”);  
       scanf  (“%f”,  &a);  
       printf  (“Please  enter  coefficient  b:  ”);  

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:

Please  enter  coefficient  a:  –1.5  


Please  enter  coefficient  b:  3.1  
Please  enter  coefficient  c:  0.7  
Please  enter  coefficient  x:  5.0  
 
The  answer  is  -­‐21.300001  
 
Note that the answer is not exactly –21.3, which is the answer a calculator would give. This is because the binary
number system cannot represent decimal floating point numbers exactly. One solution would be to modify the
printf() statement so that it limits the fractional portion only to one digit.

printf  (“\n\nThe  answer  is  %4.1f\n”,  answer);  


 
This will have the program prints the answer as -­‐21.3. Another solution would be to change the data type of the
variables a, b, c, and x from float to double for higher precision in the calculation.

4.5 Convenient Operator Shortcuts


A binary arithmetic expression does not automatically change the value of an operand. For example, assume that the
value of int_val is 12. The expression

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

int_val  =  int_val  *  6;  

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

int_val  *=  6;  

Similarly, an expression that is equivalent to loop++ and increases the value of loop by one is

loop  +=  1;  

because this is equivalent to

loop  =  loop  +  1;  

One useful expression that multiplies a variable (called squared in this example) by itself is

squared  *=  squared;  

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.

5.2 Logic Operators in C


Logic operators can operate only on integer values, but like arithmetic operators can use one or two operands.
Unlike arithmetic operators, logic operands can be an entire integer value or the individual bits of an integer value.
Typically “logic operator” means that the operation involves integer values as a whole, whereas “bitwise operator”
means that the operation involves individual bits of integer values.
Logic expressions always evaluate to one of two integer values, call Boolean values. These two values are 0 (or
FALSE) and not 0 (or TRUE). Many times program use 1 to represents a TRUE value, although any non-zero
integer can represent a TRUE value.

5.2.1 Unary Logic Operators


Only one unary logic operator exists in C, which is detailed below.
! Logic NOT (Inverter)
The logic NOT operator, represented by an exclamation point (sometimes pronounced “bang” by
old-time C programmers), corresponds to a digital inverter and will invert a logical value from
TRUE to FALSE or from FALSE to TRUE. The logic NOT operator is placed immediately
before the value to be inverted. Assume that the value of logic_var is FALSE. The expression that
inverts logic_var is

!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
 
   }  

When run, this program will output the following:

false_val  =  0,  !false_val  =  1  


true_val  =  1,  !true_val  =  0  

5.2.2 Binary Logical Operators


Binary logic operators in C use two operands. Logic expressions in C always place the operator between the two
operands, just as with arithmetic expressions in C. As an example, an expression to AND the variable logic_var and
the decimal number 3 would be

logic_var  &&  3  

The binary logic operators in C consist of the following:


&& Logic AND
The logic AND operator will produce a TRUE or FALSE value according to the following rules.

• 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.

Assume, for example, that logic_var = 1 and bool_var = 0. Then

logic_var  &&  bool_var  

is FALSE, because one of the variables (bool_var) is FALSE. On the other hand,

logic_var  &&  !bool_var  

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:

A  =  0,  B  =  0,  A  &&  B  =  0  


A  =  0,  B  =  1,  A  &&  B  =  0  
A  =  1,  B  =  0,  A  &&  B  =  0  
A  =  1,  B  =  1,  A  &&  B  =  1  

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.

Assume, for example, that logic_var = 0 and bool_var = 0. Then

logic_var  ||  bool_var  

is FALSE, because both variables (logic_var and bool_var) are FALSE. On the other hand,

!logic_var  ||  bool_var  

is TRUE because at least one of the operands (!logic_var,  the inverse of logic_var) is TRUE.

The following program demonstrates how the logic OR 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);  
       printf  (“A  =  %d,  B  =  %d,  A  ||  B  =  %d\n”,  true_val,  false_val,  

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:

A  =  0,  B  =  0,  A  ||  B  =  0  


A  =  0,  B  =  1,  A  ||  B  =  1  
A  =  1,  B  =  0,  A  ||  B  =  1  
A  =  1,  B  =  1,  A  ||  B  =  1  

5.3 Bitwise Operators in C


Bitwise operators, as with logic operators, use only integer values but operate on and affect the individual bits of
operands. Bitwise operators always required two operands. Bitwise operators allow programmers to selectively test,
set, clear, invert, and shift bits, which is very useful when working with digital hardware. As an example, bitwise
operators allow programs to determine the value of individual data lines on an input port, or light a specific LED of
an LED bank attached to an output port.

5.3.1 Bitwise Logic Operators


Bitwise logic operators in C implement digital logic functions like logic operators, but apply the functions to
corresponding bits of the operands rather than the operands as a whole. As an example, bitwise ORinging the int
hexadecimal values 0x12 and 0xA5 (binary values 00010010 and 01011010, respectively) will do the following:

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  

The bitwise logic operators in C consist of the following:

& Bitwise AND


The bitwise AND operator will AND the corresponding bits of two operands together and produce
bits according to the following rules.
24
Section 5 - Logic Operators

• If both bits are 1, the value of the result bit is 1.


• If either or both bits are 0, the value of the result bit is 0.

Assume, for example, that op_1 = 0xF0 and op_2 =  0x55, or binary  11110000 and 01010101,
respectively. Then

op_1  &  op_2  

will produce the following result:

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:

Enter  a  number  key:  4  


You  pressed  the  ‘4’  key.  
The  ASCII  value  is  0x34.  
The  value  of  the  key  is  4.  

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.

• If both bits are 0, the value of the result bit is 0.


• If either or both bits are 1, the value of the result bit is 1.

Assume, for example, that op_1 = 0x81 and op_2 =  0x5A, or binary  10000001 and 01011010,
respectively. Then

op_1  |  op_2  

will produce the following result:

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:

Enter  the  first  number:  3  


Enter  the  second  number:  5  
The  addition  is  3  +  5  =  8.  
The  sum  is  ‘8’.  

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.

^ Bitwise XOR (Exclusive-OR)


The bitwise logic XOR operator will exclusive-OR the corresponding bits of two operands
together and produce bits according to the following rules.

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  

will produce the following result:

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:

Enter  a  number:  17  


Enter  another  number:  36  
Number  1  =  17,  Number  2  =  36.  
   
Number  1  =  36,  Number  2  =  17.  
Your  numbers  have  traded  places!  

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.

5.3.2 Bitwise Shift Operators


Bitwise shift operators allow programmers to shift operands by one or more bits left or right, just as hardware shift
register shift data left or right. When operands shift to the left, C shifts a 0 into the least significant (rightmost) bit
position. When operands shift to the right, C shifts the sign bit into the most significant (leftmost) bit position. For
positive integers the sign bit is 0, and for negative integers the sign bit is 1.
The bitwise shift operators in C consist of the following:

<< Bitwise Shift Left


The bitwise shift left operator will shift the first integer operand to the left by the number of bits
indicated by the second operand. Assume, for example, that op_1 = 15 and op_2 =  3, or binary  
00001111 and 00000011, respectively. Then

op_1  <<  op_2  

will shift the value of op_1 to the left by three places as shown:

Shift 1: 00001111 → 00011110  


Shift 2: 00011110 → 00111100
Shift 3: 00111100 → 01111000

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:

Enter  a  number:  100  


100  decimal  =  01100100  binary  

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

op_1  >>  op_2  

will shift the value of op_1 to the right by four places as shown:

Shift 1: 11110000 → 01111000  


Shift 2: 01111000 → 00111100
Shift 3: 00111100 → 00011110  
Shift 4: 00011110 → 00001111  

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.

Shift 1: 00010000 → 10001000 (-


Shift 2: 10001000 → 11000100
Shift 3: 11000100 → 11100010  
Shift 4: 11100010 → 11110001  

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.

5.4 Convenient Operator Shortcuts


Just as there are operator shortcuts for arithmetic operators, there are similar operator shortcuts for logical operators
that appends the = assignment operator to the logical operator. For example, the statement

op_1 &&= op_2;

is the same as

op_1 = op_1 && op2;

and

number |= bit_mask;

is the same as

number = number | bit_mask;


31
Programming in C: A Tutorial

The one exception to this rule is for the logical NOT, or invert, operator. The statement

val_1 != val_2;

is NOT the same as

val_1 = 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.

6.2 Relational Operators in C


C uses relational operators to compare two values and determine the relationship between them. For example,
relational operators can determine whether or not two numbers are equal or, if not, which number is larger than the
other. Because comparisons between different things are meaningless (such as when trying to compare apples and
oranges), relational operators must use operands of the same type. Although C will allow relational operators to use
different data types, like comparing unsigned integers with signed integers or characters with doubles, the results of
these operations are typically meaningless.
The relational operators in C consist of the following:

== 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:

(1.0  /  2.0)  ==  0.5  =>  1  


(1.0  /  10.0)  ==  0.1  =>  0  

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.

!= Not Equals Operator


The not equals operator indicates whether or not two operands have the same value, similar to the
equals operator, but the result will be FALSE if the operands are equal and TRUE if the operands
are not equal. The not equals operator has the same limitations with floating point precision as the
equals operator. The program below replaces the equals operator in the above program with the
not equals 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);  
   }  

As you might expect, this program will output the following:

(1.0  /  2.0)  !=  0.5  =>  0  


(1.0  /  10.0)  !=  0.1  =>  1  

< Less Than Operator


The less than operator indicates whether or not the first operand is less than the second operand. If
the first operand is less than the second operand the result is TRUE, but if the first operand is
greater than or equal to the second operand the result is FALSE.
 
<= Less Than or Equal Operator
The less than or equal operator indicates whether or not the first operand is less than or equal to
the second operand. If the first operand is less than or equal to the second operand the result is
TRUE, but if the first operand is greater than the second operand the result is FALSE.

> Greater Than Operator


The greater than operator indicates whether or not the first operand is greater than the second
operand. If the first operand is greater than the second operand the result is TRUE, but if the first
operand is less than or equal to the second operand the result is FALSE.
 
>= Greater Than or Equal Operator

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.

6.3 Looking Ahead


As the overview indicated, most C programs use relational operators to control how programs operate. Specifically
the relational operators allow programs to make decisions about when and when not to execute specific statements.
This type of execution is called conditional execution, because whether or not statements execute depends upon
whether or not some condition or set of conditions is satisfied. The next section will introduce the how C programs
make decisions and conditionally execute statements.

35
Section 7 - Decisions and Conditional Execution

7. Decisions and Conditional Execution


7.1 Overview
The previous chapters introduced basic components of C, such as basic data types, functions, and operators, and
shown some simple programs that illustrate how C uses these components. These programs execute the same
sequence of statements each time that they are run, although the program input and output can vary. Programs like
these have very limited use because they can process information but not act on the information. In other words,
they cannot use information that is provided to them to decide what to do. Making decisions about which statements
to execute is called conditional execution, because the program must decide whether or not some condition or
conditions exist before executing statements.
C provides two basic conditional execution statements. The first is the IF...ELSE statement. The second is the SWITCH
statement, which is a specialized form of the IF...ELSE statement.

7.2 The IF...ELSE Statement


The IF...ELSE statement has the following form:

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:

Enter  an  integer:  24  


The  number  24  is  even.  
End  of  program.  

37
Programming in C: A Tutorial
Suppose, however, that the number entered is 7. When the program is run the output will be:

Enter  an  integer:  7  


The  number  7  is  odd.  
End  of  program.  

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:

Enter  an  integer:  9  


Enter  another  integer:  5  
9  is  greater  than  5.  
End  of  program.  

Suppose now that number1 is 99 and number2 is 100. When the program is run the output will be:

Enter  an  integer:  99  


Enter  another  integer:  100  
99  is  less  than  100.  
End  of  program.  

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:

Enter  an  integer:  3  


The  number  is  non-­‐zero.  
The  incremented  value  is  4.  

If, however, the number entered is 0 program will output:

Enter  an  integer:  0  


The  incremented  value  is  1.  

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).

7.3 The SWITCH Statement


The IF...ELSE statement is good for when a program must choose between executing one of two statements.
Sometimes, however, a program must deal with several possible choices. In these cases the SWITCH statement is a
better choice. Whereas the IF...ELSE statement uses a Boolean value to choose one of two statements to execute, the
SWITCH statement uses an integer value to chooses between several possible statements to execute. The SWITCH
statement has the following form:

switch  (integer  value)  


   {  
       case  integer  value  1:  C  statement  1;  
                                                   break;  
       case  integer  value  2:  C  statement  2;  
                                                   break;  
       ...  
       case  integer  value  n:  C  statement  n;  
39
Programming in C: A Tutorial
                                                   break;  
       default:  default  C  statement;  
   }  

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.

It the letter entered is ‘z’, the program will output:

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.

7.4 More About C Statements


7.4.1 Nested Statements
In the discussion of the IF...ELSE statement you learned that the program will execute the statement in the IF portion
it the Boolean expression evaluates to TRUE and the statement in the ELSE portion if the Boolean expression
evaluates to FALSE. Similarly, for a SWITCH statement the program executes all the statements that are associated
with an active CASE condition until it encounters a BREAK statement. C allows these statements to be additional
IF...ELSE and SWITCH statements. Placing IF...ELSE statements in an IF...ELSE statement, or SWITCH statements in a
SWITCH statement is called nesting. The program below shows how a nested IF...ELSE statement can replace the
SWITCH statement in the program that identifies whether a letter is a vowel or consonant.

#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.

7.4.2 Block Statements


Although the SWITCH statement examples have used a single statement (plus the break statement) for each case item,
there is no theoretical limit to how many statements can be associated with each case. The IF...ELSE can also contain
multiple statements by combining them into what is called a block statement. A block statement groups multiple
statements by enclosing them in a pair or braces or curly brackets, ({}) . You have actually seen block statements
before, because all the statements between the curly brackets in the main() function actually make up one block
statement. If you attempt to create a block statement without using curly brackets the compiler will either generate
an error or assume that the first statement is associated with the IF or ELSE portion of the IF...ELSE statement and that
program is always to execute the additional statements.
The program below shows how an IF...ELSE statement can use block statements.

#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.

8.2 The FOR Statement


The FOR statement is used when the programmer knows how many times a loop must be repeated. The FOR
statement typically uses a variable, called a loop index, to keep track of how many times a loop has executed and
determine when to exit the loop. The FOR statement has the following form:

for  (start  condition;  loop  condition;  index  adjustment)  


   C  statement;  

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:

Enter  an  integer  (0  to  12):  8  


8!  =  40320  

If the number 13 is entered, the program will output the following:

Enter  an  integer  (0  to  12):  13  


Invalid  entry  ‘13’.  

8.3 The WHILE Statement


The WHILE statement is used when the programmer knows when a loop should execute, but does not know exactly
how many times it will execute. The WHILE statement has the following form:

while  (Boolean  expression)  


   C  statement;  

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.  

8.4 The DO...WHILE Statement


The DO...WHILE statement is similar to the WHILE statement, except that the DO...WHILE statement evaluates the
Boolean condition associated with the loop after executing the loop statement rather than before, so that loop will
always execute at least once. The DO...WHILE statement has the following form:

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. Derived and Defined Data Types


9.1 Overview
Elementary data types, which you studied in Chapter 2, are part of the C language definition. Elementary data types
like int and float are useful for working with data in many applications. As the example programs have shown
elementary data types each hold a single piece of information, such as the ASCII value of a keypress or the result of
a calculation. Sometimes, however, it is easier to group several pieces of data together. For example, an electronics
instructor must keep track of grades for every student in the class. While a program could have a separate variable
for each student, it would be more convenient if a single variable could keep track of the grade for every student.
Derived and defined data types allow programmers to organize and work more easily with data in programs. This
chapter will examine and demonstrate the use of these data types.

9.2 Derived Data Types


A derived data type is a data type that is based upon, or derived from, elementary data types, just as a brick wall
consists of multiple individual but identical bricks and a house consists of multiple and varied building components
like bricks, boards, shingles, drywall, and other building materials. C has two derived data types: arrays (which
consists of multiple identical data units, like the brick wall) and structures (which consists of multiple varied data
units, like the house). In technical terms, arrays are accumulations and structures are aggregations.

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

elementary_data_type  variable_name[number  of  elements]  

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:

int  int_array[5]  =  {0,  1,  2,  3,  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:

printf  (“The  first  character  in  message[]  is  %c\n”,  message[0]);  


 
The program below demonstrates how a 10-element integer array can be used as a look-up table of squares. The first
part of the program calculates and stores the values for 12 to 102 in the array, and the second part prints the square of
a number entered by the user.

#include  <stdio.h>   /*  Required  for  scanf()  and  printf()  functions.  */  


 
void  main  (void)  
   {  
       int  squares[10],  index;  
 
//  Load  the  look-­‐up  table.  
 
       printf  (“Initializing  table  values...”);  
       for  (index  =  0;  index  <  10;  index++)  
           {  
               squares[index]  =  (index  +  1)  *  (index  +  1);  
           }  
       printf  (“done!\n”);  
 
//  Use  the  look-­‐up  table  to  print  the  square  of  a  number.  
 
       while  (index  !=  0)   /*  index  is  initially  10  from  loading  the  table  */  
           {  
               printf  (“Enter  a  number  between  1  and  10  (0  to  exit):  ”);  
               scanf  (“%d”,  &index);  
               if  ((index  >  0)  &&  
                       (index  <  11))  
                   printf  (“The  square  of  %d  is  %d.\n”,  value,  squares[value  –  1]);  
           }  
       printf  (“Exiting  program.\n”);  
   }  

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:

Initializing  table  values...done!  


Enter  a  number  between  1  and  10  (0  to  exit):  20  
50
Section 9 - Defined and Derived Data Types
Enter  a  number  between  1  and  10  (0  to  exit):  5  
The  square  of  5  is  25.  
Enter  a  number  between  1  and  10  (0  to  exit):  3  
The  square  of  3  is  9.  
Enter  a  number  between  1  and  10  (0  to  exit):  –1  
Enter  a  number  between  1  and  10  (0  to  exit):  0  
Exiting  program.  

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.

9.2.2 The struct Data Type


The struct (structure) data type is similar to the array data type, but the elements of a structure, called fields, can be
different elementary data types. The form of a struct data type is:

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  impedance;  

The second way to declare a structure is:

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.

#include  <stdio.h>   /*  Required  for  scanf()  and  printf()  functions.  */  


 
void  main  (void)  
   {  
       struct  complex  
           {  
               int  real  =  0;  
               int  imag  =  0;  
           };  
 
       struct  complex  number1,  number2,  product;  
 
//  Input  the  complex  number  to  be  multiplied  
 
       printf  (“Enter  the  real  and  imaginary  parts  of  Number  1:  ”);  
       scanf  (“%d  %d”,  &(number1.real),  &(number1.imag));  
       printf  (“Enter  the  real  and  imaginary  parts  of  Number  2:  ”);  
       scanf  (“%d  %d”,  &(number2.real),  &(number2.imag));  
         
/*  
     Multiply  the  numbers.  For  imaginary  numbers  (R1  +  jI1)  and  (R2  +  jI2)  

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;  
   };  

9.3 Defined Data Types


A defined data type is one that the programmer creates and that are based on underlying elementary data types. Just
as with derived data types, defined data types exist to simplify working with specific types of data. One defined data
type is the enum (for enumeration) data type. Other defined data types are created using typedef (for type definition)
statements.

9.3.1 The enum Data Type


The enum data type is a special variation of the int data type. The enum data type associates a finite set of integer
values with special identifiers called enumeration constants. An example that illustrates the concept of the enum data
type are a list of numbered items on a menu. The numbers of the items corresponds to the integers underlying an
enumerated variable, whereas the item descriptions correspond to the enumeration constants. An enum declaration
has the following form:

enum  enum_variable  {enum_constant_1,  enum_constant_2,  ...,  enum_constant_n};  

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:

enum  rainbow  {red,  orange,  yellow,  green,  blue,  indigo,  violet};                        

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:

enum  components  {resistor,  capacitor,  inductor,  diode,  transistor};  


 
enum  components  part_list;  
 
The second method to declare the enum variable is:

enum  components  part_list  {resistor,  capacitor,  inductor,  diode,  transistor};  

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:

enum  components  part_list  


   {  
       resistor,  
       capacitor  =  4,  
       inductor,  
       diode  =  5,  
       transistor  =  0  
   };  

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:

resistor = 0, capacitor = 4, inductor = 5, diode = 5, transistor = 0

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.

#include  <stdio.h>   /*  Required  for  printf()  functions.  */  


 
void  main  (void)  
   {  
       enum  days  
           {  
               Monday  =  0,  
               Tuesday,  
               Wednesday,  
               Thursday,  
               Friday,  
               Saturday,  
               Sunday  
           };  
 
       enum  days  today  =  Monday;  
 
       enum  Boolean  
           {  
               FALSE  =  0,  
               TRUE  
           };        
 
55
Programming in C: A Tutorial
       enum  Boolean  quit  =  FALSE;      
 
       while  (quit  !=  TRUE)  
           {  
               switch  (today)  
                   {  
                       case  Sunday  :  printf  (“But  the  child  that’s  born  on  the  Sabbath  day\n”);  
                                                   printf  (“Is  bonnie  and  blithe  and  good  and  gay.\n”);  
                                                   quit  =  TRUE;  
                                                   break;  
                       case  Friday  :  printf  (“Friday’s  child  is  loving  and  giving,\n”);  
                                                   today  =  Saturday;  
                                                   break;  
                       case  Tuesday  :  printf  (“Tuesday’s  child  is  full  of  grace,\n”);  
                                                     today  =  Wednesday;  
                                                     break;  
                       case  Thursday  :  printf  (“Thursday’s  child  has  far  to  go,\n”);  
                                                       today  =  Friday;  
                                                       break;  
                       case  Saturday  :  printf  (“Saturday’s  child  works  hard  for  a  living,\n”);  
                                                       today  =  Sunday;  
                                                       break;  
                       case  Wednesday  :  printf  (“Wednesday’s  child  is  full  of  woe,\n”);  
                                                         today  =  Thursday;  
                                                         break;  
                       case  Monday  :  printf  (“Monday’s  child  is  fair  of  face,\n”);  
                                                   today  =  Tuesday;  
                                                   break;  
                   }  
           }  
       printf  (“\nEnd  of  program.\n”);  
   }  

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:

Monday’s  child  is  fair  of  face,  


Tuesday’s  child  is  full  of  grace,  
Wednesday  child  of  full  of  woe,  
Thursday’s  child  has  far  to  go,  
Friday’s  child  is  loving  and  giving,  
Saturday’s  child  works  hard  for  a  living,  
56
Section 9 - Defined and Derived Data Types
But  the  child  that’s  born  on  the  Sabbath  day  
Is  bonnie  and  blithe  and  good  and  gay.  
 
End  of  program.  

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:

#include  <stdio.h>   /*  Required  for  printf()  functions.  */  


 
void  main  (void)  
   {  
       enum  GrayCode  
           {  
               G0,  
               G1,  
               G2,  
               G3  
           };  
 
       enum  GrayCode  state  =  G0;  
 
       enum  Boolean  
           {  
               FALSE  =  0,  
               TRUE  
           };  
 
       enum  Boolean  quit  =  FALSE;  
 
       printf  (“Entering  machine\n”);  
       while  (quit  !=  TRUE)  
           {  
               printf  (“Exeecuting  state  machine\n”);  
               switch  (state)  
                   {  
                       case  G0  :  printf  (“In  machine  state  0.\n”);  
                                           state  =  G1;  
                                           break;  
                       case  G1  :  printf  (“In  machine  state  1.\n”);  
                                           state  =  G3;  
                                           break;  
                       case  G2  :  printf  (“In  machine  state  2.\n”);  
                                           quit  =  TRUE;  
                                           break;  
                       case  G3  :  printf  (“In  machine  state  3.\n”);  
57
Programming in C: A Tutorial
                                           state  =  G2;  
                                           break;  
                   }  
           }  
       printf  (“\nEnd  of  program.\n”);  
   }  

When this program is run its output will be:

Entering  state  machine.  


Executing  state  machine.  
In  machine  state  0.  
Executing  state  machine.  
In  machine  state  1.  
Executing  state  machine.  
In  machine  state  3.  
Executing  state  machine.  
In  machine  state  2.  
 
End  of  program.  

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.

9.3.2 User-Defined Types


The enum data type allows programmers to define a set of values that variables can use and that helps make
programs easier to understand. For example, the enum  Boolean allowed the program to use the enumeration
constants FALSE and TRUE to implement Boolean variables, rather than the integer values 0 or 1.  This makes the
relational expression

while  (quit  !=  TRUE)  

that tests the value of the variable and its intent easier to understand than the integer expression

while  (quit  !=  1)  

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:

typedef  data_type  data_type_name;  

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

typedef  unsigned  long  int  uint32;  

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:

typedef  my_int  unsigned  int;  

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.

10.2 Strings as Character Arrays


From the discussion of arrays in the previous chapter programs can initialize arrays by specifying the value of each
element. For example, the program can initialize a character array called hello[12] with the string “Hello,  world” with
the statement:

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.

#include  <stdio.h>   /*  Required  for  printf()  function.  */  


 
void  main  (void)  
   {  
   char  hello[13]  =  "Hello,  world";  
 
//  Print  the  string  
 
   printf  ("%s\n",  hello);  
   }  
 
When this program it run it will output the following:

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.

#include  <stdio.h>  /*  Required  for  printf()  functions.  */  


 
void  main  (void)  
   {  
61
Programming in C: A Tutorial
       char  buffer[80]  =  "Hello,  world",  
     value;  
       int  index;  
 
//  Print  the  string  
 
       for  (index  =  0;  index  <  80;  index++)  
           {  
               value  =  buffer[index];  
               if  (value  !=  0)  
                   printf  ("buffer[%2d]  =  \t  %3d  =  \t  '%c'\n",  index,  value,  value);  
               else  
                   {  
                       printf  ("buffer[%2d]  =  \t  %3d  =  \t  NULL\n",  index,  value);  
 
//  If  end  of  string  reached,  terminate  the  loop  by  setting  the  index  to  80.  
                       index  =  80;  
                   }  
           }  
   }  
 
This program loops through the 80-element character array containing “Hello,  world” until it reaches the last element
in the array or the value 0, whichever occurs first. If it reaches the value 0 before the last element in the array it
terminates the loop by setting the loop index to 80. For each loop the program uses the printf() function to specify
the buffer element and the element value, both as an integer and as an ASCII character, using the tab character \t to
format the values in columns. If the value in the element in 0, which is not printable, it prints NULL. When the
program is run it outputs the following:

buffer[  0]  =    72  =   'H'  


buffer[  1]  =   101  =   'e'  
buffer[  2]  =   108  =   'l'  
buffer[  3]  =   108  =   'l'  
buffer[  4]  =   111  =   'o'  
buffer[  5]  =    44  =   ','  
buffer[  6]  =    32  =   '  '  
buffer[  7]  =   119  =   'w'  
buffer[  8]  =   111  =   'o'  
buffer[  9]  =   114  =   'r'  
buffer[10]  =   108  =   'l'  
buffer[11]  =   100  =   'd'  
buffer[12]  =        0  =   NULL  

As the program shows, C has added a 0 to the string “Hello,  world”.


The second thing to note is that the printf() function uses the %s to specify a string variable type. The printf()
function requires a null-terminated ASCIIZ string to properly print the string. Otherwise, it will continue to print
characters in memory that follow the variable until it encounters a 0 (which may take some time).

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:

Hello!  What  is  your  first  name?  John  


Hello,  John.  Nice  to  meet  you.  What  is  your  last  name?  Doe  
I  hope  that  the  Doe  family  is  well,  John.  

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
   }  

When this program is run, it will output the following:

Hello!  What  is  your  first  name?  John  


Hello,  John.  Nice  to  meet  you.  What  is  your  last  name?  Doe  
I  hope  that  the  Doe  family  is  well,  John.  

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.

10.3.1 String Functions in C


Because C uses addresses to refer to strings and arrays, programs cannot work with these types of variables as it
does with other data types. Consider the following program:

#include  <stdio.h>   /*  Required  for  printf()  function.  */  


 
void  main  (void)  
   {  
   char  buffer[80]  =  "This  is  a  string.";  
 
   if  (buffer  ==  "This  is  a  string.")  
       printf  ("The  strings  match.\n");  
   else  
       printf  ("The  strings  do  not  match.\n");  
   }  

When the program is run, it will output the following:

The  strings  do  not  match.  

There is no trick to this. Although the contents of buffer match the string, the program evaluates the Boolean
expression

buffer  ==  “This  is  a  string.”  

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:

#include  <stdio.h>   /*  Required  for  printf()  function.  */  


 
void  main  (void)  
   {  

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);  
   }  

When the program is run, it will output the following:

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.

#include  <stdio.h>  /*  Required  for  printf()  function.  */  


 
void  main  (void)  
   {  
       char  string1[80]  =  "This  is  a  string.",  
                 string2[80]  =  "This  string  is  different.",  
                 string3[80]  =  "This  is  a  string.";  
       int  index,  
               same;  
 
/*  
     Use  a  loop  to  compare  the  characters  of  string1  and  string2,  incrementing  the  index  
     with  each  loop.  End  the  loop  when  two  characters  are  not  the  same,  the  last  valid  
     element  is  reached,  or  the  null  terminator  of  either  string  is  reached.  

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:

#include  <stdio.h>          /*  Required  for  printf()  function.  */  


#include  <string.h>        /*  Required  for  the  strcpy()  function.  */  
 
void  main  (void)  
   {  
   char  string1[80]  =  "This  is  a  string.",  
                 string2[80]  =  "This  string  is  different";  
 
   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  the  strcpy()  function  to  copy  string1  to  string2.  
*/  
 
   strcpy  (string2,  string1);  

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:

#include  <stdio.h>  /*  Required  for  printf()  function.  */  


#include  <string.h>    /*  Required  for  strcpy()  function.  */  
 
void  main  (void)  
   {  
       char  string1[80]  =  "This  is  a  string.",  
                 string2[80]  =  "This  string  is  different.",  
                 string3[80]  =  "This  is  a  string.";  
       int  result;  
 
       printf  ("Comparing  '%s'  with  '%s'\n",  string1,  string2);  
       result  =  strcmp(string1,  string2);  
       if  (result  ==  0)  
           printf  ("'%s'  is  the  same  as  '%s'\n",  string1,  string2);  
       else  
           printf  ("'%s'  is  not  the  same  as  '%s'\n\n",  string1,  string2);  
       printf  ("\nComparing  '%s'  with  '%s'\n",  string1,  string3);  
       result  =  strcmp(string1,  string3);  
       if  (result  ==  0)  
           printf  ("'%s'  is  the  same  as  '%s'\n",  string1,  string3);  
       else  
           printf  ("'%s'  is  not  the  same  as  '%s'\n\n",  string1,  string3);  

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.

11.2 The Nature of Pointers


Data types like int and struct contain data with which programs work. Whenever a program declares a variable it
allocates, or sets aside, memory to hold data. For example, the variable declaration

int  int_val1,  int_val2;  

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.

11.3 Declaring Pointers


Declaring a pointer in C is similar to declaring the data type referenced by the pointer. The difference is that the
pointer is prefixed by an asterisk (*). The example below shows the variable declarations for a double and pointer to
double.

double  double_val;   /*  double_val  is  a  double  data  type  */  


double  *double_ptr;   /*  double_ptr  is  a  pointer  to  double  data  type  */  

Note that these two variables could also have been declared as follows:

double  double_val,  *double_ptr;  

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.

11.4.1 Direct Address Assignment


The first method for initializing a pointer is simply to load the pointer variable with a four-byte memory address, as
shown in the statements below:

float  *float_ptr;   /*  Declare  the  pointer  to  float  variable  */  


float_ptr  =  0x10L;   /*  Initialize  int_ptr  with  the  address  0x00000010  */

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.

11.4.2 Assigning a Variable Address


The second method for initializing a pointer is to assign the address of a declared variable to it. The statements
below shows how this can be done.

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:

#include  <stdio.h>   /*  Required  for  the  printf()  function  */  


 
void  main  (void)  
   {  
       int  int_array[10],  
               index,  
               *int_ptr;  
 
//  Load  the  array  with  the  square  of  the  element  number.  
 
       for  (index  =  0;  index  <  10;  index++)  
           {  
               int_array[index]  =  index  *  index;  
           }  
70
Section 11 - Pointers
 
//  Use  a  pointer  to  print  out  the  contents  of  the  array.  
 
       int_ptr  =  int_array;   /*  Recall  that  the  name  of  an  array  is  an  address.  */  
       while  (int_ptr  <=  &(int_array[9]))  
           {  
               printf  (“%d\n”,  *int_ptr++);  
           }  
       printf  (“End  of  program.\n”);  
   }  
 
The first part of the program is fairly straightforward. The for loop initializes each element of the array with the
square of the array index (i.e., int_array[0] = 02, int_array[1] = 12, ... int_array[9] = 92). The second part is a little more
cryptic, but actually not very difficult to understand. The pointer is initialized with the address of the array, which is
the same as &(int_array[0]), or the address of the first array element. Each iteration of the while loop then prints out
the contents of the array by accessing the int value that is indicated by the pointer and then post-incrementing the
pointer to access the next array element. The program will continue to loop until the pointer exceeds &(int_array[9]),
the address of the last element in the array. When this program is run, it will output the following:

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.

#include  <stdio.h>   /*  Required  for  printf()  function.  */  


 
void  main  (void)  
   {  
       char  buffer[80]  =  “This  is  a  string.”,  
                 *char_ptr;  
 
       char_ptr  =  buffer;   /*  Set  pointer  to  start  of  string.  */  
       printf  (“Original  string  =  %s\n”,  buffer);  
       while  (*char_ptr++  !=  0)  
71
Programming in C: A Tutorial
           {  
               if  ((*char_ptr  >=  ‘a’)  &&  
                       (*char_ptr  <=  ‘z’))  
                   *char_ptr  =  *char_ptr  +  (‘A’  –  ‘a’);  
           }  
       printf  (“New  string  =  %s\n”,  buffer);  
   }  

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:

Original  string  =  This  is  a  string.  


New  string  =  THIS  IS  A  STRING.  

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.

11.4.3 Allocating Memory


The third method of initializing pointers uses memory allocation, in which the program requests the operating
system to allocate a specific amount of memory for the program to use. The program and operating system
automatically allocate memory each time the program declares a variable, but C allows programs to allocate
memory if the program requires it. The function typically used to do so is the malloc() (for “memory allocation”)
function. The form of using the malloc() function is as follows:

mem_ptr  =  malloc  (number_of_bytes);  

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.

#include  <stdio.h>   /*  Required  for  printf()  and  scanf()  functions.  */  


#include  <stdlib.h>   /*  Required  for  malloc()  function.  */  
 
void  main  (void)  
   {  
       char  *buffer;   /*  Declare  pointer  to  character  buffer.  */  
 
       buffer  =  malloc  (80);   /*  Request  80  bytes  of  memory  for  buffer.  */  
       if  (buffer  !=  NULL)  
           {  

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:

What  is  your  name?  Aubrey  


Hello,  Aubrey!  
End  of  program.  

If the program was unable to obtain the requested memory when it is run, the output will be as follows:

Unable  to  allocate  memory.  


End  of  program.  

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:

num_bytes  =  sizeof  (data_type);  

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:

type_ptr  =  malloc  (sizeof  (data_type));

The following program shows the program above modified to use the sizeof() function to allocate memory for 80
char values:

#include  <stdio.h>   /*  Required  for  printf()  and  scanf()  functions.  */  


73
Programming in C: A Tutorial
#include  <stdlib.h>   /*  Required  for  sizeof()  and  malloc()  functions.  */  
 
void  main  (void)  
   {  
       char  *buffer;   /*  Declare  pointer  to  character  buffer.  */  
 
       buffer  =  malloc  (80  *  sizeof  (char));  /*  Request  memory  for  80  char  values.  */  
       if  (buffer  !=  NULL)  
           {  
               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”);  
   }  

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.

11.4.4 Looking Ahead


Pointers are useful for some types of data processing, but their real strength lies in allowing sections of a program to
efficiently access data from other sections of the program. These sections are called functions, which allows
programs to increase cohesion (keep related code and data together) and reduce coupling (the effect of changes in
one section of a program on another). The next chapter will examine functions in C programming.

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.

12.2 Why Use Functions?


Before writing a function, programmers should understand why programs use functions so that they can write
functions effectively. Functions in C serve three major purposes.
1) Functions help to design, organize, and programs. This allows programmers to write, analyze, modify,
and maintain programs more easily. A difficulty that programmers often face is understanding just how to
write a program that must perform some very complex task. Functions allow programmers to break down
complex programs into smaller and more manageable tasks that are easier to understand, implement, and
modify.
2) Functions allow programmers to write, test, and integrate sections of code independently. This is
particularly useful for programmers working on large programming projects and for programs that require
more than one programmer. A programmer working independently can use functions to write and debug a
large program in stages, whereas a project manager can assign sections of a large project to members of his
team so that they can work in parallel and shorten the time needed to deliver the program.
3) Functions support modular code and simplify re-use of existing code. Programs often must perform the
same task or repeat the same many times, such as getting input from the user, performing some calculation,
or writing data to a file. Functions allow programmers to do this either by accessing a function in a pre-
existing library (such as printf()) or by writing the code for some task once and simply calling the function
each time the program requires it.
Functions are a powerful feature of C when used properly, but actually can be counter-productive when used
improperly. For example, a function that performs complex addition in a program that calculates circuit impedance
is a good application for a function, because the program likely must perform complex addition many times and
other mathematical programs would likely re-use a complex addition function. Writing a function that prints the
string “Hi” probably is not a good application for a function, because this function would have limited use and not
justify the effort of writing it. Understanding the purposes for which functions are intended will help to decide when
to use functions in programs.

12.3 Declaring Functions


12.3.1 Form of Functions
All functions in C have the following form:

return_type  function_name  (parameter  list)  


   {  
       local  variables;  
 
       function  body;  
       return  return_value;  
   }  

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;  

for an int return value or

return  ‘c’;  

for a char return value. Functions can even return values by specifying an expression such as

return  num1  +  num2;  

The function will evaluate the expression and return that value to the calling statement.

12.3.2 Some Examples of Functions


The following program illustrates a function that programs might use to report the severity of an error that occurs
during program operation.

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;  

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:

typedef  enum  error_type  {NO_ERR  =  0,  MINOR_ERR,  MODERATE_ERR,  SEVERE_ERR);  

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:

There  is  a  minor  error.  

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:

void  Boolean_msg  (enum  Boolean  bool_val)  


   {  
       switch  (bool_val)  
           {  
                 case  FALSE  :  printf  ("FALSE\n");  
                                                           break;  
                 case  TRUE    :  printf  ("TRUE");  
                                                           break;  
           }  
   }  

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.

float  cube  (float  base)  


   {  
       float  result  =  0;  
         
       result  =  base  *  base  *  base;  
       return  result;  
   }  

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.

float  Pythagoras  (float  x_side,  float  y_side)  


   {  
       float  hypotenuse  =  0;  
         
       hypotenuse  =  sqrt  (x_side  *  x_side  +  y_side  *  y_side);  
       return  hypotenuse;  
   }  

12.3.3 Function Prototypes


As previous program chapters have shown, the C compiler must know what values are passed to and returned from a
function so that it can generate the correct program code. This information is provided using a function prototype.
The function prototype of C functions are provided in header files such that are specified at the start of the program
(e.g., the prototype for scanf() is contained in the header file stdio.h). For program functions programs can contain
prototype statements at the start of the program. The exact location of the prototype statement is not important
provided the statement appears before any statement that attempts to call the function, but placing them at the start
of the program makes analyzing the program much easier.
Prototype statements have the form:

return_type  function_name  (parameter  data  types);  

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:

void  error_msg  (enum  error_type);  


 
void  Boolean_msg  (enum  Boolean);  
 
float  cube  (float);  
 
float  Pythagoras  (float,  float);

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:

return_variable  =  function_name  (values  list);  

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:

side3  =  Pythagoras  (side1,  side2)  


if  (side3  >  5.0)  
   {  
       printf  (“Hypotenuse  %f  is  greater  than  5.0\n”,  side3);  
   }  

is functionally identical to

if  (Pythagoras  (side1,  side2)  >  5.0)  


   {  
       printf  (“Hypotenuse  %f  is  greater  than  5.0\n”,  side3);  
   }  

The return_variable is always omitted if the return value of function_name is void.


The function_name specifies the name of the function to execute.
The values  list provides the parameters for the statement to pass to the function being called. The sequence and data
types of the values  list must match those of the parameter list of the function being called.
The following examples demonstrate programs that call the function examples in Section 12.3.2. The first program
below exercises the error_msg() function.

#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:

There  is  no  error.  


There  is  a  minor  error.  
There  is  a  moderate  error.  
There  is  a  severe  error.  
 
End  of  program.  

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;  
           }  
   }  

When this program is run the output will be:

The  logic  state  is  FALSE.  


The  logic  state  is  TRUE.  

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

Enter  a  number:  1.2  


The  cube  of    1.200000  =  1.728000  
 
End  of  program.

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:

Enter  the  length  of  side  1:  1.2  


Enter  the  length  of  side  2:  0.5  
The  hypotenuse  of  1.200000  and  0.500000  is  1.300000  
 
End  of  program.  

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:

#include  <stdio.h>    /*  Required  for  printf()  function.  */  


 
void  doomed_to_fail  (int,  int);  /*  Prototype  for  doomed_to_fail()  function.  */  
 
void  main  (void)  
   {  
       int  num1  =  3,  
               num2  =  4;  
 
       printf  (“num1  =  %d,  num2  =  %d\n”,  num1,  num2);    
       doomed_to_fail  (num1,  num2);  
       printf  (“num1  =  %d,  num2  =  %d\n”,  num1,  num2);    
   }  
 
void  doomed_to_fail  (int  number1,  int  number2)  
   {  
       printf  (“number1  =  %d,  number2  =  %d\n”,  number1,  number2);    
       printf  (“Squaring  numbers...\n”);  
       number1  *=  number1;  
       number2  *=  number2;  
       printf  (“number1  =  %d,  number2  =  %d\n”,  number1,  number2);    
   }  

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:

num1  =  3,  num2  =  4  


number1  =  3,  number2  =  4  
Squaring  numbers...  
number1  =  9,  number2  =  16  
num1  =  3,  num2  =  4  

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.

#include  <stdio.h>    /*  Required  for  printf()  function.  */  


 
void  this_works  (int  *,  int  *);  /*  Prototype  for  this_works()  function.  */  
 
void  main  (void)  
   {  
       int  num1  =  3,  
               num2  =  4;  
 
       printf  (“num1  =  %d,  num2  =  %d\n”,  num1,  num2);    
       this  works  (&num1,  &num2);  
       printf  (“num1  =  %d,  num2  =  %d\n”,  num1,  num2);    
   }  
 
void  this_works  (int  *number1,  int  *number2)  
   {  
       printf  (“number1  =  %d,  number2  =  %d\n”,  *number1,  *number2);    
       printf  (“Squaring  numbers...\n”);  
       *number1  *=  *number1;  
       *number2  *=  *number2;  
       printf  (“number1  =  %d,  number2  =  %d\n”,  *number1,  *number2);    
   }  
 
Now when the program is run the addresses of the variables num1 and num2 are passed to the function. The program
creates copies of these addresses and loads them into the int  * variables number1 and number2, which the
this_works() function uses to access and modify the contents of *number1 (or num1) and *number2 (or num2). When
this program is run the output will be:

num1  =  3,  num2  =  4  


number1  =  3,  number2  =  4  
Squaring  numbers...  
number1  =  9,  number2  =  16  
num1  =  9,  num2  =  16  

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.

#include  <stdio.h>    /*  Required  for  printf()  function.  */  


 
typedef  struct  
   {  
84
Section 12 - Functions in C
       float  magnitude;  
       float  angle;  
   }  polar_type;  
 
void  show_polar  (polar_type);                              /*  Prototype  for  show_polar()  function.  */  
void  polar_cpy  (polar_type  *,  polar_type  *);  /*  Prototype  for  polar_cpy()  function.  */  
 
void  main  (void)  
   {  
       polar_type  vector1,  vector2;  
 
       vector1.magnitude  =  5.0;  
       vector1.angle  =  0.927295;  
       show_polar  (vector1);  
       vector2.magnitude  =  0.0;  
       vector2.angle  =  0.0;  
       show_polar  (vector2);  
       polar_cpy  (&vector1,  &vector2);  
       show_polar  (vector1);  
       show_polar  (vector2);  
   }  
 
void  show_polar  (polar_type  polar)  
   {  
       printf  ("Magnitude  =  %f,  Angle  =  %f\n",  polar.magnitude,  
                                                                                       polar.angle);  
   }  
 
void  polar_cpy  (polar_type  *polar1,  polar_type  *polar2)  
   {  
       (*polar2).magnitude  =  (*polar1).magnitude;  
       (*polar2).angle  =  (*polar1).angle;  
   }  

When this program is run the output will be:

Magnitude  =  5.000000,  Angle  =  0.927295  


Magnitude  =  0.000000,  Angle  =  0.000000  
Magnitude  =  5.000000,  Angle  =  0.927295  
Magnitude  =  5.000000,  Angle  =  0.927295  
Magnitude  =  5.000000,  Angle  =  0.927295  
 
Because programs use pointers to structures fairly often C provides an alternate method to use a pointer to access the
fields of a structure. In place of

(*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.

void  polar_cpy  (polar_type  *polar1,  polar_type  *polar2)  


   {  
       polar2-­‐>magnitude  =  polar1-­‐>magnitude;  
       polar2-­‐>angle  =  polar1-­‐>angle;  
   }  

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.

Good luck and enjoy the journey!

87

You might also like