Cts C

Download as rtf, pdf, or txt
Download as rtf, pdf, or txt
You are on page 1of 160

The C Language and its Advantages

Lesson Objectives
In this lesson, you will:

· Explain when and where C was developed, and by whom.

· List some of the advantages of C.


The C Language and its Advantages

C is a general-purpose, mid-level programming language that combines the conveniences of


high-level languages, such as Ada, BASIC, COBOL, FORTRAN, and Pascal, with the
functionality of low-level languages, such as Assembler.
Like low-level languages, C provides capabilities for working at the machine level, allowing
direct manipulation of bits and bytes, and providing close control of hardware and peripherals.
Like high-level languages, C has a syntax that makes it easier to write and maintain computer
programs than in low-level languages. Other conveniences, like built-in data-types to facilitate
the manipulation of data, lend C the feel of a high-level language.
The Beginning of C

C originated in the early 1970s at Bell Laboratories. Created by Dennis Ritchie, C emerged in
conjunction with the development of UNIX. For many years, the de facto standard was the
version of C supplied with the UNIX Version 5 operating system. This version is described in
The C Programming Language, written by Brian Kernighan and Dennis Ritchie.
With the popularity of microcomputers, new implementations of C began to appear. To maintain
compatibility and consistency between different versions, the American National Standards
Institute, or (ANSI) established a committee in 1983 to create a standard and define the language.
The ANSI C version was completed in 1987. Today, most C compilers have implemented the
ANSI standard, which has become the official version of the language.
C is a Structured Language

Like the more modern high-level languages such as Ada and Pascal, C is a structured language.
In structured languages, code and data can be easily compartmentalized into modules. In C, these
modules are called functions.
Functions provide distinct advantages:

· Parts of a program can be separated from one another so that event in one module do not
cause side effects in others.
· With independent modules, programmers can more easily work in teams. Each
programmer needs to know what each other's function is and how to pass data to it.

· Large, complex applications can be more easily built. Individual functions can be created,
compiled, and tested independently.

· Code is more readable and more easily conceptualized when it is divided into discrete
units that each performs a specific task.
In addition to functions, C programs can be further modularized through the creation of
groupings of statements, or code blocks, within the body of a function. Code blocks are logical
collections of C statements that are treated as a unit and are executed together. The body of a
function is itself a code block.
The Standard Template Library

The ability to create independent operating functions enables all C programmers to create and
share sections of code. Many procedures that are built into other languages, such as input/output
commands and string-handling routines, are accomplished in C through shared functions. This
allows C to be less defined and therefore offer greater flexibility.
Shared functions that provide specialized tasks for general use are often referred to as tools.
Tools are collected in programmer libraries, or toolkits. Additional functions, which are often
more system-dependent, are usually added.
Other Advantages of C

In addition to the benefits of a structured language, C offers other unique advantages:

· C contains a minimum of keywords: only 32 in the ANSI standard version. (In contrast,
BASIC for the IBM PC contains 159). The result is a language that isn't as strictly
defined, placing fewer restrictions on the programmer.

· C programs are portable. Unlike assembly languages, C isn't tied to the hardware it runs
on and, unlike high-level languages, it isn't as strictly defined. Coupled with the
standardization provided by ANSI, the result is a language whose programs can be ported
easily from one platform to another.

· C has a powerful and varied array of operators. Many operators, as well as other unique
language constructs, enable abbreviated coding for the creation of compact, efficient
code.

· The better C compilers can generate machine code that executes extremely fast, rivaling
that of lower-level assembler code.
The Philosophy of C
Created, influenced, and tested by working programmers, C, by design, is a language for
programmers. With its low-level functionality, high-level convenience, portability, structured
approach, and few restrictions, C was created to provide a flexible, practical, and powerful
development environment for the professional programmer.
1. When, where, and by whom was C developed?
C was developed in the early 1970s by Dennis Ritchie at Bell Laboratories.
2. What are some of the advantages of C?
C is a structured language, it contains a minimum of keywords, C programs are portable, C has a
powerful and varied array of operators, and the better C compilers can generate machine code
that execute extremely fast.
The Structure of a C Program
Lesson Objectives
In this lesson, you will:

· Explain the difference between a statement and an expression.

· Define a preprocessor directive.


Basic C Elements

The elements of a program in any language are the fixed, or defined, elements of the language
itself combined with user-defined elements. In C, the fixed elements are its 32 keywords, its
operators, and a few symbols for governing syntax.
The most basic user-defined elements are constants and variables:

· Constants are fixed data values that you embed directly into your C source code.
Constants are often called "hard data," "hard-coded data," or "magic numbers," because
they're defined prior to compilation and cannot be altered by a program as it executes.

· In contrast to constants (which define actual data), variables are definitions of memory
locations that will hold data. Variables enable you to reserve a space in computer memory
that a program can use to store different values as it executes.
Statements

Both fixed and user-defined elements are like the nouns, verbs, adjectives, and adverbs of a
spoken language. They are combined to build the equivalent of sentences, which in C are called
statements. Statements are instructions to the compiler to create machine-readable code to
perform a specific action. Like a period at the end of a sentence, a semi-colon is used to signify
the end of a C statement.
Many C statements contain expressions. Expressions are like phrases within a sentence. They are
the combination of constants, variables, and operators that together are evaluated to yield a value
that is used within the larger C statement.
Code Blocks and Functions

Just as sentences can be grouped together to form paragraphs, C statements can be combined into
compound statements, or code blocks. Code blocks are groups of statements that are logically
related and treated as a unit. Braces, {}, are used to signify the beginning and ending of code
blocks.
The most common code block is the body of a function. A function, like a chapter in a book,
divides a program into modules that perform a specific task. Unlike chapters in a book, C
functions are independent of one another and can be processed in any order.
Every C program must have at least one function called main(). The function main() is the
location the operating system passes control to when the program is run.
Preprocessor Directives

In addition to C statements, a C program can contain instructions to the compiler called


preprocessor directives or compiler directives. Although not part of the C language, preprocessor
directives expand the C programming environment by providing a mini-language to
communicate with the compiler itself. Preprocessor directives are commonly placed at the
beginning of a source file and are preceded by a pound sign, #.

External Functions and Header Files

A common use of preprocessing directives is in conjunction with external functions. Most of the
C programs that you will write will consist of calls to functions outside your source file, or
external functions. Most external functions you will use are stored in libraries and are called
library functions.
Although library functions don't need to be added to your source code, information about them
does. This information is provided in text files called header files, which are created by the writer
of the function, usually the compiler manufacturer. By convention, header files have the
extension of .h .
Header files can be merged with your program automatically by the compiler through the use of
a preprocessor directive called an include directive. For this reason, header files are also referred
to as include files.
Because C contains no built-in input/output commands, the most common header file that you
will include in your program is stdio.h . The stdio.h file contains information necessary to use the
standard library's input/output functions.
One of the most common input/output functions you will use in the standard library is
printf(). The printf() function enables your program to perform formatted output.

Function Calls

In order to use a function such as printf(), your program must transfer control to the
function. This transfer of control from one function to another is accomplished through function
calls. A function call consists of the name of the called function followed by, in parenthesis, any
special information the function needs to perform its task.
For example, the following C statement transfers control to the function printf():

printf("Welcome to C programming");

Comments

To provide explanations of what various parts of your program are designed to do, you can place
comments throughout the code. Programmer comments can be placed almost anywhere in a
program as long as they're delimited by the following character pairings to the right and to the
left.

/* comment */
The following code illustrates a typical, albeit short, C program.

/*
welcome.c - This program prints a welcome
message to the screen, and uses
a variable to display the length of
this course in days.
*/

#include <stdio.h>

main()
{
int num;

num = 5;
printf("\nWelcome to the C course.");
printf("\nThis course is %d days long.", num);
}

1. What is the difference between a statement and an expression?


A statement is an instruction to the compiler to create machine readable code. They are the C
equivalent of a complete sentence. Expressions are like phrases within a sentence. Statements are
made up of one or more expressions.
An i2. What is a preprocessor directive?
nstruction to the compiler, entered in a program with a # (pound sign) preceding the directive.
Writing C Programs
Lesson Objectives
In this lesson, you will:

· List the syntax rules for C


Editing

Writing a C program is often referred to as editing the program because it involves typing at a
computer keyboard. Editing is the process of creating, or modifying, a human-readable form of
the program, called a source file, by using a text editor. A text editor is a simple word processor
that creates straight-text files, or files consisting of only ASCII characters, which all the compiler
can understand. Most compilers come with a text editor. Although a full-featured word processor
can also be used to create a source file, the file must be saved as a text-only file to avoid the
inclusion of embedded format-control characters that are unintelligible to the compiler.
C language source files must have the extension .c because the C compiler accepts only files that
end with a .c as valid C language source files.
Matters of Syntax

In C, certain rules of syntax apply:

· C is case-sensitive.

· All keywords are lowercase.

· Keyword names are reserved and cannot be used for any other purpose.

· Whitespace (spaces, tabs, and new lines) is ignored.


When you're writing C programs, common practice is to type program statements in lowercase,
for ease of typing.
In addition, due to the rich variety of operators, most special characters have defined meaning to
C and should be avoided when you're creating user-defined elements. An exception to this rule is
the underscore character, _ . An underscore is often used to give clarity to variable and function
names by placing it between words within the name. For example, the variable name
this_is_a_constant is much more comprehensible than thisisaconstant.

Comments
Programmer comments can be placed almost anywhere in a program, provided that they're:

· delimited by the backslash/asterisk character combination as follows:

/* comment
*/

· not placed in the middle of a keyword or identifier, and

· not nested.
All but the most obvious functions should include explanations at the top of the function
describing what the function does, how it is called, and what it returns. Comments within the
middle of C statements should be avoided because they inhibit the readability of the program.
Matters of Style

The readability of your source code is an important part of coding. The amount and placement of
whitespace between program elements is overlooked by the compiler. In contrast to most of the
other languages, the C language allows you to place an entire program on a single line. The
simple C program:

#include <stdio.h>

main()
{
printf("Welcome to C\n");
}
could also be written:

#include <stdio.h>

main(){printf("Welcome to C\n");}
Expanding code vertically into its logical parts, aligning matching braces, and indenting nested
blocks of code are standard techniques for increasing program readability.
Because C is so flexible in how you can construct a program (not only visually but in terms of
how you use the language itself), organizations often develop coding standards; to ensure
consistency from one program to another.
1. What are the syntax rules for C?

C is case-sensitive, all keywords are lowercase, keyword names are reserved and cannot be used
for any other purpose, and whitespace is ignored.
Building an Executable Version of a C Program
Lesson Objectives
In this lesson, you will:

· Explain the difference between an interpreter and a compiler.

· Define linking.
Interpreters and Compilers

After you have created a source file, you cannot immediately run, or execute, it. Source code
must be translated from its human-readable form into a binary version called object code, or
machine code, that is understood by the host computer. This is accomplished either by an
interpreter or a compiler.
Interpreters read and convert source code one line at a time and execute it directly. Compilers
read and convert the entire program into object code without executing it, creating a separate file
called an object file. Theoretically, any language's source code can be interpreted or compiled.
Although C interpreters have had popularity as debugging tools, C source files are usually
compiled. A compiled program generally executes faster than an interpreted program.
While this course has been written to be generic, as far as which compiler you use, the following
figure shows how you can access the compile command in Borland's free C++ compiler for
Windows, which you can use to compile C programs.
Preprocessing and Compiling

The compiler carries out both preprocessing and compiling. Preprocessing is the modification of
a source file prior to its translation into machine language, through the execution of any
preprocessing directives that the programmer has embedded in the source code. Preprocessing is
carried out by a part of the compiler (actually a separate program) called a preprocessor. An
example of preprocessing is the addition of header files to a source file through the execution of
include statements.
Once preprocessed, the source file is then compiled, or translated into a version of the program
called an object file. Object files are given a default extension, such as .o or .obj, by the compiler.
Linking

Owing to the modular approach of C, the final components of a C program most likely will be
contained in separate places. Before the final program can be run, all the different modules must
be connected, or linked together. This process of linking is accomplished by a program called a
linker.
Two types of modules might need to be linked to an object file to create a final, executable
program:

· Library functions exist in programmer libraries and are already compiled into object
code. Although header files containing information about library functions are added to a
source file, the functions themselves are not. Linking connects an object file with the
addresses of any library functions called within the program.

· Other object files. For large programs that are built from separate source files, linking
provides the mechanism to connect a recently compiled object file with previously
compiled files to produce a complete program.
The steps of preprocessing, compiling, and linking are often referred to as the build process. The
build process culminates in a build of the program, or a final, executable version.
Executing

Executing a program is the act of running the final version of the program provided by the linker.
To execute a C program, enter the program name at the operating-system prompt.
1. What is the difference between an interpreter and a compiler?
An interpreter is a program that reads and converts source code one line at a time and executes it
directly. A compiler is a program that reads and converts an entire program into object code
without executing it, creating a separate file called an object file.
2. What is linking?
Linking is the process of connecting the many different modules (object files) of a program and
creating the final executable version.
Debugging a C Program
Lesson Objectives
In this lesson, you will:
List three comDebugging a C Program

In the development of a C program, errors can occur either during compilation (or compile time),
during linking (or link time), or during execution (or run time).

· mon mistakes that cause a compiler error.


Compile-Time Errors

Errors reported by the compiler are generally syntax errors where an element of the source code
isn't in a format that can be converted to valid C object code. When the compiler reports an error,
it gives a number indicating the line in the source file that generated the error followed by an
error code and a brief description of the error.
Common causes of compiler errors are:

· a missing semi-colon following a C statement,

· improperly spelled keywords, and

· improperly delimited code blocks involving either too few or too many braces
Link-Time Errors

Link-time errors are the result of problems the linker has in connecting an object file with
external modules needed to run the program. Like the compiler, when the linker reports an error,
it provides an error code and a brief error description. However, because the linker works with
object files output by the compiler, it does not provide a number of a line in the source file for
you to refer to when you're troubleshooting.
Common causes of linker errors are:

· In the source file, either with misspelled names of external functions or with a missing
reference to an appropriate header file.

· At the command line, either with mistyped names of other object files or with a mistyped
library name.
Run-Time Errors

Run-time errors occur during the actual execution of a program, causing a program to operate in
unexpected ways. Rather than the result of a syntax problem, they're conceptual in nature and are
also called logic or semantic errors.
Resolving Errors

Resolving most compiler and linker errors is a process of correcting syntax. Run-time errors,
however, are more serious.
A common procedure for resolving run-time errors is to:
Get a clean copy of the source code.
Examine the program, executing each statement mentally.
Insert "printf" statements at strategic places within the source file. Rebuild and rerun the
program to track variables and determine program flow.
If necessary, use any available debugging utilities.
1. What are three common mistakes that cause a compiler error?
Misspelling a keyword, forgetting a semicolon, and using braces incorrectly

Examining and Running a C Application Program


Lesson Objectives
In this lesson, you will:

· Define a function definition.


Examining and Running a C Application Program

In essence, you can think of a C program as a collection of functions, either defined within the
source file or externally in other source files or in libraries. The programs you have examined so
far have consisted of only one user-defined function in the source file, main(). Most C source
files will contain additional user-defined functions; each created to accomplish a specific task
within the program. All functions you create must be both declared functions and defined
functions. Statements that declare functions are grouped at the beginning of the program and are
called function prototypes. Statements that define a function precede the body of each individual
function and are called function definitions.

Reading Larger C Programs

A general strategy for reading larger C programs is to first read any programmer comments at the
beginning of the program and then locate the function main(). Because main() receives
control from the operating system when the program is run, in a well-written C program, it acts
as a switchboard, calling other functions and coordinating the flow of the program. Having
gained a "bird's eye view" of the program from the function main(), other functions can then
be analyzed in the order in which they're called. Once called, a function might call other
functions. When a function completes, it always returns control back to the calling function.
Ultimately, control is returned back to the function main().

By convention, main() is listed first, followed by any other user-defined functions. Other
functions are typically listed in the order they're first called. In larger programs, it might be more
convenient to list them alphabetically for ease of lookup. The beginning portion of a C program,
prior to the function main(), typically consists of preprocessing directives, definitions of user-
defined elements that are common to all functions, and a list of the functions themselves.
Because these items make sense only at a more detailed level of understanding, it is best to skip
them when you're first reading a program and get right to the function main().

1. What is a function definition?


The statement that precedes the body of a function. The term is also used to describe the
combined unit of that statement and the body of the function.
Data Types
Lesson Objectives
In this lesson, you will:

· Explain why you would use of a constant instead of a variable.

· Describe an escape sequence.


Data Types

Computers and computer programs generally exist for a single purpose: to process data. When
you're using a computer or writing computer programs, how the computer handles information
internally is usually a secondary concern. What type of information goes into your program, the
format in which the information is going to be returned to you, and the correctness of the
processing are your primary concerns. The C programming language uses data types similar to
other computer languages. However, the C language uses its own terminology and syntax for
creating and using data-type structures. In addition, there are several data features that are unique
to the C language.
Defining Data

It would be convenient if the C language used only one data type; if, regardless of the nature of
the data (numbers, characters, or strings of characters), data were all handled in the same manner.
Unfortunately, as with most computer languages, this isn't the case. Depending on the type of
information you're working with, you need to specify your data types, and how you're going to
represent them in your C program. There are two broad formats for data: constants and variables.
Constants
A constant is data that is set before the computer program begins and retains the same value
throughout the life of the program. A constant can be:

· a number,

· a character; or

· a word.
An example of a commonly used constant is the value of pi, 3.14159. If you used this in a
program, you would not want its value to change as a result of the action of the program.
Variables
A variable is a placeholder, for an assigned value, in the computer's memory. A variable can also
be a number, a character, or a word. Variables are more complex than constants. When a variable
is used, the following must be considered:

· indicating the data type (number, character, or word),

· the location in memory where the variable is stored,

· naming the variable; and

· giving the variable a value.


Variables are often given names that relate to the values they represent. Specific conventions
apply to variables, relating to the type and size of the data. The value that is given to a variable
may change once, or many times, throughout the life of the program.
The printf() Function and its Data Format Specifiers

In Lesson 1, you briefly examined the printf() function, and how it is used. Because the
printf() function is the most common way to print data in C, you need to learn more about
printf() before you can thoroughly examine the C data types. The format of the printf()
function is:

printf( argument );
Within the parentheses is the argument for the printf() function. Also within the parentheses
is a pair of double quotes. Whatever is within the double quotes is printed to the screen. For
example, the program statement:

printf("Welcome to C Programming.");
will print the sentence: Welcome to C Programming.
Escape Sequences
An escape sequence is a special character preceded by a backslash (\). An escape sequence
located within the double quotes of the printf() argument represents a difficult-to-represent
character sequence. For example, the new-line (\n), tab (\t), and backspace (\b) characters are
non-printing characters. They don't produce a readily visible output on your screen. You will
learn more about non-printing characters and their uses later in this lesson. Other commonly used
escape sequences include:

Escape Sequence Meaning


\a Alert (beep)
\b Backspace
\f Form feed
\n New line
\t Tab
\' Single quote
\'' Double quote
\\ Backslash
\0## Octal number
\X## Hexadecimal number

In the printf() function below, the escape sequence \n represents the newline character.
The program statement:

printf("\nWelcome to C Programming.");
will print a blank line before the text. The program statement:

printf("\n\n\tWelcome to C Programming.\n\n");
will place two blank lines before the text, two blank lines after the text, and move the phrase over
one tab.
Format Specifiers
To include data in your printf() call, you need to use format specifiers. The format specifier
is located within the double quotes of the printf() statement, and is preceded by the percent
symbol, %. The data the format specifier refers to follows the last double quote in the printf()
argument. Using different types of data requires you to use different format specifiers. The
following table summarizes the format specifiers, the type of data used, and a typical input and
output for each type:

Format Specifier Type Input Output


%c Character 'B' B
%d Decimal 10 10
%ld Long Decimal 100000 100000
%e Exponential 15000 1.5e4
%f Floating point 10.50 10.50000
%o Octal 8 10
%s Character string "Hello!" Hello!
%x Hexadecimal 18 12

When you use the %c format specifier, you must enclose the character in single quotes. When
you use the %s format specifier, you must enclose the string in double quotes. For example, the
program statements:

printf("Welcome to %c Programming.", 'C');


printf("Welcome to %s", "C Programming.");
will both produce the output:

Welcome to C Programming.

The printf() Function and its Data Format Specifiers

The program statement:

printf("Welcome to C Programming %d.", 1);


will print the text:

Welcome to C Programming 1.
In this example, the %d format specifier refers to a decimal value. The constant 1 is the data
placed in the text.
The number of places to the right or left of the decimal in a floating-point number can be set by
including digits in the %f format specifier. For example, the program statement:

printf("The check is for $%.2f.", 10.50);


will produce the output:

The check is for $10.50.


The number of decimal places to the right of the decimal has been restricted to two. By including
a number to the left of the decimal, you can right-justify numbers of uneven amounts. The
printf() function has many escape sequences, format specifiers, and tools that you can use.
You have learned the basics of printf() and should now know enough about the function to
use it to learn the C data types.
The following code listing is the source for the L2-1.c program that you will examine in the
upcoming task.
#include <stdio.h> /* include statements */

main() /* main function */


{
printf("The integer value is : %d \n",
100);

printf("The signed integer value is : %d


\n",-100);

printf("The floating point value is : %f


\n",100.50);

printf("The formatted float value is : %.2f


\n",100000.50);

printf("The justified,formatted value is :


%9.2f \n", 100.50);

printf("The character x is : %c \n", 'x');

printf("The character string xyz is : %s


\n","xyz");
}

Basic Data Types of the C Language

In addition to the designations of constant and variable, you will find that there are different
types of both. Several of the C data types were introduced as constants when you examined the
printf() format specifiers. There are two broad groups of C data types: integer data type and
floating point data type.
Integer and Character Data

The integer data type consists of numbers that don't have a fractional component and are never
written with a decimal point.

· Numbers like 1, 50, -50, and 567 are examples of integer constants or values that can be
assigned to integer variables.

· Numbers such as 150.4 or 354.00 aren't integers and should not be assigned to integer
variables.
A subset of the integer type is the character data type. While characters may seem to be a
different set of data, and in some references are treated as such, it is important to realize that
characters are always handled by your computer as integers. On your computer system, each of
the characters that you use (0 through 9, A through Z, and a through z), including special
characters (such as !, @, #, $, %, ^, and &), is represented by a numeric code.
You will encounter two primary codes that are used to represent characters:

· ASCII (American Standard Code for Information Interchange) code; and

· EBCDIC (Extended Binary Coded Decimal Interchange Code) code.


Examples of character representation and their ASCII code numeric values chart are illustrated in
the following table.

Character ASCII value #

# 35

0 48

1 49

A 65

B 66

a 97

b 98

{ 123

} 125

Integer and character data are discussed in greater depth in the topic entitled "Data Declaration
and Assignment" following Task A-2. When you're creating variables, the C language requires
you to define the type of data you will work with.
Integer Data-type Keywords
The data-type keywords used with integer and character data are summarized in the following
table.

Keywords Meaning
char Alphanumeric data.
int Basic keyword for integer data.
short Integer data, no larger than an int, and may be smaller.

long Integer data, at least as large as an int, and may be larger.

In addition to the basic integer and character keywords, C has keyword modifiers that are used in
combination with the integer and character keywords to modify their meanings. The keyword
modifiers used with the integer data types are summarized below:
Keyword Modifiers Meaning
unsigned Positive value only.
signed Positive or negative value.

By default, the char, int, short, and long data types are signed.

Floating-Point Data

Floating-point numbers are often referred to in other computer languages, such as Pascal and
FORTRAN, as real numbers. They are numbers that contain a fraction of a number (or mantissa),
such as 0.50, -1.3, or 125.56, or numbers that have a decimal component, such as 100.00.
Floating-point numbers can be represented in the traditional format, 125.56, in scientific-notation
format, 1.2556x102, or in exponential format, 1.2556e2 , where e2 is the exponent. To reproduce
the original number, move the decimal point two places to the right. In the C language, you must
use the exponential format, such as 1.2556e2.
Floating-point Keywords
The floating-point keywords used with floating-point data are summarized in the following table.

Keywords Meaning
float Basic keyword for floating-point values.
double As precise as a float, and may be more precise.

In addition to the float and double keywords, C contains keyword modifiers that can be
used in combination with float and double to modify their meanings. The keyword modifiers
used with the floating-point data types are summarized below:

Keyword Meaning
Modifier
long Used with double; at least as precise as a double, and may be more precise.

By default, the float and double data types are signed. The long double is also signed
by default.
Data Storage

When you use data and designate specific data types, you're creating locations in memory where
the data will be stored. Understanding how the data is stored will help you understand the
limitations and possible problems that you might encounter when you use different data types.
Storage in Memory
The smallest storage unit in computer memory is the bit. The bit uses a binary, or base-2, method
of storage. Within each bit is a 0 or a 1. Using bits as building blocks, the next level of storage is
the byte. A typical byte is made up of eight bits. Using base-2 (binary) calculations, 2 to the
eighth power is 256, which is the number of possible values a byte can store. Since the first value
is zero, the largest number that can be stored in an 8-bit byte is 255.
The word is the next building block in computer memory and is made up of bytes. As a unit of
storage, words will vary by computer design. In early computers, a word was one byte long. In
today's computers, words might be 8 bytes (64 bits) or more in length. In most desktop
computers, you will find words to be either 16 or 32 bits long.
Data Ranges

Understanding the memory organization and the sizes allocated to the data types will help you
determine the limitations of the data types in a C program.
Character Data
Character data is stored as integers. The char type is generally one byte in length and may be
represented as signed or unsigned data. You may need to refer to your compiler manual to
determine whether the char is signed or unsigned by default.

The values that can be stored in the type char are summarized in the following table:

Type Bytes Lower Range Upper Range


char 1 -128 127
signed char 1 -128 127
unsigned char 1 0 255

The ASCII code provides for character representations using the range of 0 to 127. This allows
the ASCII code to be used with signed or unsigned character types.

Integer Data
The ranges for integer data on your system depends on the size, in bytes, of the default int.
When a type is signed, it uses the high-order bit to store the sign; the absolute value that can
be stored is reduced by approximately one-half.
The values that can be stored in the integer data types are summarized in the following table:

Type Bytes Lower Range Upper Range


short [int] 2 -32768 32767
unsigned 2 0 65535
short

long [int] 4 -2147483648 (2 billion) 2147483648 (more than 2 billion)


unsigned 4 0 more than 4 billion
long

Floating-point Data
Floating-point data uses a different type of organization than the integer types. The exact number
of bits used for each component storing floating-point data will vary with the system. Floating-
point storage consists of two parts:

· storage for the exponent value storage and sign storage; and

· storage for the nonexponent storage portion.


The number of bits used for the nonexponent portion determines the precision of the number. For
example:

· A four-byte (32-bit) float, floating-point storage system will usually use 24 bits for the
nonexponent portion and will produce a precision to 6 or 7 digits.

· An eight-byte (64-bit) double, floating-point storage system may use extra bits to
increase the precision of the data stored. However, it is required by the ANSI C standard
to be only as precise as the float.

· The ANSI C standard also allows for a long double. This may be more precise than
the double, but is guaranteed to be at least as precise a double.

The range of values that can be stored in the floating-point data types depends on the size in
bytes. The range of values for certain size floating-point types is summarized in the following
table:

Type Bytes Lower Range Upper Range


float 4 3.4e-38 3.4e+38
double 8 1.7e-308 1.7e+308
long 8 the figure below minimum size 1.7e-308 1.7e+308
double

The sizeof Operator

The C language provides the sizeof operator, which returns a result in bytes. For example,
you can use the following statement to determine the size, in bytes, of an integer on your system:
sizeof (int)
Once you know the size of a data type for your system, you can calculate, or refer to the above
charts, to determine the maximum and minimum values that you can use.
The following code listing shows the source for the L2-2.c program, which you will examine in
the next task.

#include <stdio.h> /* include files */

main() /* main function */


{

/*
use the sizeof operator to display the size of
data types on your system
*/
printf("\nThe size of a char is %d bytes.",
sizeof(char));
printf("\nThe size of an int is %d bytes.",
sizeof(int));
printf("\nThe size of a short is %d bytes.",
sizeof(short));
printf("\nThe size of a long is %d bytes.",
sizeof(long));
printf("\nThe size of a float is %d bytes.",
sizeof(float));
printf("\nThe size of a double is %d bytes.",
sizeof(double));
printf("\nThe size of a long double is %d
bytes.", sizeof(long double));
}

Data Declaration and Assignment

To use data assignment in a C program, you must follow certain conventions:

· With constants, the conventions are simple. You generally get what you see.

· With variables, there are steps you must follow, including:

· declaring (creating) the type of data you want to use, and giving a meaningful
name to the variable; and

· assigning a value to the named variable.


Variable Names
The ANSI C standard allows variable names of up to 31 characters long. In reality, you can
create longer variables, but the compiler isn't required to recognize any characters beyond that
length. Variable names can include upper- and lowercase characters, digits, and the underscore
character. It is recommended that you use meaningful variable names. For example, in the
checkbook program, you will use a variable named current_balance to hold the current-
balance value.
Variable Declaration
Usually, a section at the beginning of the program is designated for the declaration of variables.
This section is placed after the opening brace in main() and before any program statements:

#include <stdio.h>

main()
{
/* variable declarations */
type name;
type name;
.
.
program statements;
}
This convention adds to the readability of your program and promotes good program planning.
Declaration and assignment is required for every variable you want to use. While the declaration
method is standard regardless of the data type, the assignment of values varies depending on the
type of data that you're using.

TIP You must declare and assign every variable you want to use in your program or your
: compiler will report an error
Integer Data

Using an integer constant in your program is so easy that it seems obvious. Whenever you want
to use an integer constant, simply insert the value. For example, in the printf() statements:

printf("The value of ten is %d.", 10);


and

printf("The value of negative ten is %d.", -10);


the integer constants are 10 and -10.
Declarations
To create integer variables, you must use program statements to declare and name the variables.
There are several formats for integer declaration, including:

type variable_name;
type variable_name;
and

type variable_name,
variable_name,
variable_name;
Declaration types include:

· int, short, or long,

· short int or long int; and

· the unsigned variants of the above.

The integer type should be determined by the size and range of the data that you want to store in
the variable.
Initialization and Assignment
Initializing a variable means placing a starting value in the variable, assuring that it begins life in
the program with a known value. Assigning a value to a variable is similar to initialization, but
different values can be assigned once or many times during the life of the program. To initialize
and assign values to a variable, use the equal sign (=) character. Initialization can be
accomplished in the declaration:

type variable_name = value;

type variable_name = value,


variable_name = value;
Assignment can be done as a separate statement:

variable_name = value;
More than one variable can be assigned a value with a single program statement:

variable_name = variable_name = value;


Integer Data

Overflow and Underflow


To find the data range of a variable, look at its declaration to find its type, and then refer to the
chart that summarized the lower and upper limits for that particular data type. Although some of
the data types hold very large values, with others, it is possible to go beyond the maximum or
minimum value.
However, a typical result is that the value will wrap around. For example, if you're working with
the type short int, with a maximum value of 32767, and you increase it by 1, to 32768, the
value that is output may be -32768. In other words, the value "wrapped around" to the minimum
value of the type. The point to be aware of is that there are limits to the types; if you encounter
"strange" results, you might be dealing with data that has gone beyond its limits.
Character Constants
Character constants are simply the characters that they represent. When they're used, such as in
the printf() statement, they're enclosed in single quotes.

printf("The character A is %c.", 'A');


If the single quotes were not placed around the character, the printf() statement would view
the A as a variable.
Declarations
The declaration for the char type is similar to integer declaration.

char variable_name;
As was illustrated with the integer declarations, the actual format of the declaration can vary. The
ANSI C standard allows you to use the signed and unsigned modifiers with the char
type.
Initializing and Assigning Character Constants
To initialize a type char to a specific character constants, use the equal sign, =, character. The
character you are using to initialize or assign is enclosed in single quotes.

char variable_name = 'character';


For example, to declare a type char variable named letter and initialize it to A, use the
following code:

char letter = 'A';


To declare a type char variable named number and then assign it the character 1, use the
following code:

char number;

number = '1';
Although 1 is an integer, in this example it is being treated it as the character 1; therefore, it must
be enclosed in single quotes.
Character Assignment Using Numeric Codes
Character data can be treated as a subset of the integer data type. The computer actually stores
character data as integers. By using integers, you can assign values to character variables.
Using ASCII values, the following code:
char letter1 = 65,
letter2 = 66;
creates two type char variables, letter1 and letter2, and assigns the integer values 65
and 66, which, in ASCII code, represent the characters A and B.

Integer Data

Assignment of Non-printing Characters


Non-printing characters were introduced with the printf() function. You were shown how the
\n, (new line), and \t, (tab) characters were used. There are a number of ways that non-printing
characters can be assigned to type char variables.
First, non-printing characters can be used by enclosing the escape sequence in single quotes,
similar to the assignment of character constants. For example, the code:

char newline = '\n';


creates a type char variable named newline and initializes it to the new-line escape
sequence. Using the newline variable in the printf() statement:

printf("Go to the next line.%c", newline);


is the same as:

printf("Go to the next line.\n");


Second, the ASCII value of the escape sequence can be assigned to the variable. For example,
the code:

char newline = 10;


assigns the integer value 10 to newline. The ASCII value 10 represents the new-line, or line-
feed, character. There are several other ways of representing non-printing characters, such as
octal and hexadecimal representation. The two methods discussed above will enable you to use
the non-printing characters on most systems.

TIP The single quotes aren't required when you're using an escape sequence within the
: printf() double quotes.

Floating-Point Data
Constants
There are a number of valid ways by which floating-point constants can be represented.
Following are some examples of correct forms:

3141.59 3.

.14159 .14159e+3

3.14159e3 3.14159e+3

3.14159e-3 3.14159E3

3E3

You cannot use a format that contains a space in the constant, such as 3.14159 E+3. Valid
floating-point formats can be used as constants in printf() statements, or they can be used in
initialization or assignment of variables.
Declarations
The syntax of the floating-point variable declarations is the same as that of the integer type. The
declaration types include float, double, and long double. The floating-point type you
choose should be determined by the size and range of the data you want to store in the variable.
Initializing and Assignment
Initialization and assignment of floating-point variables is the same as for the integer type.
Overflow and Underflow
If you try to place a number that is too large or too small for the floating-point data type you're
using, you can be sure that you won't get the desired result. The result will depend on the
compiler that you use. You might receive a run-time error, the program might abort and issue an
underflow or overflow message, or you might not receive a warning. If overflow or underflow of
your floating-point variables is a concern, check the documentation supplied with your compiler
for the ranges of the different floating-point types
The typedef Keyword

The C language allows the use of the keywords, typedef, in combination with a valid C data
type, to redefine the name of the data type. Redefining the name of a data type can create a
program that is easier to read and to understand, and may be more portable. The format for use of
the typedef keyword is as follows:

typedef original_data_typenew_name;
For example, if you're familiar with a language that uses the name "real," for floating-point
values, the following statement would allow you to emulate that nomenclature:
typedef float real;
The following statement creates two floating-point variables:

real value1, value2;


It is important to remember that you're not creating a new data type; you're using an alias for the
data type in the typedef statement.

The typedef Keyword

The following code listing shows the source for the L2-3.c program, which you will examine in
the next task.

#include <stdio.h> /* include statements */

main() /* main function */


{
/* integer declarations */
int int1;
short int2;
long int3;
/* floating point declarations */
float val1;
double val2;
/* character declarations */
char ch1 = 'A'; /* declaration & assignment */
char ch2, ch3; /* multiple declarations */

/* integer assignments */
int1 = 100;
int2 = -100;
int3 = 2000000000;
printf("The int1 value is : %d
\n", int1);
printf("The int2 value is : %d
\n", int2);
printf("The int3 value is : %ld
\n", int3);
int2 = 2000000000;
printf("Assigning large value to a short : %ld
\n", int2);

/* floating point assignments */


val1 = 1000.50;
val2 = -1000.50;
printf("The val1 value is : %f \n", val1);
printf("The val2 value is : %.2f \n", val2);
/* displaying exponential values */
printf("val2 displayed in exponential form :
%e \n", val2);

/* character assignments */
ch2 = 65; /* numeric assignment */
ch3 = '\a'; /* non-printing character (alert) */

printf("The ch1 value is : %c \n", ch1);


printf("The ch2 value is : %c \n", ch2);
printf("The ch3 value is : %c \n", ch3);
printf("The ch3 ascii value is : %d \n", ch3);
}

1. Why would you use a constant rather than a variable?


Sometimes you'll have data that you do not want to change during your program's execution.
That data could include scientific constants (the speed of light, PI, and so forth) but just as likely
will be your own constant data.
2. What is an escape sequence?
An escape sequence is a character preceded by a backslash (\). It is a way of representing special
characters, such as tabs, new-lines, and quotes, which cannot be directly entered into a program.
Operands, Operators, and Arithmetic Expressions
Lesson Objectives
In this lesson, you will:

· Explain the difference between a binary and a unary operator.

· Define a bitwise operator.


Operators and Operands

Operators and operands are combined to form expressions that are used in C program
statements. The ANSI C standard uses several terms to describe operators, operands, and their
actions. The best way to illustrate these is with an example. By using the simple C program
statement:

int1 = int2 + int3;


you can examine most of the terminology.
Operators perform actions on operands. In the sample program statement, the +, or addition
symbol, is operating on the int2 and int3 operands by adding them. The =, or assignment
operator, then assigns the result to the int1 operand. ANSI C uses additional terms to describe
the operator/operand relationship, including:

· data object,

· lvalue, and
· rvalue.
The term data object refers to the actual location where the data is stored. The term lvalue refers
to the label used to locate the storage location, such as a variable name. The term rvalue refers to
data that can be used to modify the lvalue data. In the sample program statement, the lvalue is
int1. It is assigned the results of the rvalues to the right of the assignment operator and refers to
the data object where the result of the addition will be stored.
The Basic C Operators

The C programming language uses operators that are common to most programming languages,
including:

Symbol Action
() Parentheses
* Multiplication
/ Division
% Modulus
+ Addition
- Subtraction
= Assignment

The arithmetic operators *, /, %, +, and -, and the assignment operator =, require two operands
and are sometimes referred to as binary operators. Parentheses can enclose a number of
operations.
The % symbol, or modulus operator, is used in integer arithmetic and returns the remainder of
two integers. For example, the result of (8 % 5) is 3.
Associativity refers to the way in which the expression is evaluated. If there is more than one
addition operator in an expression and they're not enclosed in parentheses, they will be added
together from left to right. The arithmetic operators and the parenthetic associativity is from left
to right. The assignment operator's associativity is from right to left; because the calculations
must be performed before the result is assigned to the lvalue.
Rules of Precedence
When you create an arithmetic expression or are evaluating an existing expression, the order in
which the operations are performed determines the resulting value. Because of this, there are
rules that govern the order of the arithmetic operations. These are known as the rules of
precedence.
Precedence dictates the order in which the operations in an expression are performed. Each of the
operators has a precedence value assigned to it. This value is in relation to the other operators
and determines the order of the operations.
The following table summarizes, from highest to lowest, the precedence of the operators you will
learn about in this lesson:

Precedence Operator Associativity

Highest () Left to right


* / % Left to right
+ - Left to right

Lowest = Right to left

When operators are at the same level of precedence, the evaluation is done according to their
associativity, unless they're enclosed in parentheses.

Understanding the Arithmetic Operators


Test your knowledge of arithmetic operators by clicking on the View Code button below to
observe a sample of code. The code will appear in a separate window. You can move the window
and use the scrollbar to view all of the code. While viewing the code, type your answers in the
boxes provided, then click on the answer button to compare your answers to ours.
include <stdio.h> /* include files */ main() /* main function */ { int int1,int2; float float1,float2;
char char1,char2; /* * To illustrate operators, operands, and arithmetic operations */ /* Statement
1 */ int1 = 25; int2 = 25; int1 = int1 + int2 - 10; printf("\nThe value of int1 is %d.", int1); int2 =
int1 + (int2 - 10); printf("\nThe value of int2 is %d.", int2); /* Statement 2 */ float1 = 100.5;
float2 = 25; float1 = float1 * 2/2 + float2; printf("\nThe value of float1 is %f.", float1); float2 =
((float1 * 2) + 25 - 10) / float2; printf("\nThe value of float2 is %f.", float2); /* Statement 3 */
char1 = 'A'; char1 = char1 + 2; printf("\nThe ASCII value of char1 is %d, or" "the character
%c.", char1, char1); char2 = ((char1 +2) - 1); printf("\nThe ASCII value of char2 is %d, or" "the
character %c.", char2, char2); }
1. What is the value of int1?
40
2. What is the value of int2?
55
Understanding the Arithmetic Operators
Test your knowledge of arithmetic operators by clicking on the View Code button below to
observe a sample of code. The code will appear in a separate window. You can move the window
and use the scrollbar to view all of the code. While viewing the code, type your answers in the
boxes provided, then click on the answer button to compare your answers to ours.
#include <stdio.h> /* include files */

main() /* main function */


{
int int1,int2;
float float1,float2;
char char1,char2;
/*
* To illustrate operators, operands, and
arithmetic operations
*/
/* Statement 1 */
int1 = 25;
int2 = 25;
int1 = int1 + int2 - 10;
printf("\nThe value of int1 is %d.", int1);
int2 = int1 + (int2 - 10);
printf("\nThe value of int2 is %d.", int2);

/* Statement 2 */
float1 = 100.5;
float2 = 25;
float1 = float1 * 2/2 + float2;
printf("\nThe value of float1 is %f.",
float1);
float2 = ((float1 * 2) + 25 - 10) / float2;
printf("\nThe value of float2 is %f.",
float2);

/* Statement 3 */
char1 = 'A';
char1 = char1 + 2;
printf("\nThe ASCII value of char1 is %d, or"
"the character %c.", char1, char1);
char2 = ((char1 +2) - 1);
printf("\nThe ASCII value of char2 is %d, or"
"the character %c.", char2, char2);
}

1. What is the value of char1?


’C’
2. What is the value of char2?
’D’
Unary Operators

Unary operators act upon a single operand. Some of the C unary operators will be familiar. For
example, the + and - signs that give a positive or negative value to a number are unary
operators. Other C unary operators are unique to the language. You have already used one of the
C unary operators, the sizeof operator. The sizeof operator is unary because it applies its
actions to only one operand. Other unary operators that you will use are the sign operators, and
the increment and decrement arithmetic unary operators.
The sign operators
The sign operators (+ sign operator and - sign operator) are familiar as operators that designate
positive or negative numbers. The absence of a sign usually indicates a positive value. The
format for the unary sign operator is:

signvalue
The sign is placed immediately in front of the value. Although the unary + operator does not
alter a value, the ANSI C standard does allow its inclusion for emphasis and for improved
readability of program statements. The sign operators are high on the precedence list, second
only to the parentheses. The + and - operators share an equal level of precedence.

The increment and decrement operators


The unary increment and decrement operators are unique to the C language and its derivatives
(C++, Java, and JavaScript, to name a few). They do exactly what their names imply: they
increase or decrease the value of their operand by one. The format of the increment and
decrement operators includes two modes: prefix and postfix.

· When the prefix mode is used in an expression or statement, it increments the value by
one before the value is used. The form of the prefix increment operator is:

+
+valu
e;

· When the postfix mode is used in an expression or statement, it increments the value by
one after the value is used. The form of the postfix increment operator is:

value
++;

· The prefix and postfix mode of operation of the decrement operators is the same as for
the increment operators, except that they decrease their operand by one. The forms of the
prefix decrement operator and the postfix decrement operator are the same as for the
prefix and postfix increment operator:

--
value
;
value
--;
In general, the increment and decrement operators replace program statements such as:

int1 = int1 + 1;
/* could be replaced by */
int1++;
/* and */
int1 = int1 - 1;
/* could be replaced by */
int1--;
Used by themselves, in statements such as:

int++;
or,

++int;
there is no difference in the result of the prefix or postfix mode. However, when used in
expressions and program statements, the result may be entirely dependent on the mode. For
example, the statements:

int1 = 10;
int2 = 10;
int1 = int1 + ++int2;
yield a result of 21. The int2 variable is incremented by 1 (to 11) prior to being added to int1.
The statements:

int1 = 10;
int2 = 10;
int1 = int1 + int2++;
yield a result of 20. The int2 variable is incremented by 1 (to 11) after the addition of int1
and int2.

When you use the unary increment and decrement operators, your program will be compact,
clean, and efficient. The increment and decrement operators are on the same level of precedence
as the sign operators.

Arithmetic Assignment Operators

The arithmetic assignment operators are also unique to the C language and its derivatives.
Arithmetic assignment operators provide a shorthand method for performing arithmetic
operations and assigning the results. For example, the program statement:

int1 = int1 + int2;


can be written by using the addition assignment operator as:

int1 +=int2;
The results will be the same. The other arithmetic assignment operators are:
Assignment Operator Action
+= Addition/assignment
-= Subtraction/assignment
*= Multiplication/assignment
/= Division/assignment
%= Modulus/assignment

Expressions can be used as the right side of the assignment operator. For example, the statement:

int1 += int2 - int3;


subtracts int3 from int2, adds the result to int1, and assigns the result to int1.

The arithmetic assignment operators share the same level of precedence as the simple assignment
operator, on the lower portion of the precedence chart, because the assignment is made after the
expression is evaluated.
Type Conversion

In most computer languages, trying to assign a value of one type to a data object of another type
is allowed. The C language, however, allows for the conversion of data by two methods. C will
automatically convert types for you; it also provides an operator that forces conversion to a type
that you designate.
Automatic Type Conversion
Type conversion can be a convenience, or a problem, depending on the result of the conversion
attempt. There are two types of conversion: promotion conversion and demotion conversion.
Promotion is attempting to convert a lowercase data type to a higher case, and demotion is the
converse. The ranking of data types is:

Rank Data Type

Highest long double

double

float

unsigned long

long

unsigned int
Lowest int

The short and char types aren't listed; they will be automatically converted to int or
unsigned int, depending on which is the larger value.

The rules that C follows regarding automatic type conversion are:


Types char and short will be converted to type int. If unsigned short is larger
than int, then the type short variable will be converted to an unsigned int.

In a conversion, the lower ranking value will be promoted to the higher value.
When an assignment statement is involved, the result of the calculation will be promoted or
demoted to the type being assigned to.
Depending on your compiler and the error-level set, a warning might be issued regarding
promotions or demotions.

TIP In general, promotions aren't a problem. However, demotions can be dangerous,


: because values of a higher type may overflow the upper range of a lower type.
Forced Type Casting
The C programming language allows you to force a data type to be converted to an alternate
designated data type by using cast operators. The format of the cast operator is:

(type)operand;
The type that you're converting, or casting, to must be enclosed in parentheses. Automatic type
conversion and casting can produce different results. For example, in the following code
segment:

int int1, int2;


float float1, float2;

float1 = 5.5;
float2 = 5.6;

int1 = float1 + float2;


int2 = (int)float1 + (int)float2;
the resulting values of int1 and int2 are different.

· In the int1 assignment, the values are first added to produce a result of 11.1, then
truncated to produce an int value of 11.

In the int2 assignment, the float values are first truncated to 5, then added to produce an
integer result of 10. The Bitwise Operators
C is a mid-level language, with features of both high- and low-level languages. One of the
features of C that enables it to function as a low-level language, such as the assembler languages,
is a set of operators known as the bitwise operators. Bitwise operators enable you to include in a
program code that works with the individual bits (0s and 1s). The operators allow for actions
such as bit-checking, modifying individual bits, bit-masking, and multiplication and division
accomplished by bit-shifting.

TIP The code is shown here with numbers simply to make referring to portions of the
: code easier in class. You would not of course, include those line numbers if you
were to enter this program into a file.

1 #include /* include files */


2 main() /* main function */
3 {
4 char char1,char2;
5 int int1,int2,int3,int4;
6 float float1,float2;
7 /* To demonstrate the precedence of the basic arithmetic operators */
8
9 /* Statement 1 */
10 int1 = 25;
11 int2 = 12;
12 int1 += int2;
13 int1 = --int2;
14 printf("\nThe result of Statement 1 is: %d", int1);
15
16 /* Statement 2 */
17 int1 = 5;
18 int2 = 10;
19 int1 %= 3;
20 int2 = (int1++ + 25);
21 printf("\nThe result of Statement 2 is: %d", int2);
22
23 /* Statement 3 */
24 int1 = 5;
25 int2 = 10;
26 char1 = 'A'; /* ascii value 65 */
27 int1 *= 3;
28 int1 += (++char1 + int2);
29 printf("\nThe result of Statement 3 is: %d", int1);
30
31 /* Statement 4 */
32 int1 = 25;
33 int2 = 500;
34 float1 = 12.75;
35 int2 /= int1;
36 int1 = int2;
37 int1 = (int)float1 + int2;
38 printf("\nThe result of Statement 4 is: %d", int1);
39
40 /* Statement 5 */
41 float1 = .5;
42 float2 = 25;
43 float1 = float1 * (2 / 2) + float2;
44 printf("\nThe result of Statement 5 is: %.2f", float1);
45
46 /* Statement 6 */
47 int1 = 100;
48 float1 = 100;
49 float2 = 100;
50 float1 = float1 - --float2;
51 float1 = --float1;
52 int1 = (int)float1;
53 printf("\nThe result of Statement 6 is: %d", int1);
54 }

1. What's the difference between a binary and a unary operator?


These terms describe categories of operators that accept different numbers of operands: unary
accepts one, binary accept two, and ternary, which are not covered in this lesson, accept three
operands.
2. What is a bitwise operator?

Input/Output Management
Lesson Objectives
In this lesson, you will:

· List four places where input can come from.

· Define a data stream.


Input/Output in a C Program

Limiting your program to the use of hard-coded data may be acceptable in some cases, but
generally, you will require some means of obtaining data from sources outside of your program.
This lesson explores some of the more common methods to input data to your program and
output information processed by your program.
Implementation of I/O in the C Language

In the original C language, input/output (I/O) methods were not provided. The development of
I/O functions was left to those who wrote C programs. Because the C language and UNIX
operating system were inextricably linked in their early days of development, many of the I/O
functions that have been developed for C are very UNIX-like.
From the many I/O functions that have been developed, the ANSI standard has recognized and
designated an I/O set.
Because of standardization and the application of a standard set across a broad spectrum of
hardware and system configurations, the standard set of I/O functions and the manner in which
they perform has been kept simple. Many of the specific features that were originally part of the
functions aren't implemented. For instance, in the standard set you won't find graphics functions.
These and other system-specific functions are generally located in libraries provided by the
compiler that you're using.
Input/Output Libraries

C libraries contain code and information linked in and available for use by your program. The
I/O functions and supporting information that you will use in your program are an example of
code contained in these libraries. The ANSI C standard I/O functions are found in the stdio.h
library file. In addition, depending on your compiler, you will find other header files that contain
I/O definitions. Some of these are:

Header File Contents

ctype.h Character I/O functions

conio.h Custom I/O functions

graphics.h Graphic I/O functions

When you become more skilled in using the C language, you can create your own header files
and write your own I/O functions.
The stdio.h File

As mentioned previously, the stdio.h header file contains the ANSI standard I/O functions that
you will need to write C programs. Using stdio.h enables you to ignore the differences that you
encounter from one machine to another. For example, the end of a line may be designated by a
new line, a carriage return plus special characters, or some other character combination. When
you use the I/O facilities in stdio.h, these differences are handled for you, so they're transparent
to you as a programmer.
Some of the stdio.h declarations and functions that you will use in this course are:

· the EOF (End Of File) symbol

· getchar()

· putchar()

· printf()
· scanf()

Depending on the compiler you use, you may or may not need to have the actual statement;

#include <stdio.h>
in your program to link the contents of stdio.h to your program. For this course, however, you
will include the stdio.h statement in the programs that you develop.
I/O Locations
Till now, when you used the printf()function, you were not concerned with where the output
was going. The assumption was that it would go to the screen. And, while you have not yet
addressed the issue, you can assume that input will come from the keyboard. The C language
uses a modular approach to I/O handling. In the following concepts, you will learn how C
handles I/O devices, files, and the flow of data, and treats then in a manner that promotes
modularity and portability.
Input can come from the following places:

· the keyboard (default I/O device)

· a file on disk

· a tape

· other remote media


Output can be directed from the following places:

· the screen (default I/O device)

· a file on disk

· translated into a different form and sent as a signal


Keyboard, Display-screen, and File I/O
Input to a C program can come from the keyboard, a file on a disk, a tape, or another remote
media. Similarly, the output from a program can be directed to the screen, a disk file, or
translated into a different form and sent as signal (such as for turning a motor on and off).
All I/O devices, regardless of type, are treated as files by a C program. The keyboard is treated as
an input file; the screen is an output file.
The keyboard and screen are considered the default I/O devices, or files, by a C program. Each
time the program is started, the default files (keyboard and screen) are opened; when the program
terminates, the I/O files are closed.
Data Streams
To handle the differences in data format coming from different input locations and being sent to
different output locations, C uses the data streams concept. A data stream is exactly that: a flow
of data. The stream can come from different input locations and be directed to different output
locations.
Three data streams are opened when you start a C program:

· The stdin data stream refers to standard input. By default, the keyboard is referred to by
stdin.

· The stdout data stream refers to standard output. By default, the screen is referred to by
stdout.

· The stderr data stream refers to standard error. By default, the screen is referred to by
stderr.
Later in the course, you will learn the actual format of the data streams and how they can be used
with I/O functions to manipulate the flow of data to and from alternate locations. For now, you
need to be familiar only with the basic organization of the C language

Buffered and Unbuffered I/O

When you're working with keyboard input, you will encounter two different types: unbuffered
and buffered.

· If you run a program that immediately echoes each character you type to the screen, and
each character appears immediately, then you're using unbuffered input. For instance, if
you type Welcome to C!, the screen would display:

WWeellccoommee ttoo CC!!

· If no output appears on the screen until you finish typing and press [Enter], then you're
using buffered input.
Buffered Input
Most systems that you will work with will use buffered input. Buffering works by storing
characters in a temporary memory location until a specific signal is sent; for example, the code
issued by pressing the [Enter] key. The buffer is then flushed, and the block of characters is sent
to the program. Buffering has the advantages of:

· Allowing corrections to input before it is transmitted.

· Being more efficient by transmitting characters as a group, rather than individually


processing them.
In your programs, you will generally require buffered input, but there may be times when it is
desirable to use an unbuffered state. For example, when you include the phrase: PRESS ANY
KEY TO CONTINUE in a program, your intentions aren't for the user to press a key and then
have to press [Enter].
Later in this lesson, you will learn about I/O functions that enable you to use an unbuffered input
state
1. What are four places where input can come from?
The keyboard (default I/O device), a file on disk, a tape, and other remote media.
2. What is a data stream?
A data stream is a flow of data. Input streams flow from a data source file to the
program. Output streams flow from the program to an output file. C treats all data sources,
including the keyboard, and output locations, including the screen, as if they were files.
The getchar() and putchar() Functions and Single-Character I/O
Lesson Objectives
In this lesson, you will:

· Use the input functions, getchar( ), getch( ), and getche( ), explain which might or might
not be available in a fully-ANSI-compliant compiler.
The getchar() and putchar() Functions and Single-Character I/O

The ANSI standard getchar()and putchar()functions are the simplest and most basic of
the I/O functions. They are located in the stdio.h file and are available to your program when you
include stdio.h . The getchar()and putchar()functions perform their actions on one
character at a time, getting input from stdin and sending output to stdout. Character-at-a-time I/O
may seem to be of little use; however, in Lesson 4, you will learn about looping and testing.
When loops and tests are used with getchar()and putchar(), you can create dynamic I/O
code that handles multiple combinations of characters and tests for any range of ending values.
The getchar() Input Function

When used in a simple program, as follows:

#include <stdio.h>
main()
{
getchar();
}
the getchar()function will accept any character you enter, including a space, tab, new line, or
special character, and echo it to the screen. The actual value that is returned by the
getchar()function is an integer that represents an ASCII character. Because of the manner in
which the getchar()function operates, you can treat it as if it works with a character. In fact, a
more practical use of getchar()is to have the input assigned to a character variable.

The format of the assignment statement is:

variable = getchar();
A sample program illustrating the use of the assignment statement follows:

#include <stdio.h>

main()
{
char char1;

char1 = getchar();
}
The input character is also assigned to the character variable char1 for further processing.

The putchar() Output Function

The putchar()function outputs a single character to the screen, or stdout. The format of the
putchar()function is:

putchar(variable);
The putchar()function uses an integer representation of a character variable as its argument.
An example of putchar()in a program is:

#include <stdio.h>

main()
{
char char1;

char1 = getchar();
putchar(char1);
}
The output of the sample program consists of the character echoed by getchar()and the
output of putchar(), or another occurrence of the character on the screen. The result of
entering the character "a" at the prompt is:

a
a
If you were to enter a string of characters such as "abc", (allowable with buffered input), the
result is:

abc
a
Only the first character is processed by getchar()and putchar(). The program executes
only once, and the getchar()and putchar()functions execute only once, handling a single
character. If a loop had been used, each of the characters could be processed and output to the
screen.
The following code listing shows the source for the L3-1.c program, which you will examine in
the next task.

#include <stdio.h> /* include files */

main() /* main function */


{
char ch1;

ch1 = getchar(); /* get character from keyboard */


putchar(ch1); /* output the character to the terminal screen */
}
The Problem with getchar()

You have examined how getchar()processes single characters. However, each time you
entered a single character, you had to press [ENTER] to send the contents of the buffer to your
program. What happens to the newline character?
Depending on the compiler you use, one or more characters, including the newline character,
might remain in the input queue after you use getchar(). These characters can cause
problems if your goal is to create an interactive environment. There are methods of working
around this problem, one of which you'll investigate in the following task.
Alternatives to the ANSI Standard Character I/O Functions

Several alternate character I/O functions might be available with your compiler. Unlike the
getchar()function, these aren't defined by the ANSI standard, but are provided with most
compilers.

I/O Function Press [Enter]? Echo to Screen?


getchar() Yes Yes
getch() No No
getche() No Yes

The putch()function operates in a manner similar to putchar().


1. Of the input functions, getchar(), getch(), and getche(), which might or might not be available
in a fully-ANSI-compliant compiler?

Since getch( ) and getche( ) aren't ANSI-standard functions, they might not be available in a
compiler that isn't fully compliant with ANSI specifications.
Formatted Input and the scanf() Function
Lesson Objective
In this lesson, you will:

· Explain what format specifiers can be used in a scanf() call.


The scanf() Input Function

By now, you're fairly familiar with the printf()output function. The complement of the
printf()function is the scanf()input function. In contrast to the character-by-character I/O
functions you have seen in this lesson, the scanf()function allows the input of formatted data
of types:

· character (char)

· integer (int)

· floating point (float)

· arrays of characters (string)


Other types of data can be used with scanf(). Refer to your compiler's documentation for a
complete range of scanf()input types.

Format of the scanf() Function

The scanf()function is similar to printf()except that input is directed by the format


specifiers to the variable arguments for storage in memory. The format of scanf()is :

scanf("control string/format specifiers", variable arguments);


Control String
The control string of scanf()is always enclosed in double quotes. There are many
methods for including control characters within the control string. In this course, you will
concentrate on the format specifiers used within the control string. For additional information,
refer to your compiler's documentation or other reference material.
Format Specifiers
The format specifiers designate the type of data that you will input; as with
printf(), they're preceded by the % symbol. A summary of the primary format specifiers is:

Format Specifier Variable


%c Single character
%d Decimal integer
%f Floating-point number
%s String of characters

Variable Arguments
The variable arguments consist of the variable name preceded by the & character. The &
character, also known as the address operator, refers to the address in memory in which the
information is stored. The concept of passing values by referring to their addresses is an
important one in the C language and will be covered in later in the course.
Character Arrays
Earlier, you learned how to declare and assign character variables. A string, or character array,
is a variable that consists of more than one character. The declaration of the array is similar to
that of a single character, but it includes the length of the array enclosed in brackets. Following is
a comparison of single-character and character-array declarations :

char char1; /* single character declaration */


char arr[20]; /* twenty character array */
The variable arr is a 20-character array, or it can be referred to as a set of 20 characters.

Using the scanf() Function

The following table illustrates some examples of the use of scanf():

Use Result
scanf("%c A single character is stored in the char1 variable location.
",
&char1);

scanf("%d A single integer is stored in the int1 integer variable location.


",
&int1);

scanf("%s A set of characters (that should not exceed the declared array length) is stored
", arr);
in the arr character-array location.

When you use scanf(), make sure you consider the following particulars of that function:

· On its own, the scanf()function does not check to see if the number of characters that
you input exceeds the array length. Characters that exceed the array length are stored in
memory outside of the array, possibly overwriting other data.

· You don't use the & (address operator) character with a character array (a string). The
reason being is that the name of the string variable essentially refers directly to the
address and therefore the address operator is unnecessary.
Input of Multiple Data
The scanf()function provides for input of multiples of the same or different data types at the
command line. Because the scanf()function ignores spaces, tabs, and new lines, you can use
them to separate the multiple input. For example, the code:

scanf("%d%d", &int1, &int2);


accepts input of two integers from the command line. The input must be separated as follows:

312 (space, tab, or new line) 856


The statement:

scanf("%c%d%s", &char1, &int2, arr);


accepts input of a single character, an integer, and a string of characters. Valid input for such a
statement might be:

a 12345 abcdef
where a is the character, 12345 is the integer, and abcdef is the string data.

Avoiding Problems with the newline Character when using scanf()

You might recall the problem with getchar()and the newline character left in the input queue
that gets read by the next getchar(). The scanf()function might, depending on your
system, cause a similar problem. There are two methods of overcoming this problem if you
encounter it:

· Follow the scanf()with an empty, or unassigned, getchar().

· Include the newline character in the double quotes of the scanf()control string, after
the format specifiers, as shown in the following example. (The newline character is read
by scanf(), so the input queue will be cleared.)
scanf("%c%d\n", char1, int1);

The Reality of Using scanf()

The scanf()function represents a versatile method for inputting a variety of data types. Other
features that accompany scanf()enable you to provide formatting, alternate delimiters
between command-line data, and ways to test the success of the actual operation of scanf().
Review your compiler documentation for specifics.
However, there are a number of shortcomings to scanf()and its manner of operation might not
always be what you expect. Because of this, you'll find that it's not used as commonly in
programs as are the other input methods. The more convenient methods, which aren't covered
right here, let you provide ways to input data as a string. In those cases, you would then use other
C-language facilities to manipulate the input data.

1. What format specifiers can you use in a scanf () call?


Any of the format specifiers you use with printf ( ), chiefly %c, %d, %f, and %s.
The Control-Flow Program Statements
Lesson Objectives
In this lesson, you will:

· Define a control-flow statement.

· Define a relational operator.

Control-Flow Statements and the Logical


and Relational Operators

In C, the control-flow statements are sometimes referred to as conditional statements. They


determine the direction that a program takes and are similar to statements you might be familiar
with from other languages. The concept of a control-flow statement is this: if a condition you
specify is true, then perform the specified action or actions. Actions are one or more program
statements associated with the control-flow statement. The format of the control-flow statement
is:
keyword(expression) action;
The expression represents the condition being evaluated. If the statement contains only one
action to perform, there are no begin and end braces, and the statement is concluded with the
semi-colon. If there are multiple actions, the statement is as follows:
keyword(expression)
{

action;
action;
action;
}
The action statements are enclosed in braces, and a semi-colon follows each statement.
Control-flow statements can be divided into two major groups: branches and loops.
The branching statements that you will use in this lesson are:

· The if branching statement

· The if...else branching statement

· The switch branching statement

· The C-language ternary branching statement


Looping statements are also known as iterative statements. In this lesson, you will use the
following loops:

· The for loop

· The while loop

· The do...while loop

Determining Truth in the C Language

In real life, the "search for truth" may be never-ending. However, in the C language it is nowhere
near that difficult. In C, truth is clearly defined and is the basis for determining the outcome of
the test in a statement.
True/False Representation
As is the case for most computer operations, truth is evaluated as a number. In general:

· TRUE is generally represented as 1, but can be any non-zero number.

· FALSE is represented as 0.

In the following statement:

keyword(expr. evaluates to 1) action;


the expression evaluates to TRUE, and the action is performed. In the following statement:

keyword(expr. evaluates to -5) action;


the expression also evaluates to TRUE (negative 5 is non-zero), and the action is performed.
However, when the condition in the statement:

keyword(expr. evaluates to 0) action;


is evaluated to 0, or FALSE, the action isn't performed, and the program goes on to the next
statement.
The basis for determining truth in the conditional statement is generally based on a test of certain
conditions. In order to test a condition, you need a means of comparison, or a way to relate
something to something else. In C, you can do this by using relational and logical operators.
Relational Operators

The relational operators compare their operands on a numeric basis. The relational operators are:

Operator Meaning

< Less than

<= Less than or equal to


== Equal to
> Greater than
>= Greater than or equal to
!= Not equal to

Some examples of the use of relational operators in evaluating a condition are:

keyword(int1 < int2) action;


keyword(int1 >= int2) action;
keyword(int1 == int2) action;
keyword(int1 != int2) action;
The first statement tests for int1 less than int2, the second tests for int1 greater than or
equal to int2, and the third tests for the two integers being equal. Statement 4 is TRUE if
int1 does not equal int2.

Be careful of the following situation:

keyword(int1 = int2) action;


Instead of performing a test, the statement actually assigns the value of int2 to int1, and will
be evaluated to TRUE, as long as int2 is not 0. If int2 is 0, int1 will be assigned the value 0,
and the statement will be evaluated to FALSE.

Evaluating the Truth of an Expression


To illustrate the representation of truth as a number, consider the expression that assigns a value
to an integer x in the following control-flow statement:

keyword(x = (int1 < int2)) action;


If int1 is less than int2, then the expression is TRUE; x will be assigned the value 1. If,
however, int1 is greater than int2, then the expression is FALSE; x will be assigned the value
0. When you're debugging programs, this technique can be useful in determining the results of
complex expressions.
Relational Operators

The relational operators compare their operands on a numeric basis. The relational operators are:

Operator Meaning

< Less than

<= Less than or equal to


== Equal to
> Greater than
>= Greater than or equal to
!= Not equal to

Some examples of the use of relational operators in evaluating a condition are:

keyword(int1 < int2) action;


keyword(int1 >= int2) action;
keyword(int1 == int2) action;
keyword(int1 != int2) action;
The first statement tests for int1 less than int2, the second tests for int1 greater than or
equal to int2, and the third tests for the two integers being equal. Statement 4 is TRUE if
int1 does not equal int2.

Be careful of the following situation:

keyword(int1 = int2) action;


Instead of performing a test, the statement actually assigns the value of int2 to int1, and will
be evaluated to TRUE, as long as int2 is not 0. If int2 is 0, int1 will be assigned the value 0,
and the statement will be evaluated to FALSE.

Evaluating the Truth of an Expression


To illustrate the representation of truth as a number, consider the expression that assigns a value
to an integer x in the following control-flow statement:

keyword(x = (int1 < int2)) action;


If int1 is less than int2, then the expression is TRUE; x will be assigned the value 1. If,
however, int1 is greater than int2, then the expression is FALSE; x will be assigned the value
0. When you're debugging programs, this technique can be useful in determining the results of
complex expressions.
Relational Operators

The relational operators compare their operands on a numeric basis. The relational operators are:

Operator Meaning

< Less than

<= Less than or equal to


== Equal to
> Greater than
>= Greater than or equal to
!= Not equal to

Some examples of the use of relational operators in evaluating a condition are:

keyword(int1 < int2) action;


keyword(int1 >= int2) action;
keyword(int1 == int2) action;
keyword(int1 != int2) action;
The first statement tests for int1 less than int2, the second tests for int1 greater than or
equal to int2, and the third tests for the two integers being equal. Statement 4 is TRUE if
int1 does not equal int2.

Be careful of the following situation:

keyword(int1 = int2) action;


Instead of performing a test, the statement actually assigns the value of int2 to int1, and will
be evaluated to TRUE, as long as int2 is not 0. If int2 is 0, int1 will be assigned the value 0,
and the statement will be evaluated to FALSE.

Evaluating the Truth of an Expression


To illustrate the representation of truth as a number, consider the expression that assigns a value
to an integer x in the following control-flow statement:
keyword(x = (int1 < int2)) action;
If int1 is less than int2, then the expression is TRUE; x will be assigned the value 1. If,
however, int1 is greater than int2, then the expression is FALSE; x will be assigned the value
0. When you're debugging programs, this technique can be useful in determining the results of
complex expressions.
Relational Operators

The relational operators compare their operands on a numeric basis. The relational operators are:

Operator Meaning

< Less than

<= Less than or equal to


== Equal to
> Greater than
>= Greater than or equal to
!= Not equal to

Some examples of the use of relational operators in evaluating a condition are:

keyword(int1 < int2) action;


keyword(int1 >= int2) action;
keyword(int1 == int2) action;
keyword(int1 != int2) action;
The first statement tests for int1 less than int2, the second tests for int1 greater than or
equal to int2, and the third tests for the two integers being equal. Statement 4 is TRUE if
int1 does not equal int2.

Be careful of the following situation:

keyword(int1 = int2) action;


Instead of performing a test, the statement actually assigns the value of int2 to int1, and will
be evaluated to TRUE, as long as int2 is not 0. If int2 is 0, int1 will be assigned the value 0,
and the statement will be evaluated to FALSE.

Evaluating the Truth of an Expression


To illustrate the representation of truth as a number, consider the expression that assigns a value
to an integer x in the following control-flow statement:

keyword(x = (int1 < int2)) action;


If int1 is less than int2, then the expression is TRUE; x will be assigned the value 1. If,
however, int1 is greater than int2, then the expression is FALSE; x will be assigned the value
0. When you're debugging programs, this technique can be useful in determining the results of
complex expressions.
Relational Operators

The relational operators compare their operands on a numeric basis. The relational operators are:

Operator Meaning

< Less than

<= Less than or equal to


== Equal to
> Greater than
>= Greater than or equal to
!= Not equal to

Some examples of the use of relational operators in evaluating a condition are:

keyword(int1 < int2) action;


keyword(int1 >= int2) action;
keyword(int1 == int2) action;
keyword(int1 != int2) action;
The first statement tests for int1 less than int2, the second tests for int1 greater than or
equal to int2, and the third tests for the two integers being equal. Statement 4 is TRUE if
int1 does not equal int2.

Be careful of the following situation:

keyword(int1 = int2) action;


Instead of performing a test, the statement actually assigns the value of int2 to int1, and will
be evaluated to TRUE, as long as int2 is not 0. If int2 is 0, int1 will be assigned the value 0,
and the statement will be evaluated to FALSE.

Evaluating the Truth of an Expression


To illustrate the representation of truth as a number, consider the expression that assigns a value
to an integer x in the following control-flow statement:

keyword(x = (int1 < int2)) action;


If int1 is less than int2, then the expression is TRUE; x will be assigned the value 1. If,
however, int1 is greater than int2, then the expression is FALSE; x will be assigned the value
0. When you're debugging programs, this technique can be useful in determining the results of
complex expressions.
Logical Operators

The logical operators are often used with expressions containing relational operators to evaluate
a conditional test or expression. There are three logical operators in the C language:

Operator Meaning

&& AND

|| OR
! NOT

The AND Operator


The AND operator requires the conditions on both sides of it to be TRUE. For example, the
expression:

(int1 < int2) && (int2 > int3)


requires int1 to be less than int2, and int2 to be greater than int3, for the test to be
TRUE.

The OR Operator
The OR operator requires only one of the conditions on either side of it to be TRUE. For
example, the expression:

(int1 < int2) || (int2 > int3)


requires int1 to be less than int2, or int2 to be greater than int3, for the test to be TRUE.

The NOT Operator


The NOT operator reverses the conditions of its operand. For example, the expression:

!(int1 < int2)


indicates that int1 must be greater than or equal to int2 for the test to be TRUE. Looked at
in a different way, if int1 is less than int2, the result would be TRUE. But, the NOT operator
reverses that value resulting in a final value of FALSE for the expression.

Precedence of Logical and Relational Operators

When you're evaluating the truth of a conditional expression or when you're working with any
expression, the order of execution is paramount. If you don't know the order in which the actions
of the expression occur, it is impossible to determine the correct result. The precedence of the
operators that have been covered in Lessons 1 through 4 are summarized in the following table:

Precedence Operator Associativity

Highest ( ) left to right


! ++ -- + right to left
* / % left to right
+ - left to right
< <= > >= left to right
== != left to right

&& left to right

|| left to right

Lowest = += -= *= /= right to left

The relational operators are in the center of the chart, while the logical! operator receives a high
precedence and the && and || operators are located lower in the chart. The assignment and
assignment-with-operation operators are at the bottom, and are included for comparison with the
relational and logical operators.
Examples of the Logical Operators
Logical operators can be used singly, with two operators:

(expression) && (expression)


in multiples:

((expr) && (expr) && (expr))


or in combination with the other logical operators:

(expr) && (!(expr) || (expr))


Remember to use care with the placement of parentheses. Because they're the highest operators
on the precedence chart, they determine the basis for further evaluation of the expression.
There are numerous forms of evaluations that can take place in the expression of a control-flow
statement. Expressions can even contain assignments, I/O functions, and mathematical
operations. An example of such an expression is:
keyword((char1 = getchar()) != '\n') action;
Some of these will be covered in the course. You'll learn how to use additional implementations,
as you become more proficient in the use of the C language.

Order of Evaluation of the Logical


Operators

The evaluation of expressions that use the logical operators is always from left to right.
Evaluation of the expression is terminated on encountering a portion of the expression that does
not evaluate to the specified condition. For the entire expression to be TRUE in the following
example:

((expr1) && (expr2) && (expr3))


each of the expressions must evaluate to TRUE. If expr1 evaluates to TRUE, expr2 is next.
If expr2 is evaluated to FALSE, the evaluation procedure is terminated, and expr3 isn't
evaluated. In the following example:

((expr1) && (expr2) || (expr3))


expr1 and expr2 need to be TRUE, or expr3 needs to be TRUE. If expr1 and expr2 are
evaluated to FALSE, expr3 won't be evaluated.

The Simple if Statement

Arguably the most commonly used control-flow statement is the if statement. The simplest
form of the if statement contains an expression to be evaluated and a single action to be
performed, if the result of the expression is TRUE. If the expression is evaluated to FALSE, the
action is skipped, and the next program statement is executed. The format of the simple if
statement is:

if(expression) action;
The expression is enclosed in parentheses, and the action is terminated with a semi-
colon.
A slightly more complex if statement involves performing more than one action if the
expression is TRUE. If more then one action is to be performed when the expression is
TRUE, the format is:

if(expression){

action;
action;
action;
}
Beginning and ending braces are used to enclose the action statements. Each action statement is
terminated with a semi-colon. The placement of the beginning and ending braces is optional,
depending on your personal requirements.
The if...else Statement

The simple form of the if statement allows you to either perform or skip an action. The next
form, if...else, allows the choice between two actions, based on the evaluation of an
expression. The format of the if...else statement is:

if(expression)

action;
else
action;
If the expression is TRUE, the first action is performed; otherwise, the action following the "else"
is performed. No braces are required for single statements. However, including braces won't
cause an error; you might want to get in the habit of doing so to facilitate the addition of program
statements in the future. When more than one action is required, the statements must be enclosed
in braces:

if(expression){

action;

action;

action;
} else {

action;

action;

action;
}

Examining and Evaluating Code that Uses the if Statement


Test your knowledge of using the if statement by clicking on the View Code button below to
observe a sample of code. The code will appear in a separate window. You can move the window
and use the scrollbar to view all of the code. While viewing the code, type your answer in the
box provided, then click on the check answer button to compare your answer to ours.
1 /* Examples of code with the "if" statement */ 2 main() /* main function */ 3 { 4 char ch1, ch2;
5 int num1, num2, num3; 6 float real1, real2; 7 8 /* Segment 1 */ 9 ch1 = 'A'; 10 ch2 = 'B'; 11
if( ch1 < ch2) 12 ch1 = ch2; 13 14 /* Segment 2 */ 15 num1 = 10; 16 num2 = 5; 17 num3 = 0; 18
if(( num1 >= num2) && (num2 >= num3)){ 19 num2 = num1; 20 num3 = num2; 21 } else 22
num1 = 0; 23 24 /* Segment 3 */ 25 num1 = 10; 26 num2 = 5; 27 num3 = 10; 28 if((num1 ==
num2) || (num1 == num3)){ 29 num1 = num2++; 30 num2 = num2; 31 } else { 32 ++num3; 33
num1 = ++num3; 34 35 } 36 37 /* Segment 4 */ 38 ch1 = 'A'; 39 num1 = 10; 40 num2 = 3; 41
real1 = num1 % num2; 42 if((ch1 > num1) && (ch1 > num2) && (ch1 > real1)) 43 ch1 = 'B'; 44
else 45 ch1 = 'C'; 46 } /* End of main() */
1. Observe the following table. Complete the values for the Result column in the Check Answer
box below.

Segment Variable Result


_________________________________________________________

1 ch1
2 num1
2 num2
2 num3
3 num1
3 num2
3 num3
4 ch1
__________________________________________________________
Segment Variable Result _________________________________________________________
1 ch1 B 2 num1 10 2 num2 10 2 num3 10 3 num1 5 3 num2 6 3 num3 10 4 ch1 B
__________________________________________________________
More Complex if...else Statements

The if...else statements can be more complex than shown so far. For example, an
if...else statement can contain other if...else statements nested within its code. Or,
you can use compound if...else to create if...else...if statements.

Nested if...else Statements


When you include if or if...else statements within an if...else statement, you're said
to be nesting them. You can even nest more than one type of if or if...else statement
within an outer if...else statement, as is shown in the following code sample:

if(expression){

action;
if(expression) action;
action;

} else {

action;
if(expression){
action;
action;
} else {
action;
action;
}
action;
}
The preceding example contains a simple if statement nested in the first group of program
statements, and an if...else statement following the original else. Nested if...else
statements aren't part of the original if. They are separate program statements that act
independently, and will be executed only when the branch they're in is chosen.
The Compound if...else...if Statement
The compound if...else...if statement differs from the nested if in that it is all
represented as one program statement. The format of the if...else...if statement is:

if(expression)

action;
else
if(expression)
action;
else
if(expression)
action;
The above format is referred to as the if...else...if ladder; or if...else...if stairway. The example
represents only one style format and contains only single statements for each if.

The format for multiple program statements is:

if(expression) {

action;
action;
} else if(expression) {
action;
action;
} else if(expression) {
action;
action;
}
Style Considerations
Indentation and braces become important when you begin including complex statements like
if...else...if in your code. For example, the statement:

if(expression)

if(expression) printf("Hi!");
else if(expression) printf("Bye!");
else printf("Hi! and Bye!");

is a valid C statement. However, it is difficult to read and might lead to logic errors when you try
to interpret the results. After all, which if does each else go with?

The following statement contains exactly the same code (the two blocks are logically identical),
but through better attention to indenting, it is written in a more legible format:

if(expression) {
if(expression) printf("Hi!");
else if(expression)
printf("Bye!");
else
printf("Hi! and Bye!");
}
With the second format, you'll probably find it easier to determine which else goes with which
if. With this second format, you can easily see that each else goes with the immediately-
preceding if. Incidentally, the beginning and ending braces aren't required, but they too help to
create more-readable code.
Thanks to the flexibility of C, you can create valid code that is almost unreadable. Thus, the style
you adopt (and the readability it provides) becomes one of the key elements of writing good C
programs.
The ? or Ternary Operator

The ? (ternary) operator offers you a compact equivalent to an if...else statements. The
ternary operator is somewhat unique to C, being available in C and its derivative languages (C+
+, Java, JavaScript, and so forth) but not many others.
The format of the ternary operator is:

expression1 ? expression2 : expression3;


expression1 is usually a test, while expression2 and expression3 are usually
values that are chosen as a result of the test in expression1. The statement reads: "If
expression1 is TRUE, then do expression2, else do expression3." The easiest way to
understand the ternary operator is with examples. The following code:

if(int1 < int2)


int3 = 100;
else
int3 = 200;
can be written with the ternary operator as follows:

int3 = (int1 < int2) ? 100 : 200;


TIP The parentheses around the (int1 < int2) expression are optional, but make the
: statement clearer.
The statement is read, "If int1 is less than int2, int3 is 100, else int3 is 200." Notice the
placement of the ? operator (after the test) and the : character (separating the quantities that can
be assigned to int3).

What you should also note about the use of the ternary operator in the preceding example is that
it, in its entirety, evaluates to a value. Look at how we assigned the value of the result of the
ternary operator statement (the (int1 < int2) ? 100 : 200 portion of the example) to
the variable int3. You would be right to deduce that this means you can use a ternary operator
wherever an expression is valid. For example, you could use a ternary operator statement as
the expression in an if(expression) statement. This distinction is largely semantics, as
you can always re-write the logic of your program to use an if...else statement in place of
a ternary operator. But C programmers love shortcuts!
The Jump Statements

There are three single-word statements that let you change the direction of your program. They
are break, continue, and goto.

The break Statement


You can use the break statement to terminate loops or the switch statement. The format of
the break statement is:

break;
On encountering the break statement, the program terminates the loop or switch, and the
program resumes at the next following program statement.
The continue Statement
The continue statement is used with loops; its format is:

continue;
When the continue statement is encountered, the program jumps to the beginning of the loop
and resumes execution. The statements in the loop, following continue, aren't executed.

The goto Statement


The goto statement is similar to goto statements in other languages. It causes the program to
resume execution at a location in the function, designated by a label. The format of goto is:

goto label;
/* some amount of code here */

label:
A label is any valid C identifier, followed by the : character. The goto statement must be
contained within a single function.

TIP In general, the use of the goto statement is discouraged, because it causes jumps that
: can render a program difficult to follow and interpret.

The switch Statement

The switch statement allows for multiple branching, based on the test of an expression against
a list of constants. The value being tested can be of the type integer (int) or character (char).
The format of the switch statement is:

switch(value){
case constant1: action;
break;
case constant2: action;
.
.
action;
break;
case constant3: action;
break;
.
.
default: action;
}
The value is compared against each case until a match is found. When a match is made, the
specified actions are performed and the break statement is executed, causing the remainder of
the cases to be ignored and processing to continue at the first executable statement following the
switch statement. If no match is made, the default case is processed. Any actions specified by
the default case are performed, and processing proceeds to the next program statement.
Actually, you don't need to include the break statement, although without it, the logic that gets
executed might not match your expectations. If break isn't present, execution passes to the
next case in the switch statement, even if the value does not meet the criteria of that
following case.
You also don't need to include a default case. If it isn't included and no matching case is
found, execution resumes with the statement following the switch statement.

Testing for Different Data Types


The switch statement can test for integer or character data. An example of a statement that
tests for the integers 1 and 2 follows:

switch(int1){
case 1: action;
break;
case 2: action;
break;
default: action;
}
An example of a statement that tests for the characters A and B is:

switch(char1){
case 'A': action;
break;
case 'B': action;
break;
default: action;
}
Enclose character constants in single quotes. An example of a statement that tests for the
characters A, B, and C, and performs the same action if char1 is A or B, is:

switch(char1){
case 'A':
case 'B': action;
break;
case 'C': action;
break;
default: action;
}
This format can also be used with integer data.

Examining and using the switch Statement


Test your knowledge of the switch statement by clicking on the View Code button below to
observe a sample of code. The code will appear in a separate window. You can move the window
and use the scrollbar to view all of the code. While viewing the code, type your answers in the
boxes provided, then click on the check answer button to compare your answers to ours.
/* Use of the "switch" statement */ main() /* main function */ { char ch1, ch2; int num1,
num2; /* Segment 1 */ ch1 = 'X'; switch(ch1) { case 'A': num1 = 65; break; case 'B': num1 = 66;
break; case 'C': num1 = 67; break; default: num1 = 0; } /* Segment 2 */ num1 = num2 = 10;
switch(num1) { case 0: case 5: num1 += num2; num2 = num1; break; case 10: case 15: num1 -=
num2; num2 = num1; break; default: printf("\nEnd of Program\n"); } /* Segment 3 */ if(ch1 ==
'A' || ch1 == 'B' || ch1 == 'C') printf("\nOne of the first three letters."); else if(ch1 == 'X' || ch1 ==
'Y' || ch1 == 'Z') printf("\nOne of the last three letters."); } /* End of main() */

1. Observe the following table. Complete the values for the Result column in the Check Answer
box below.

Segment Variable Result


________________________________________________

1 num1
2 num1
2 num2
________________________________________________
Segment Variable Result
___________________________________________________

1 num1 0
2 num1 0
2 num2 0
___________________________________________________

2. In the Check Answer box below, write a switch statement to replace the if statement in
Segment 3 of the preceding code listing.

switch(ch1) { case 'A': case 'B': case 'C': printf("\nOne of the first three letters."); break; case 'X':
case 'Y': case 'Z': printf("\nOne of the last three letters."); }
1. What is a control-flow statement?
Control-flow statements are conditional statements with which you can control the logical flow
of a program.
2. What are relational operators?
Relational operators are operators that compare operands (on a numeric basis) and that are used
within control-flow statements.

The Data-Checking Process


Lesson Objectives
In this lesson, you will:
· List three types of problems that you should check for with any user-input data.
The Importance of Data Checking

When writing a program, you'll need to consider what data will be entered and how the user will
be prompted to enter it. Your program should test the data that is input to make sure that none of
it will cause problems or produce unexpected outcomes. For example, in your checkbook
program you prompt the user for information such as name, id, dates, dollar amounts, and
transaction codes. Some possibilities you need to take into consideration are:

· The length of the name that users will input,

· The format of the date, id, and dollar amounts that your program requires and that users
will expect (or want) to use,

· Whether to expect (and handle) upper- or lowercase codes,

· What to do when users enter incorrect codes; and

· How to handle processing problems, such as numbers out of an acceptable range.


The ideal program will produce a concise and clear user interface, handle a wide range of correct
input, and efficiently but clearly handle incorrect input. As they said in "Mission Impossible,"
your mission, if you choose to accept it, is to write such a program. (This course won't self-
destruct in 10 seconds!)
Preventing Your Program from Crashing

First, your program needs to handle information, even incorrectly-entered or out-of-range data,
in a graceful manner, without "crashing" and leaving the user to wonder what he or she did
wrong.
Using the information you have learned so far, you have a basis for developing this type of code.
You can:

· Define the data types needed, declare the proper variables, and write code to correctly
process this information.

· Develop I/O code that will obtain the correct data type from the user.

· Use loops, branches, and test conditions to make sure that the input is of the type
required, and continue to prompt the user for correct input if it is not.
You will probably expend most of your programming effort writing the data-checking and user
interface components of your programs. While this may seem like extra work, your programs
will be considered a success only if they interact with the user in a smooth, concise, and clear
manner, as well as performing the correct behind-the-scenes processing.
. What are three types of problems that you should check for with any user-input data?
That the length is within the range you require and that it won't exceed the capacity of the
variables that you'll use to hold that input. That data is in the format you expect, for example that
users actually input numbers or characters when your program calls for such data. That the data
users enter is logically correct. For example, if you ask them to enter their age, you should check
that it's within a reasonable range (more than 1 and less than 100 might be a valid range).

Using Arrays, Strings, and Pointers in the Checkbook Program


Lesson Objectives
In this lesson, you will:

· Explain how changing program segments to use arrays and pointers can be beneficial.
Working with Larger Program Segments

In larger programs, when you're changing and updating the code, you should follow an orderly
process of evaluating the steps you need to take. Simply going into a program and deleting
program statements, with the intent of replacing them with new code, can cause many
difficulties. The interactions of the code in a C program can extend far beyond the immediate
statements you want to modify. Many programmers have made what they considered to be
successful modifications, only to find after thorough testing that the results that occur in another
portion of the program aren't what they should be.
Organizing Your Work

When you're embarking on the modification of a program, you should follow an orderly process
and some, if not all, of the following procedures:

· Organize and review any documentation that relates to the portion of the code you will
work with.

· Thoroughly test and understand how the program works from the user's standpoint.

· Determine which code segments you need to work with.

· Determine the relationships between those segments and any others connected to them.
After you have performed some or all of the above, you will need to progress further in the
process and begin to work with specific code statements and structures. When you're working
with arrays, pointers, strings, and functions, you need to follow precise steps and document your
work. Some of the steps you might need to use include:

· Examine the function-prototype section for the functions you will be working with.
· Understand the function type and any arguments declared in the prototype.

· Examine the function definition and become familiar with the actions that are performed
in the function.

· Take note of any statements that modify values, particularly those that use pointers.

· Examine the function call and the variables that are passed to the function.

· When you change the program, be sure to document the changes and maintain a backup
copy of the program.
These steps are only a brief introduction to the processes involved in modifying large programs.
There is no real substitute for the trials, tribulations, and success that comes with experience. In
the following tasks, you will be putting many of these steps into practice, working with a version
of the checkbook program that contains arrays, pointers, strings, and string input.

Examining and understanding the Initialize() function in the Checkbook Program


Test your knowledge of the initialize() function in the Checkbook program by clicking on the
View Code button below to observe a sample of code. The code will appear in a separate
window. You can move the window and use the scrollbar to view all of the code. While viewing
the code, type your answers in the boxes provided, then click on the check answer button to
compare your answers to ours.
#include <stdio.h> /* include files */
#include <stdlib.h>

/* constant definitions */
#define END '\0'
#define MAXNAME 19
#define MAXDATE 9

/* function prototypes */
void main(void);
void Initialize(char *name, char *date);
char Print_Menu(void);
void Print_Balance(float *bal_bal);
void Do_Withdrawal(char *date, float with_amount, float *with_bal);
void Do_Check(char *date, float check_amount, float *check_bal);
float Do_Deposit(float dep_amount, float dep_bal);
void Print_Statement(int id, char name[], float start_balance,
char date[], char code, float amount,
float curr_bal);

void main(void) /* main function */


{
/* Variable declarations */
char code,
date[10],
name[20],
tmp[10];
int id;
float amount,
start_balance,
current_balance,
check_amount,
dep_amount,
with_amount;

/* Variable initialization */
code = ' ';
id = 0;
amount = start_balance = current_balance = 0;
check_amount = dep_amount = with_amount = 0;
Initialize(name, date);

/* Get_Info */
/* Program header */
printf("\n\t\tWELCOME TO THE CHECKBOOK BALANCING PROGRAM\n\n");
printf("\n\t\tPlease enter your id : ");
id = atoi(gets(tmp));
printf("\n\t\tPlease enter your name : ");
gets(name);
printf("\n\t\tPlease enter your starting balance : ");
current_balance = atof(gets(tmp));
start_balance = current_balance;

while (code != 'q' && code != 'Q') {


code = Print_Menu();
printf("\n\n\n\n");
switch (code) {
case 'b':
case 'B':
Print_Balance (¤t_balance);
break;
case 'w':
case 'W':
/* Do_Withdrawal */
Do_Withdrawal(date, with_amount, ¤t_balance);
break;
case 'c':
case 'C':
/* Do_Check */
Do_Check(date, check_amount, ¤t_balance);
break;
case 'd':
case 'D':
/* Do_Deposit */
printf("\n\t\tPROCESSING DEPOSIT");
printf("\n\t\tEnter date of deposit: ");
scanf(" %s", &date);
printf("\n\t\tAmount of deposit is : ");
scanf(" %f", &dep_amount);
current_balance = Do_Deposit(dep_amount,
current_balance);
printf("\n\n");
break;
case 'p':
case 'P':
Print_Statement(id, name, start_balance, date,
code, amount, current_balance);
break;
default:
break; /* go back to menu */
} /* end switch */
} /* end while loop */
} /* end main */

/*
* Initialization() (initialize the date and name arrays)
*/
void Initialize(char name[], char date[])
{
int x;
for(x=0; x < MAXNAME; x++) name[x] = ' ';
name[MAXNAME] = END;
for(x=0; x < MAXDATE; x++) date[x] = ' ';
date[MAXDATE] = END;
}

/*
* Print_Menu() (Prints the transaction menu)
*/
char Print_Menu(void)
{
/* Transaction menu */
printf("\n\n\n\n");
printf("\n\t\t\tPLEASE CHOOSE A TRANSACTION.");
printf("\n");
printf("\n\t\t\t Display Balance :B");
printf("\n\t\t\t Write a Check :C");
printf("\n\t\t\t Make a Deposit :D");
printf("\n\t\t\t Make a Withdrawal :W");
printf("\n\t\t\t Print Statement :P");
printf("\n\t\t\t Quit :Q");
printf("\n");
printf("\n\t\t\tENTER YOUR CHOICE :" );
return(getchar());
}

/*
* Print_Balance() (Prints the current balance)
*/
void Print_Balance(float *bal_bal)
{
printf("\n\n\t\tCURRENT BALANCE IS : $%.2f", *bal_bal);
printf("\n\n\t\tPRESS ENTER TO CONTINUE");
getche();
printf("\n\n");
}
/*
* Do_Withdrawal() (Subtracts withdrawal from current balance)
*/
void Do_Withdrawal(char *date, float with_amount, float *with_bal)
{
char n,tmp[10];

printf("\n\t\tWITHDRAWAL TRANSACTION");
printf("\n\t\tDate of Withdrawal : ");
gets(date);
printf("\n\t\tAmount of withdrawal : ");
with_amount = atof(gets(tmp));
/* Check for insufficient funds in account */
if (with_amount > *with_bal) {
printf("\n\n\t\t\t *** INSUFFICIENT FUNDS *** \n\n");
printf("\n\n\t\t\tPRESS ENTER TO CONTINUE");
getche();
} else {
*with_bal = (*with_bal - with_amount);
}
}

/*
* Do_Check() (Subtracts check from current balance)
*/
void Do_Check(char *date, float check_amount, float *check_bal)
{
char tmp[10];

printf("\n\t\tCHECK PROCESSING");
printf("\n\t\tEnter date of check : ");
gets(date);
printf("\n\t\tAmount of check is : ");
check_amount = atof(gets(tmp));
/* Check for bad check */
if (check_amount > *check_bal) {
printf("\n\n\t\t\t *** INSUFFICIENT FUNDS *** \n\n");
printf("\t\t\t$20.00 fee deducted from account.");
*check_bal -= 20;
printf("\n\n\t\t\tPRESS ENTER TO CONTINUE");
getche();
} else {
*check_bal = (*check_bal - check_amount);
}
}

/*
* Do_Deposit() (adds deposits to current balance)
*/
float Do_Deposit(float dep_amount, float dep_bal)
{
dep_bal += dep_amount;
return(dep_bal);
}

/*
* Print_Statement() (prints checkbook statement)
*/
void Print_Statement(int id, char name[], float start_balance, char date[],
char code, float amount, float curr_bal)
{
/* Print_Statement */
printf("\n\t\tSTATEMENT SUMMARY");
printf("\n\t\tCustomer Id : %d",id);
printf("\n\t\tCustomer Name : %s",name);
printf("\n\t\tStarting Balance : $%.2f",start_balance);
printf("\n");
printf("\n\t\tDate\t\tCode\t\tAmount\t\tBalance");
printf("\n\t\t%s\t%c\t %8.2f\t
%10.2f",date,code,amount,curr_bal);
printf("\n");
printf("\n\t\tCurrent Balance : $%.2f",curr_bal);
printf("\n\n\t\t\tPRESS ENTER TO CONTINUE");
getche();
}

What are the types and names of each formal argument in the Initialize() function prototype?

Char has a name of name and char has a name of date.


2. What are the types and names of the actual arguments (variables) passed to Initialize() in the
function call?
A char array has a name of name and a char array has name of date.
3. What actions are performed in the Initialize() function?
The arrays are initialized, in this case filled with space characters.
Mixing Input and Output Functions

In the version of the checkbook program you just worked with, you have a mix of input and
output function types. Because of the peculiarities of some of the I/O functions, they don't
always mix well, and can cause problems in your program. In the following activities, in addition
to continuing to examine the use of pointers and arrays, you will see how some of the I/O
problems can occur, and how they can be avoided by using the gets()string input function.

Examining the Do_Withdrawal() function and the use of Pointers and the gets() string input
Function
Test your knowledge of the Do_Withdrawal() function and the get() string input function by
clicking on the View Code button below to observe a sample of code. The code will appear in a
separate window. You can move the window and use the scrollbar to view all of the code. While
viewing the code, type your answers in the boxes provided, then click on the check answer
button to compare your answers to ours.

#include <stdio.h> /* include files */


#include <stdlib.h>

/* constant definitions */
#define END '\0'
#define MAXNAME 19
#define MAXDATE 9

/* function prototypes */
void main(void);
void Initialize(char *name, char *date);
void Print_Menu(char *code);
void Print_Balance(float *bal_bal);
void Do_Withdrawal(char *date, float with_amount, float *with_bal);
void Do_Check(char *date, float check_amount, float *check_bal);
float Do_Deposit(float dep_amount, float dep_bal);
void Print_Statement(int id, char name[], float start_balance,
char date[], char code, float amount,
float curr_bal);

void main(void) /* main function */


{
/* Variable declarations */
char code,
date[10],
name[20],
tmp[10];
int id;
float amount,
start_balance,
current_balance,
check_amount,
dep_amount,
with_amount;

/* Variable initialization */
code = ' ';
id = 0;
amount = start_balance = current_balance = 0;
check_amount = dep_amount = with_amount = 0;
Initialize(name, date);

/* Get_Info */
/* Program header */
printf("\n\t\tWELCOME TO THE CHECKBOOK BALANCING PROGRAM\n\n");
printf("\n\t\tPlease enter your id : ");
id = atoi(gets(tmp));
printf("\n\t\tPlease enter your name : ");
gets(name);
printf("\n\t\tPlease enter your starting balance : ");
current_balance = atof(gets(tmp));
start_balance = current_balance;

while (code != 'q' && code != 'Q') {


Print_Menu(&code);
switch (code) {
case 'b':
case 'B':
Print_Balance (¤t_balance);
break;
case 'w':
case 'W':
/* Do_Withdrawal */
Do_Withdrawal(date, with_amount, ¤t_balance);
break;
case 'c':
case 'C':
/* Do_Check */
Do_Check(date, check_amount, ¤t_balance);
break;
case 'd':
case 'D':
/* Do_Deposit */
printf("\n\t\tPROCESSING DEPOSIT");
printf("\n\t\tEnter date of deposit: ");
scanf(" %s", &date);
printf("\n\t\tAmount of deposit is : ");
scanf("%f", &dep_amount);
current_balance = Do_Deposit(dep_amount,
current_balance);
printf("\n\n");
break;
case 'p':
case 'P':
Print_Statement(id, name, start_balance, date,
code, amount, current_balance);
break;
default:
break; /* go back to menu */
} /* end switch */
} /* end while loop */
} /* end main */

/*
* Initialization() (initialize the date and name arrays)
*/
void Initialize(char name[], char date[])
{
int x;
for(x=0; x < MAXNAME; x++) name[x] = ' ';
name[MAXNAME] = END;
for(x=0; x < MAXDATE; x++) date[x] = ' ';
date[MAXDATE] = END;
}

/*
* Print_Menu() (Prints the transaction menu)
*/
void Print_Menu(char *code)
{
char tmp[20];

/* Transaction menu */
printf("\n\n\n\n");
printf("\n\t\t\tPLEASE CHOOSE A TRANSACTION.");
printf("\n");
printf("\n\t\t\t Display Balance :B");
printf("\n\t\t\t Write a Check :C");
printf("\n\t\t\t Make a Deposit :D");
printf("\n\t\t\t Make a Withdrawal :W");
printf("\n\t\t\t Print Statement :P");
printf("\n\t\t\t Quit :Q");
printf("\n");
printf("\n\t\t\tENTER YOUR CHOICE :" );
gets(tmp);
*code = tmp[0];
printf("\n\n\n\n");
}

/*
* Print_Balance() (Prints the current balance)
*/
void Print_Balance(float *bal_bal)
{
printf("\n\n\t\tCURRENT BALANCE IS : $%.2f", *bal_bal);
printf("\n\n\t\tPRESS ENTER TO CONTINUE");
getche();
printf("\n\n");
}

/*
* Do_Withdrawal() (Subtracts withdrawal from current balance)
*/
void Do_Withdrawal(char *date, float with_amount, float *with_bal)
{
char n,tmp[10];

printf("\n\t\tWITHDRAWAL TRANSACTION");
printf("\n\t\tDate of Withdrawal : ");
gets(date);
printf("\n\t\tAmount of withdrawal : ");
with_amount = atof(gets(tmp));
/* Check for insufficient funds in account */
if (with_amount > *with_bal) {
printf("\n\n\t\t\t *** INSUFFICIENT FUNDS *** \n\n");
printf("\n\n\t\t\tPRESS ENTER TO CONTINUE");
getche();
} else {
*with_bal = (*with_bal - with_amount);
}
}

/*
* Do_Check() (Subtracts check from current balance)
*/
void Do_Check(char *date, float check_amount, float *check_bal)
{
char tmp[10];

printf("\n\t\tCHECK PROCESSING");
printf("\n\t\tEnter date of check : ");
gets(date);
printf("\n\t\tAmount of check is : ");
check_amount = atof(gets(tmp));
/* Check for bad check */
if (check_amount > *check_bal) {
printf("\n\n\t\t\t *** INSUFFICIENT FUNDS *** \n\n");
printf("\t\t\t$20.00 fee deducted from account.");
*check_bal -= 20;
printf("\n\n\t\t\tPRESS ENTER TO CONTINUE");
getche();
} else {
*check_bal = (*check_bal - check_amount);
}
}

/*
* Do_Deposit() (adds deposits to current balance)
*/
float Do_Deposit(float dep_amount, float dep_bal)
{
dep_bal += dep_amount;
return(dep_bal);
}

/*
* Print_Statement() (prints checkbook statement)
*/
void Print_Statement(int id, char name[], float start_balance, char date[],
char code, float amount, float curr_bal)
{
/* Print_Statement */
printf("\n\t\tSTATEMENT SUMMARY");
printf("\n\t\tCustomer Id : %d",id);
printf("\n\t\tCustomer Name : %s",name);
printf("\n\t\tStarting Balance : $%.2f",start_balance);
printf("\n");
printf("\n\t\tDate\t\tCode\t\tAmount\t\tBalance");
printf("\n\t\t%s\t%c\t %8.2f\t
%10.2f",date,code,amount,curr_bal);
printf("\n");
printf("\n\t\tCurrent Balance : $%.2f", curr_bal);
printf("\n\n\t\t\tPRESS ENTER TO CONTINUE");
getche();
}

. What is the Do_Withdrawal() function type?

void
2. What are the types and names of the formal arguments in the Do_Withdrawal() function
prototype?

Type Name
___________________________________________

char pointer date


float with_amount
float pointer with_bal
___________________________________________

3. What are the types and names of the actual arguments (variables) passed to Do_Withdrawal()
in the function call?

Type Name
___________________________________________

char array date


float with_amount
pointer(address) current_balance
___________________________________________

4. What actions are performed in the Do_Withdrawal() function definition?

The user is prompted for the date and amount of the transaction. Their input is converted to a
float. Then, provided they have a sufficient balance, the transaction amount is subtracted from
their balance.
Examining the Print_Statement() function and the arguments passed to it
Test your knowledge of the Print_Statement() function and passed arguments by clicking on the
View Code button below to observe a sample of code. The code will appear in a separate
window. You can move the window and use the scrollbar to view all of the code. While viewing
the code, type your answers in the boxes provided, then click on the check answer button to
compare your answers to ours.
#include <stdio.h> /* include files */
#include <stdlib.h>

/* constant definitions */
#define END '\0'
#define MAXNAME 19
#define MAXDATE 9

/* function prototypes */
void main(void);
void Initialize(char *name, char *date);
void Print_Menu(char *code);
void Print_Balance(float *bal_bal);
void Do_Withdrawal(char *date, float with_amount, float *with_bal);
void Do_Check(char *date, float check_amount, float *check_bal);
void Do_Deposit(char *date, float dep_amount, float *dep_bal);
void Print_Statement(int id, char name[], float start_balance,
char date[], char code, float amount,
float curr_bal);

void main(void) /* main function */


{
/* Variable declarations */
char code,
date[10],
name[20],
tmp[10];
int id;
float amount,
start_balance,
current_balance,
check_amount,
dep_amount,
with_amount;

/* Variable initialization */
code = ' ';
id = 0;
amount = start_balance = current_balance = 0;
check_amount = dep_amount = with_amount = 0;
Initialize(name, date);

/* Get_Info */
/* Program header */
printf("\n\t\tWELCOME TO THE CHECKBOOK BALANCING PROGRAM\n\n");
printf("\n\t\tPlease enter your id : ");
id = atoi(gets(tmp));
printf("\n\t\tPlease enter your name : ");
gets(name);
printf("\n\t\tPlease enter your starting balance : ");
current_balance = atof(gets(tmp));
start_balance = current_balance;
while (code != 'q' && code != 'Q') {
Print_Menu(&code);
switch (code) {
case 'b':
case 'B':
Print_Balance (¤t_balance);
break;
case 'w':
case 'W':
/* Do_Withdrawal */
Do_Withdrawal(date, with_amount, ¤t_balance);
break;
case 'c':
case 'C':
/* Do_Check */
Do_Check(date, check_amount, ¤t_balance);
break;
case 'd':
case 'D':
/* Do_Deposit */
Do_Deposit(date, dep_amount, ¤t_balance);
break;
case 'p':
case 'P':
Print_Statement(id, name, start_balance, date,
code, amount, current_balance);
break;
default:
break; /* go back to menu */
} /* end switch */
} /* end while loop */
} /* end main */

/*
* Initialization() (initialize the date and name arrays)
*/
void Initialize(char name[], char date[])
{
int x;
for(x=0; x < MAXNAME; x++) name[x] = ' ';
name[MAXNAME] = END;
for(x=0; x < MAXDATE; x++) date[x] = ' ';
date[MAXDATE] = END;
}

/*
* Print_Menu() (Prints the transaction menu)
*/
void Print_Menu(char *code)
{
char tmp[20];

/* Transaction menu */
printf("\n\n\n\n");
printf("\n\t\t\tPLEASE CHOOSE A TRANSACTION.");
printf("\n");
printf("\n\t\t\t Display Balance :B");
printf("\n\t\t\t Write a Check :C");
printf("\n\t\t\t Make a Deposit :D");
printf("\n\t\t\t Make a Withdrawal :W");
printf("\n\t\t\t Print Statement :P");
printf("\n\t\t\t Quit :Q");
printf("\n");
printf("\n\t\t\tENTER YOUR CHOICE :" );
gets(tmp);
*code = tmp[0];
printf("\n\n\n\n");
}

/*
* Print_Balance() (Prints the current balance)
*/
void Print_Balance(float *bal_bal)
{
printf("\n\n\t\tCURRENT BALANCE IS : $%.2f", *bal_bal);
printf("\n\n\t\tPRESS ENTER TO CONTINUE");
getche();
printf("\n\n");
}

/*
* Do_Withdrawal() (Subtracts withdrawal from current balance)
*/
void Do_Withdrawal(char *date, float with_amount, float *with_bal)
{
char n,tmp[10];

printf("\n\t\tWITHDRAWAL TRANSACTION");
printf("\n\t\tDate of Withdrawal : ");
gets(date);
printf("\n\t\tAmount of withdrawal : ");
with_amount = atof(gets(tmp));
/* Check for insufficient funds in account */
if (with_amount > *with_bal) {
printf("\n\n\t\t\t *** INSUFFICIENT FUNDS *** \n\n");
printf("\n\n\t\t\tPRESS ENTER TO CONTINUE");
getche();
} else {
*with_bal = (*with_bal - with_amount);
}
}

/*
* Do_Check() (Subtracts check from current balance)
*/
void Do_Check(char *date, float check_amount, float *check_bal)
{
char tmp[10];
printf("\n\t\tCHECK PROCESSING");
printf("\n\t\tEnter date of check : ");
gets(date);
printf("\n\t\tAmount of check is : ");
check_amount = atof(gets(tmp));
/* Check for bad check */
if (check_amount > *check_bal) {
printf("\n\n\t\t\t *** INSUFFICIENT FUNDS *** \n\n");
printf("\t\t\t$20.00 fee deducted from account.");
*check_bal -= 20;
printf("\n\n\t\t\tPRESS ENTER TO CONTINUE");
getche();
} else {
*check_bal = (*check_bal - check_amount);
}
}

/*
* Do_Deposit() (adds deposits to current balance)
*/
void Do_Deposit(char *date, float dep_amount, float *dep_bal)
{
char tmp[10];

printf("\n\t\tPROCESSING DEPOSIT");
printf("\n\t\tEnter date of deposit: ");
gets(date);
printf("\n\t\tAmount of deposit is : ");
dep_amount = atof(gets(tmp));
*dep_bal += dep_amount;
printf("\n\n");
}

/*
* Print_Statement() (prints checkbook statement)
*/
void Print_Statement(int id, char name[], float start_balance, char date[],
char code, float amount, float curr_bal)
{
/* Print_Statement */
printf("\n\t\tSTATEMENT SUMMARY");
printf("\n\t\tCustomer Id : %d",id);
printf("\n\t\tCustomer Name : %s",name);
printf("\n\t\tStarting Balance : $%.2f",start_balance);
printf("\n");
printf("\n\t\tDate\t\tCode\t\tAmount\t\tBalance");
printf("\n\t\t%s\t%c\t %8.2f\t
%10.2f",date,code,amount,curr_bal);
printf("\n");
printf("\n\t\tCurrent Balance : $%.2f",curr_bal);
printf("\n\n\t\t\tPRESS ENTER TO CONTINUE");
getche();
}
1. To make sure you're working with a fully completed version of the code, open the code.
Examine the Print_Statement() function, and complete the following table:

Function Prototype Function Call


Argument Type Argument Name Argument Type Argument Name
____________________________________________________________________

____________________________________________________________________

Function Prototype Function Call


Argument Type Argument Name Argument Type Argument Name
______________________________________________________________________

int id int id
char array name char array name
float start balance float start balance
char code char code
float amount float amount
float curr_bal float current_balance
______________________________________________________________________

2. Are any of the variable values modified by the Print_Statement() function?


The Print_Statement( ) function produces a display of the last transaction that has taken place.
No processing takes place in the function. The values displayed are passed by value.
. If changing program segments to use arrays and pointers doesn't change the user experience
significantly, why bother changing the code?
You might see increased efficiency and a commensurate improvement in processing time when
you use more efficient and powerful programming techniques. Of course, there's also the pride
that comes from successfully and correctly using the more-powerful features of the programming
language.
Structures
Lesson Objectives
In this lesson, you will:

· Define a structure.

· Explain the "dot" notation and why it is used.


Structures

The C language enables you to create several different custom data types. The most common of
these is the structure. A structure is a collection or grouping of variables, under a name defined
by the programmer. The variables in a structure can be of the same or of different types, but are
usually logically related in some respect. For example, a structure of the user-defined type
customer might contain type char array variables for the name and address, and type float
variables for an account balance or a billing amount.
There are several steps involved in creating and using a structure:
You must create a template, or definition, of what the structure will contain. This template
becomes your new user-defined data type.
As with any of the built-in data types, you must declare variables that will be of your new
data type.
Finally, you need to assign values to and access the variables that make up the structure type.
The variables that comprise the structure are called the members of the structure. Some
references refer to the structure members as elements.
Creating a Structure

To create the new data type (the structure), you need to define a template containing the variables
that make up the type. The format for creating a structure definition is:

struct type_name {

type variable_name;
type variable_name;
type variable_name;
type variable_name;

} structure_variable_name;

· The keyword struct indicates that you are defining a new data-type template.

· The type_name is the user-defined name given to the new data type.

· The statements between the braces are the declarations for the members of the structure.
You'll need one each of these statements for the members of your structure.

· The structure_variable_name is the declaration of the variable that will be of


your custom struct type.

Structure Members
You declare a structure member using the same format as for the declaration of a "normal" C
variable. The type is followed by the variable name, and is terminated with a semi-colon.
The members that make up a structure are usually of the common C data types. However, you
can also use user-defined types (like other structures) as members of your structures. You'll
examine how to do so later in this lesson in Topic 7D, Nested structures.
Considering Structures and their Uses
Test your knowledge of structures by typing your answers in the boxes provided, then click on
the check answer button to compare your answers to ours.
1. Give an example of a structure you might use in one of your programs (note what it would
represent and what its members might be).
Answers will vary, but you might use a structure to represent some real-world data like a
customer, product, or favorite movie. Using the last idea, your favorite movie structure might
have members to represent the title, lead actor, running time, and whether you have a copy in
your library or not.
2. Using the example you considered for the previous question, write the structure definition
statement you would use to create your custom data type.
Using the favorite movie suggestion, you would use:

struct fav_movie {
char title[30];
char lead_actor[30];
char run_time[8]; /* as hh:mm:ss */
bit own;
} my_favorites;
Variations of the Structure Definition and Variable Declaration

If you need only one variable of the structure type, you can omit the type_name from the
definition, as follows:

struct {

type variable_name;
type variable_name;
type variable_name;
type variable_name;

} structure_variable_name;
The opening brace immediately follows the keyword struct, and the name of the structure
variable that implements this custom type follows the closing brace.
If you need more than one variable of the structure type, the type_name parameter is required.
Then, you can list two or more variable names after the ending brace, as follows:

struct type_name {

type variable_name;
type variable_name;
type variable_name;
type variable_name;

} structure_variable_name1, structure_variable_name2;
As before, you put the type_name before the opening brace. Then, you list as many
structure_variable_name# variable names, as you need following the closing brace.

Separating the Structure Definition and Variable Declaration


You can define the structure and declare of variables of the structure type using separate
statements, as follows:

/* define the structure */


struct type_name {

type variable_name;
type variable_name;
type variable_name;
type variable_name;

};

/* now, declare a variable of type type_name */


struct type_namestructure_variable_name;
In this case, the first statement defines the structure; the second statement declares the variables
that are of the defined struct type.
Examples of Creating a Structure
To make all this a little more concrete, let's examine an example of a structure being created and
variables of that type being declared. What follows is the C statement that creates a structure of
the user-defined type-name customer:

struct customer {
char
name[20];
char id[5];
char
state[10];
float
balance;
float
amt_due;
};
This structure contains character-array declarations for the name, id, and state, and floating-
point declarations for the balance and amt_due (amount due). The preceding statement only
defines the structure. No variables of the customer type have been declared yet. To actually
declare a variable of type customer, you would then use a statement like the following:

struct customer fla_cust, ny_cust, pa_cust;


This statement declares three variables, fla_cust, ny_cust, and pa_cust, that are each of
the type customer.
You could have combined those two steps into one statement, as in the following example:

struct customer {

char name[20];
char id[5];
char state[10];
float balance;
float amt_due;

} fla_cust, ny_cust, pa_cust;


In this case, the three structure variables are declared at the end of the definition.
Using the typedef Keyword with a
Structure Definition

As you learned, you can use the typedef keyword to create alternate names for the built in C
data types. You can also use typedef in a structure definition to create an alias for your
structure, too. Doing so makes it a bit easier (and clearer in your code) to later declare variables
of your custom data type.
You would use typedef with a structure definition in this way:

typedef struct{
.
. structure members;
.
} new_name;
Using typedef in this way does not redefine or create a new structure (declare a variable to be
of your custom type). Instead, it defines a new name for the structure. The following example
uses typedef to define a new name for the structure customer, and then declares an
instance of the structure:
typedef struct {
char name[20];
char id[5];
char
state[10];
float balance;
float amt_due;
} customer;

void main(void)
{
customer ny_cust;
.
.
}
Looking at the previous code, it is easy to see that ny_cust is of type customer. Since you
know (and presumably, other programmers looking at your code would know) that customer
is not a default data type, code like this makes it clear that you're using a custom data type.
Initializing Structure Members

As with character strings declared independently of a structure, you can initialize the character
strings that are structure members as part of the structure-variable declaration. Numeric members
of a structure can also be initialized in the declaration. One format for initializing the members of
a structure is:

typedef struct {
char name[20];
char id[5];
char state[10];
float balance;
float amt_due;
} customer;

void main(void)
{
customer fla_cust = {
"John Doe",
"123a6",
"NY",
1000.00,
150.00
};
.
.
}
The structure variable fla_cust initializes name, id, and state to character strings, while
the balance and amt_due members are initialized to floating-point values.

Each member was given its own line for clarity, though you could have put all of the
initialization strung together on a single line.
Referring to Structure Members and Assigning Values to Numeric Structure Members

The members of a structure are referred to by using the dot operator, which is simply the period
character (.). The dot operator is used between the variable name and the member name, as in
the following example:

typedef struct {
char name[20];
char id[5];
char state[10];
float balance;
float amt_due;
} customer;
void main(void)
{
customer fla_cust = {
"John Doe",
"123a6",
"NY",
};
.
.
fla_cust.balance = 2334.66;
fla_cust.amt_due = 100.00;
.
.
}
In this preceding example, the structure's character string name, id, and state members are
initialized in the declaration. Values are assigned the numeric members balance and
amt_due in separate statements that use the dot operator.

You can also use the dot operator to refer to the contents of the structure elements, as in the
following printf() statements:

printf("\nCustomer name : $%s", fla_cust.name);


printf("\nID : $%s", fla_cust.id);
printf("\nState : $%s", fla_cust.state);
printf("\nAmount Due : $%.2f", fla_cust.amt_due);
printf("\nBalance : $%.2f", fla_cust.balance);
If you were to implement these statements, they would produce the following output:

Customer name : John Doe


ID : 123a6
State : NY
Amount Due : $100.00
Balance : $2334.66
Using gets() to Input Character Strings to Structure Members

The string input function gets() is a convenient way to input values to structure members. You
can use gets() as follows to input a character string into a structure member (that is a char
array):

gets(structure_variable_name.member_name);
Be sure to provide enough space in the structure definition for the maximum size of an input
string.
Using gets( ) to Input Values to Numeric Structure Members
You can also use gets() to input numeric values into a numeric structure member. But, you
must translate the input using one of the text-to-numeric conversion functions. You would do so
as shown here (assumes the structure member is of type int or float):

char tmp[size];
.
.
.
structure_variable_name.member_name = atoi(gets(tmp));

/* or */

structure_variable_name.member_name = atof(gets(tmp));
In the preceding example, a character array tmp is created to hold the input string from the
gets()function. The string is converted to the correct data type by using the string-conversion
functions (either atoi() or atof()). The resulting numeric value is then assigned to the
structure member.
Assignment of a Structure to Another Structure

If you're using an ANSI C-compliant compiler, you have the option of directly assigning the
values in a structure to another structure of the same type. Let's say you had created an instance
of the customer structure and called it fla_cust. (In our previous examples, we set values
for fla_cust when we defined it.) If you later declared another structure variable, ny_cust,
you could use the following statement:

ny_cust = fla_cust;
to assign the values "John Doe," "123a6," "NY," 2334.66 and 100.00 to the ny_cust structure
(because those are the values stored in fla_cust according to our previous code examples). If
you aren't using an ANSI C compiler, you might need to initialize or perform each member
assignment separately.

Examining and Understanding code that contains a Structure


Test your knowledge of code that contains a structure by clicking on the View Code button
below to observe a sample of code. The code will appear in a separate window. You can move
the window and use the scrollbar to view all of the code. While viewing the code, type your
answers in the boxes provided, then click on the check answer button to compare your answers
to ours.
#include /* include files */
#include

#define MAX_NAME 20
#define MAX_ID 7
#define MAX_DATE 10

/* function prototypes */
void main(void);

void main(void) /* main function */


{
char tmp[20];

/* structure definition */
struct trans{
char name[MAX_NAME];
char id[MAX_ID];
char date[MAX_DATE];
float amount;
};

/* structure variable declaration */


struct trans transaction;

printf("\n\nEnter name : ");


gets(transaction.name);

printf("\nEnter date : ");


gets(transaction.date);
printf("\nEnter amount : ");
transaction.amount = atof(gets(tmp));

printf("\nCustomer name : %s", transaction.name);


printf("\nID : %s", transaction.id);
printf("\nTransaction date : %s", transaction.date);
printf("\nAmount : $%.2f", transaction.amount);
} /* end main() */

1. What user-defined structure type is created in the program?


.
2. What are the types and names of the structure members?

Type Name
___________________________________________

___________________________________________

Type Name
___________________________________________

char array name{}


char array date{}
float amount
___________________________________________

3. What is the maximum number of characters that the name and date members can hold?
name[ ] 20date[ ] 10.
4. What is the name of the structure variable declared in the program?
.
5. To what values are the structure members initialized?
name[ ] "John Doe"date[ ] "1-25-94"amount 1000.00.
Assignment of a Structure to Another Structure

Examining and Modifying a program that uses gets() to input values to structure members
Test your knowledge of a program that uses gets() to input values by clicking on the View Code
button below to observe a sample of code. The code will appear in a separate window. You can
move the window and use the scrollbar to view all of the code. While viewing the code, type
your answers in the boxes provided, then click on the check answer button to compare your
answers to ours.
#include /* include files */
#include

#define MAX_NAME 20
#define MAX_ID 7
#define MAX_DATE 10

/* function prototypes */
void main(void);

void main(void) /* main function */


{
char tmp[20];

/* structure definition */
struct trans{
char name[MAX_NAME];
char id[MAX_ID];
char date[MAX_DATE];
float amount;
};

/* structure variable declaration */


struct trans transaction;

printf("\n\nEnter name : ");


gets(transaction.name);

printf("\nEnter date : ");


gets(transaction.date);
printf("\nEnter amount : ");
transaction.amount = atof(gets(tmp));

printf("\nCustomer name : %s", transaction.name);


printf("\nID : %s", transaction.id);
printf("\nTransaction date : %s", transaction.date);
printf("\nAmount : $%.2f", transaction.amount);
} /* end main() */

1. What is the name and size of the array that temporarily holds input in the last call to the gets()
function?
The name is tmp and it's size is 20.
2. Is that array adequate to handle the character strings name[], id[], and date[]?
Yes, the largest of those is name[ ], which can store a maximum of 20 characters.
3. What string-conversion function is used in the transaction.amount assignment statement?
atof( )
Assignment of a Structure to Another Structure

Click on the Activity button to learn how to examine and add a structure member.
This concludes your work with the checkbook program, although it is incomplete as compared to
what you experimented with at the beginning of this course. To complete the checkbook program
to match the program you saw in Lesson 1, you'll need to apply techniques that are covered in
the remainder of this lesson. You'll also need to apply the concepts of preprocessor directives and
multi-file compilations, which are not covered in this lesson.
If you decide that you want to finish the checkbook program at a later date and develop a version
that operates in the same manner as the program you tested in Lesson 1, refer to the L1-4.c
source code in the L1 directory of your data disk. This is the final version of the checkbook
program. You can use it as a guide to develop a finished version of the program.
1. What is a structure?
A collection or grouping of variables, under a name defined by the programmer. The variables in
a structure can be of the same or of different types, but are usually logically related in some
respect.
2. What is the "dot" notation and why do you use it?
The "dot" notation describes the use of the dot (the "." or period) operator. You use this operator
to access the values of members of a structure.
Arrays of Structures
Lesson Objectives
In this lesson, you will:

· List the steps for creating an array of structures.


Arrays of Structures

Defining and declaring structure variables is useful when you're working with a limited set of
data. However, you will generally be working with larger sets of data, so you will need structures
that are better suited to handling them.
As you know, you can use arrays to store large amounts of data of a similar type in contiguous
locations in memory. You have also learned in this lesson that you can define a new structure
type to handle related data that might be of different types.
Well, the C language enables you to create arrays of structures that are also stored in contiguous
locations in memory. In fact, the most common usage of structures is the array format. If you
defined a structure to handle customers, as in the example you examined in Topic 7A, you would
most likely have more than one customer in Florida, New York, and Pennsylvania, so you would
need an array of the customer type to handle the information
Creating an Array of Structures

Creating an array of structures is similar to creating any other array type. Two steps are involved:
Define the structure.
Declare one or more array variables of the structure type.
The format for defining and declaring an array of structures is:

struct type_name {

type variable_name;
type variable_name;
type variable_name;
type variable_name;

} structure_variable_name[size];
The number of structures created is represented by size. You can also use two separate
statements to accomplish the same goal:

struct type_name {

type variable_name;
type variable_name;
type variable_name;
type variable_name;

};

struct type_name variable_name[size];


You can even use typedef in yet another variation of the statement, as in:

typedef struct {

type variable_name;
type variable_name;
type variable_name;
type variable_name;
} type_name;

type_name variable_name[size];
Of course, an example will make all this much clearer. In the following example, the code
defines the structure customer and declares three arrays of the type customer. Each array
consists of 100 elements, each of which is a structure:

#include <stdio.h>
#define MAX 100
/* function prototype */
void main(void);

typedef struct {
char name[20];
char id[5];
char state[10];
float balance;
float amt_due;
} customer;

void main(void)
{
customer fla_cust[MAX],
ny_cust[MAX],
pa_cust[MAX];
.
.
}

Referring to a Structure and Its Members


in an Array

Referring to and working with an array of structures and the individual structure members
requires the use of notation similar to the array notation that you use with standard arrays.
Referring to an Individual Structure and Its Members
Following the previous example, where three 100-element arrays of type customer were
declared, the notation to refer to an individual structure in the array is:

array_name[element_number]
For example, to refer to the entire tenth structure in the 100-element array fla_cust, you
would use:

fla_cust[9]
Remember that each member of the array is a structure. So, the preceding notation refers to the
structure as a whole. To refer to a member within an individual structure in the array, the format
is:

array_name[element_number].structure_member_name
The array name is followed by the element number and the name of the member to which you
want to refer. For example,

fla_cust[9].name
refers to the name member, in the tenth structure of the array. That member just so happens to
be a 20-character array itself. In the following example, the notation in the
printf()statements refers to the individual members of the tenth structure in the array:

#include <stdio.h>

#define MAX 100

/* function prototype */
void main(void);

void main(void)
{
struct customer {
char name[20];
char id[5];
char state[10];
float balance;
float amt_due;
};

struct customer fla_cust[MAX],


ny_cust[MAX],
pa_cust;

.
.

printf("\nCustomer Name : %s", fla_cust[9].name);


printf("\nCustomer ID : %s", fla_cust[9].id);
printf("\nAmount Due : $%.2f", fla_cust[9].amt_due);
printf("\nBalance : $%.2f", fla_cust[9].balance);
}
Remember, in the C language, arrays always begin indexing at 0.
Assigning Values to an Array of Structures

You can assign values to the numeric members of a structure element by using array notation.
For example, the statement

fla_cust[9].balance = 1000.00;
assigns the floating-point value 1000.00 to the balance member of the tenth element of
fla_cust.

Using the String Input Function gets() to Assign Values to Array Members
The gets()function is most often used to obtain user input that is assigned to structure
members. In the following example:

gets(fla_cust[9].name);
a character string (the customer name) is assigned to the name member located in the tenth
element of fla_cust. Remember to reserve enough space in member-array declarations to
handle the string input.
In the following statement:

tmp[80];
.
.
fla_cust[9].balance = atof(gets(tmp);
the 80-character array tmp is declared. The array will hold the input from the gets()function.
The input is converted to a floating-point value and is assigned to the balance member of the
tenth element in fla_cust.

Examining and Working with a program that contains an array of Structures


Test your knowledge of a program that contains an array of structures by clicking on the View
Code button below to observe a sample of code. The code will appear in a separate window. You
can move the window and use the scrollbar to view all of the code. While viewing the code, type
your answers in the boxes provided, then click on the check answer button to compare your
answers to ours.

#include <stdio.h> /* include files */


#include <stdlib.h>

#define MAX_NAME 20
#define MAX_DATE 10
#define MAX_STRUCTS 3

/* function prototypes */
void main(void);

void main(void) /* main function */


{
char tmp[20];
int x;

/* structure definition */
struct trans{
char name[MAX_NAME];
char date[MAX_DATE];
float amount;
} transaction[MAX_STRUCTS];
for(x=0; x<MAX_STRUCTS; x++){
printf("\nEnter name : ");
gets(transaction[x].name);
printf("Transaction date : ");
gets(transaction[x].date);
printf("Amount : ");
transaction[x].amount = atof(gets(tmp));
}

for(x=0; x<MAX_STRUCTS; x++){


printf("\n\nCustomer name : %s", transaction[x].name);
printf("\nTransaction date : %s", transaction[x].date);
printf("\nAmount : $%.2f", transaction[x].amount);
}

} /* end main() */

1. What is the name of the variable declared to be an array of structures?

transaction[ ]
2. What is the name of the symbolic constant that designates the size of the array?
MAX_STRUCTS
3. How many structures are in that array?
3

1. What are the steps for creating an array of structures?


First, define the structure. Second, declare one or more array variables of the structure type
Passing Structures to Functions
Lesson Objectives
In this lesson, you will:

· Explain what you do differently to pass a structure member's value to a function when
you want to modify that value than when you want simply to refer to that value.
Passing Structure-Member Values to Functions
You can pass the value of a structure member to a function by using a pass-by-value notation. In
such a situation, you would be using the value of the member without altering its value. When
you're passing a structure member to a function, the types must match in each of the following:

· The structure definition,

· The function prototype,

· The function definition and,

· The function call.


An example of code that passes a structure member to a function is:

1 #include <stdio.h>
2
3 /* function prototype */
4 void main(void);
5 float new_bal(float x, float y);
6
7 typedef struct {
8 char name[20];
9 char id[5];
10 char state[10];
11 float balance;
12 float amt_due;
13 } customer;
14
15 void main(void)
16 {
17 customer fla_cust;
18 .
19 .
20 fla_cust.balance ==>
21 new_bal(fla_cust.balance, fla_cust.amt_due);
22
23 } /* end main() */
24
25 float new_bal(float x, float y)
26 {
27 return(x - y);
28 }
The new_bal()function prototype and definition contain arguments for two floating-point
values. The function call passes the following values:

· The value in fla_cust.balance,and

· The value in fla_cust.amt_due.

The amount due is subtracted from the balance, and the result is passed back by using the
return(x-y)statement. The new balance is then assigned, in main()function, to
fla_cust.balance. The value in fla_cust.balance isn't changed in the
new_bal()function.

To pass a member of a structure that is a character array, the format is the same as when you're
passing an ordinary character array to a function. A character array is specified in the function
prototype and definition, and the name of the array is the argument in the function call. For
example:

1 #include <stdio.h>
2
3 /* function prototype */
4 void main(void);
5 void print_cust(char arr[]);
6 typedef struct {
7 char name[20];
8 char id[5];
9 char state[10];
10 float balance;
11 float amt_due;
12 } customer;
13
14 void main(void)
15 {
16 customer fla_cust;
17 .
18 .
19 print_cust(fla_cust.name);
20 .
21 .
22 } /* end main() */
23
24 void print_cust(char arr[])
25 {
26 printf("\nCustomer name : %s", arr);
27 }
Remember that name of the array is a pointer to the first element in the array.
Passing the Address of a Structure Member to a Function

You can also pass the address of a structure member to a function. You would do this when
you're intent is to alter the value of the structure member. Using the same example as before, to
pass the address of a structure member:

1 #include <stdio.h>
2
3 /* function prototype */
4 void main(void);
5 void new_bal(float *x, float y);
6
7 typedef struct {
8 char name[20];
9 char id[5];
10 char state[10];
11 float balance;
12 float amt_due;
13 } customer;
14
15 void main(void)
16 {
17 customer fla_cust;
18 .
19 .
20 new_bal(&fla_cust.balance, fla_cust.amt_due);
21 .
22 .
23 } /* end main() */
24
25 void new_bal(float *x, float y)
26 {
27 *x = *x - y;
28 }
The new_bal()function prototype and definition now contain a pointer argument into which
the address of fla_cust.balance is placed. The function call now passes the following
values:

· The address of fla_cust.balance,and

· The value in fla_cust.amt_due.

With this example, the value in fla_cust.balance is changed within the


new_bal()function.

Passing an Entire Structure to a Function

Most often, you will define your structures in header files and then include those header files in
your source files that make use of the structure. The reason for this is that the definition of a
structure must appear before the prototypes of any functions that pass or return the structure and
the compiler needs to see the structure type before the structure is used. Putting these definitions
into a header file ensures that your program will meet these requirements because you always put
the statements to include header files at the beginning of your programs.
Whenever you create an instance of a structure, pass a structure into a function, or return a
structure from a function, you can use the keyword struct with the structure name.
Alternatively, you can typedef the structure into a new name and use the name, as in the
following example:

1 #include <stdio.h>
2
3 /* structure definition */
4 typedef struct {
5 char name[20];
6 char id[5];
7 float x;
8 float y;
9 } customer;
10
11 /* function prototypes */
12
13 void main(void);
14 void print_cust(customer);
15
16 void main(void)
17 {
18 customer fla_cust;
19 .
20 .
21 print_cust(fla_cust);
22 .
23 .
24 } /* end main */
25
26 void print_cust(customer cust1)
27 {
28 printf("\nCustomer Name: %s", cust1.name);
29 printf("\nCustomer Id : %s", cust1.id);
30 printf("\nBalance : %.2f", cust1.x);
31 printf("\nAmount Due : %.2f", cust1.y);
32 }
The following example does not use the typedef keyword:

1 #include <stdio.h>
2
3 /* structure definition */
4 struct customer {
5 char name[20];
6 char id[5];
7 float balance;
8 float amt_due;
9 };
10
11 /* function prototypes */
12
13 void main(void);
14 void print_cust(struct customer);
15
16 void main(void)
17 {
18 struct customer fla_cust;
19 .
20 .
21 print_cust(fla_cust);
22 .
23 .
24 } /* end main */
25
26 void print_cust(struct customer cust1)
27 {
28 printf("\nCustomer Name: %s", cust1.name);
29 printf("\nCustomer Id : %s", cust1.id);
30 printf("\nBalance : %.2f", cust1.x);
31 printf("\nAmount Due : %.2f", cust1.y);
32 }
Again, it is typical to declare structure definitions in a header file (outside of all functions) and
include them in source files. The structure definition must come before the function prototypes
that either pass or return the structure.
The Disadvantages of Passing an Entire Structure to a Function

Although you can pass an entire structure to a function, there are drawbacks to the process.

· The members of the structure are accessed by value only. You cannot modify the original
structure members.

· More importantly, when a structure is passed to a function, the overhead involved with
the stack can reduce run-time performance and cause your program to degrade.
Fortunately, C enables you to pass a pointer to a structure to overcome these problems.

Using Structure Pointers

By using a structure pointer that contains the address of a structure, you can access and modify
the members of a structure.
Creating Structure Pointers
You can use pointers to structures, just as you would use pointers to other variable types. The
structure pointer must be declared to be of the structure type you have defined. The format for
creating a pointer to a structure is:

struct structure_type_name *pointer_name;


An example of a statement that creates a pointer to a structure is:

struct customer
{
char name[20];
char id[5];
float balance;
float amt_due;
};

void main(void)
{
struct cust fla_cust, ny_cust, pa_cust;

struct customer *ptr;

.
.

}
The preceding code creates a pointer to a customer type, which initially points to nothing, and
three instances of the customer structure.

Assignment of a Structure Address to a Structure Pointer


The assignment statement for the pointer is similar to other pointer assignments:

ptr = &fla_cust;
This statement places the address of the fla_cust structure in the ptr pointer.

Accessing Structure Members with a


Pointer

To use a pointer to refer to and access the values of structure members, you must use notation
with the arrow operator.
The Arrow Operator
When you're using a pointer to a structure, the arrow operator takes the place of the dot operator.
The arrow operator consists of the hyphen (-) character, followed by the greater-than (>)
character, to form the following sequence: -> . Following the previous example, the following
statements:

printf("\nCustomer name : %s ",ptr->name);


printf("\nAmount due : %.2f",ptr->amt_due);
will print the string in the name member and the floating-point value in the amt_due member
of the fla_cust structure.

Using Structure Pointers to Assign Values to Structure Members

A sample program that assigns values to structure members through the use of a structure pointer
is:

#include <stdio.h>
#include <stdlib.h>
struct customer {
char name[20];
char id[5];
char state[10];
int zip;

/* function prototypes */
void main (void);

void main (void)


{

char tmp[20];

struct customer fla_cust, ny_cust, pa_cust;


struct customer *ptr;

ptr = &fla_cust;

printf("\nEnter user name : ");


gets(ptr->name);

printf("\nEnter user id :");


gets(ptr->id);

printf("\nEnter state : ");


gets(ptr->state);

printf("\nEnter zip code : ");


ptr->zip = atoi(gets(tmp));

}
The arrow operator is used with the pointer ptr to input values to the fla_cust structure
members. An alternate method of placing a value in the structure member character arrays is to
use the string copy function, as follows:

printf("\nEnter user name : ");


gets(tmp);
strcpy(ptr->name,tmp);
Remember, use the dot operator when you're working directly with the structure and the arrow
operator when you're indirectly referring to a structure with a pointer.

1. What do you do differently to pass a structure member's value to a function when you want to
modify that value or when you want simply to refer to that value?
Use the address operator, &, in the function call to modify the member directly. To simply pass
the value of that member to the function, do not use the address operator in the function call. For
example: some_function(my_struct.mem_value); some_function(&my_struct.mem_value).
Nested Structures
Lesson Objectives
In this lesson, you will:

· Define a nested structure.

· Explain how far structures can be nested.


Nested Structures

A nested structure is a structure that is a member in another structure.

Creating Nested Structures

To create a structure that contains another structure as one of its members, you must:

· Define the structure that is to be nested.

· Declare a structure variable of the type to be nested.

· Define the structure that will hold the nested structure.


For example, the following code illustrates the creation of a nested structure:

struct address{
char street[40];
char city[20];
char state[4];
};
struct customer{
char name[20];
struct address addr;
float balance;
float amt_due;
};

void main(void)
{

struct customer fla_cust;


.
.
}
In this preceding example, a new structure, address, is defined. The variable addr of the
structure type address is declared within the customer structure. In the customer-structure
definition, the variable addr is one of the structure members. The definition uses the keyword
struct, followed by the structure type (address) and a structure variable name (addr).
Accessing Nested Structure Members

Using the dot operator between each structure-member notation, as in the following example can
access members of a nested structure:

gets(fla_cust.addr.street);
In this statement, input is obtained from the user and stored in the street member of the
structure nested in the fla_cust variable. Conversely, to print the same member of the nested
structure, the notation of the printf() statement would be:

printf("\nStreet : %s", fla_cust.addr.street);


The ANSI C standard specifies that nested structures can be up to 15 levels deep. Check with
your compiler's documentation to determine how many levels of nesting you can use.

Examining code that Contains a Nested Structure


Test your knowledge of code that contains a nested structure by clicking on the View Code
button below to observe a sample of code. The code will appear in a separate window. You can
move the window and use the scrollbar to view all of the code. While viewing the code, type
your answers in the boxes provided, then click on the check answer button to compare your
answers to ours.
#include <stdio.h> /* include files */
#include <stdlib.h>

/* function prototypes */
void main(void);

void main(void) /* main function */


{
char tmp[20];

struct address{
char city[20];
int zip;
};

struct customer{
char name[20];
struct address addr;
char id[5];
float balance;
float amt_due;
}cust;

printf("Enter name : ");


gets(cust.name);
printf("Enter ID : ");
gets(cust.id);
printf("Enter city : ");
gets(cust.addr.city);
printf("Enter zip code: ");
cust.addr.zip = atoi(gets(tmp));

printf("\nCustomer name : %s",cust.name);


printf("\nCustomer ID : %s",cust.id);
printf("\nCustomer city : %s",cust.addr.city);
printf("\nCustomer zip code : %d",cust.addr.zip);
} /* end main() */

1. There are two structure definitions; what are the names of the defined structure types?
Address and customer.
2. What is the variable name declared for each of the structure types?
addr and cust.
3. What are the types and names of the type struct address members?
The member types are char array and int. The member names are city and zip.
4. What format is used to include the address structure as a member of the customer structure
definition?

The type is struct, the structure name is address, and the variable name is addr.
5. What is the format used in the gets() and printf() statements to refer to the nested structure
members?
cust.addr.city and cust.addr.zip.
Accessing Nested Structure Members
Examining code that Contains a Structure Pointer
Test your knowledge of code that contains a structure pointer by clicking on the View Code
button below to observe a sample of code. The code will appear in a separate window. You can
move the window and use the scrollbar to view all of the code. While viewing the code, type
your answers in the boxes provided, then click on the check answer button to compare your
answers to ours.
#include /* include files */
#include

/* function prototypes */
void main(void);

void main(void) /* main function */


{
char tmp[20];

struct address{
char city[20];
int zip;
};

struct customer{
char name[20];
struct address addr;
char id[5];
float balance;
float amt_due;
}cust;

struct customer *ptr;


ptr = &cust;

printf("Enter name : ");


gets(ptr->name);
printf("Enter ID : ");
gets(ptr->id);
printf("Enter city : ");
gets(ptr->addr.city);
printf("Enter zip code: ");
ptr->addr.zip = atoi(gets(tmp));

printf("\nCustomer name : %s",ptr->name);


printf("\nCustomer ID : %s",ptr->id);
printf("\nCustomer city : %s",ptr->addr.city);
printf("\nCustomer zip code : %d",ptr->addr.zip);
} /* end main() */

1. What is the name of the structure pointer?

ptr.
2. What value is assigned to the pointer?
The address of cust.
3. What is the format used in the gets() and printf() statements to refer to the nested structure
members?
ptr->addr.city and ptr->addr.zip.
1. What is a nested structure?
A nested structure is a structure that is a member of another structure.
2. How many levels can you nest structures?
According to the ANSI specification, you can nest structures up to 15 levels deep.
Dynamic Memory Allocation
Lesson Objectives
In this lesson, you will:

· Define a stack.

· Explain why you would want to allocate and release memory for use by your program.
Memory and Variables

Up to this point in the course, when memory has been needed by a program, it has been set aside
by declaring the desired type of variables. For variables declared in any function, space in
memory is set aside to hold the value assigned to the variable. This memory is reserved until the
function finishes.
Because the function main() is the first function invoked and is resident until the program
terminates, any variables in main() exist for the entire duration of the program. For local
variables, created in a function other than main(), memory is set aside and retained until the
function finishes.
This may not always be the optimal way to allocate memory. Fortunately, you can instead write
your programs obtain memory as they are running. With dynamic memory allocation, memory is
not reserved or set aside at the start of the program; rather, it is allocated on an as-needed basis.
The dynamic memory allocation functions, in conjunction with their pointer arguments, are used
to support many programming structures, such as linked lists and binary trees. The basics of the
memory allocation functions are covered in this topic.
When a program is compiled, much of the memory locations needed for the program to hold
variable and constant data can be determined in advance. As the compiler works through the
main part of the program and each of the other functions, it can figure out what memory will be
needed when the program runs.
When the program is loaded, it can request the needed memory from the operating system before
the program actually begins to run. The operating systems reserve the needed memory locations
by stacking one variable on top of another in memory, in a tight, neat block. Because of the way
this process works, this part of memory is known as the stack. Memory reserved within the stack
cannot be freed up until the program quits running.
Some programs need to use large chunks of memory to hold data, but they only need those
chunks for a short period of time. Rather than tie up all that memory the entire time the program
is running, such programs can temporarily allocate storage locations from another portion of
memory, known as the heap. When the program is done using a particular chunk of heap
memory, it simply tells the operating system that it is done, and the system returns that memory
to the heap, where it can be freely doled out to other needy programs. For best utilization of
memory, clearly having a heap is a good idea.
As the name implies, memory within the heap is not nearly as ordered as that within the stack.
With various programs allocating and deallocating memory from the heap, it quickly becomes a
mess. With little chunks of memory in use in various locations throughout the heap, the heap can
quickly become filled with holes, like Swiss cheese. This makes it difficult to allocate large
blocks of memory from the heap when they are needed. To solve this problem, operating systems
periodically perform the task of "compacting the heap," in which allocated memory is shuffled
around to free up large blocks as much as possible.
The two most important functions involved in the dynamic allocation process are malloc()and
free().

· malloc()allocates memory.

· free()releases memory when it is no longer needed.

These functions reside in the stdlib.h header file. The following include statement might be
needed to use the functions:

#include <stdlib.h>

The malloc() Function

To use malloc(), you pass it the size of the memory you need as an argument and it returns a
pointer to the beginning location of that block of memory. The generic format for using the
malloc()function is:

(void *)malloc(size_needed);
malloc()returns a generic pointer. Therefore, you should write the statement so that the return
value is cast to the type you're working with, which you would do as follows:
(type *)malloc(size_needed);
Instead of specifying a particular amount of memory, you can use the sizeof operator to
determine the proper amount of memory based on your particular computer system. For
example, let's say you needed a block of memory to hold 100 integers. You could guess how
much memory is required. But, because the size of an integer varies from system to system, you
might encounter problems when you move your code to another platform. Here's where you can
use the sizeof operator to avoid problems, as shown in the following code:

int *ptr;
ptr = (int *)malloc(100 * sizeof(int));
In this example, an integer pointer is declared and assigned the result of the malloc()
function, which is a pointer to the beginning of the memory block. The cast operator indicates an
integer pointer (int *), and the argument requests 100 integers.

Checking for the Null Pointer


It is possible that the memory in the heap can become full. If this situation occurs, malloc()
returns a null pointer. To avoid problems, you should check for a null pointer whenever you call
malloc(). Such a test might resemble the following:

int *ptr;
ptr = (int *)malloc(100 * sizeof(int));
if(ptr == NULL) {
printf("\nInsufficient Memory");
/* exit gracefully */
}
The integer pointer ptr will contain the address of the first integer in the block of memory or
the NULL value, if there is no memory available.

TIP If a null pointer is returned and you attempt to use it without checking, your system
: is likely to crash.
Accessing the Variables in the Memory Location
Provided malloc()successfully returns a pointer to your new block of memory, you can begin
working with the memory. You use pointer notation to access the variables in the memory block.
To assign a value to the first integer, for example, use the statement:

*ptr = value;
To assign a value to the third integer, use pointer arithmetic, as follows:

*ptr+2 = value;
For example, to sum the first and third elements, use the statement:
sum = *ptr + *(ptr+2);

The free() Function

The free()function is the opposite of malloc(). You use free() to release the memory
you reserved with malloc(). Releasing memory makes it available for other calls.

Using the free( ) Function


The format for using the free()function is:

free(pointer);
where pointer is the return value assigned by malloc().

TIP Any time you are dealing with memory and pointer manipulation you should be
: cautious. For example, be sure to use a valid pointer argument with the free()
function. Strange results might occur if your program attempts to free up memory
that it hadn't allocated.

Using malloc() and free(), PART 1


Test your knowledge of using malloc() and free() by clicking on the View Code button below to
observe a sample of code. The code will appear in a separate window. You can move the window
and use the scrollbar to view all of the code. While viewing the code, type your answers in the
boxes provided, then click on the check answer button to compare your answers to ours.
#include <stdio.h> /* include files */
#include <stdlib.h>

/* function prototypes */
void main(void);

void main(void) /* main function */


{
char tmp[20];
int x;
float sum, *tmp_ptr, *ck_ptr;
if((ck_ptr = (float *)malloc(3 * sizeof(float))) == NULL){
printf("\nInsufficient Memory for request");
exit(1);
};

sum = 0;
tmp_ptr = ck_ptr;
for(x=0; x<3; x++){
printf("Enter check no. %d amount: ", x+1);
*tmp_ptr = atof(gets(tmp));
sum += *tmp_ptr;
tmp_ptr = tmp_ptr+1;
}

printf("\nThe sum of the checks is $%.2f.", sum);

free(ck_ptr);
} /* end main() */

1. What are the types and names of the pointers in the code?
_ptr and ck_ptr, each is a pointer to a float.
2. What is the name of the pointer assigned the return value of malloc()?
ck_ptr
3. What data-type argument is passed to malloc()?
.
4. How many floating-point locations will be reserved in memory by malloc()?
times the size of a float on the platform on which this program gets compiled.
5. What argument is passed to the free() function?
ck_ptr

The free() Function

Using malloc() and free(), PART 2


Test your knowledge of using malloc() and free() by clicking on the View Code button below to
observe a sample of code. The code will appear in a separate window. You can move the window
and use the scrollbar to view all of the code. While viewing the code, type your answers in the
boxes provided, then click on the check answer button to compare your answers to ours.
#include <stdio.h> /* include files */
#include <stdlib.h>

#define MAX_NAME 20
#define MAX_ID 6

/* function prototypes */
void main(void);

void main(void) /* main function */


{
char tmp[20];
struct customer{
char name[MAX_NAME];
char id[MAX_ID];
float balance;
} *cust;

cust = (struct customer *)malloc(sizeof(struct customer));

if(cust == NULL){
printf("\nInsufficient Memory for request");
exit(1);
} else {
printf("Enter name : ");
gets(cust->name);
printf("Enter id : ");
gets(cust->id);
printf("Enter balance : ");
cust->balance = atof(gets(tmp));

printf("\nCustomer name : %s", cust->name);


printf("\nID : %s", cust->id);
printf("\nBalance : $%.2f.", cust->balance);
} /* end if-else */

. What is the cast operator used with malloc()?


(struct customer *)
2. What is the function argument passed to malloc()?
sizeof(struct customer)
3. What type of notation (array or pointer) is used to access members of the structure?
pointer.
4. Could the other notation be used to access structure members? Why or why not?
No, because cust is a pointer to a structure.
5. If you declared a cust1 structure variable as follows, would the cust1 structure be in the same
memory as the structure *cust points to?
struct customer{
char name[MAX_NAME];
char id[MAX_ID];
float balance;
} cust1, *cust;
cust = (struct customer *)malloc(sizeof(struct customer));

No. The cust1 structure would be in the stack, and cust (because it is dynamically allocated
through malloc) would be in the heap.
Using malloc() and free(), PART 3
Test your knowledge of using malloc() and free() by clicking on the View Code button below to
observe a sample of code. The code will appear in a separate window. You can move the window
and use the scrollbar to view all of the code. While viewing the code, type your answers in the
boxes provided, then click on the check answer button to compare your answers to ours.
#include <stdio.h> /* include files */
#include <stdlib.h>

#define MAX_NAME 20
#define MAX_ID 6

/* function prototypes */
void main(void);

void main(void) /* main function */


{
char tmp[20];
int x;
struct customer{
char name[MAX_NAME];
char id[MAX_ID];
float balance;
} *ptr, *cust;

ptr = (struct customer *)malloc(2 * sizeof(struct customer));

if(ptr == NULL){
printf("\nInsufficient Memory for request");
exit(1);
}

cust = ptr;
for(x=0; x<2; x++){
printf("\nEnter name : ");
gets(cust->name);
printf("Enter id : ");
gets(cust->id);
printf("Enter balance : ");
cust->balance = atof(gets(tmp));
cust = cust+sizeof(struct customer);
} /* end for loop */

cust = ptr;
for(x=0; x<2; x++){
printf("\n");
printf("\nThe address of customer %d is %d.", x+1, cust);
printf("\nCustomer name : %s", cust->name);
printf("\nID : %s", cust->id);
printf("\nBalance : $%.2f.", cust->balance);
cust = cust+sizeof(struct customer);
} /* end for loop */

cust = ptr;
free(ptr);
free(cust);
} /* end main() */

1. What are the names of the structure pointers created in the struct customer definition?
ptr and cust.
2. How many structures are dynamically allocated?

2
3. Which pointer is assigned the beginning address of the memory block?
ptr
4. What address is assigned to the *cust pointer?
At the beginning of each for loop, it is assigned the same address as ptr. Then, after each pass
through the loop, it is increased by the size of the structure so that it points to the next structure.
5. Why are two pointers used to refer to the block of structures? (The original address is retained
in ptr, to reset the cust pointer to the beginning of the block.)
The original address is retained in ptr, to reset the cust pointer to the beginning of the block. The
cust pointer is used to move through the block, one record at a time.
6. In the for loop, which statement increments the cust pointer to the next structure?
cust = cust + sizeof(struct customer);
1. What is a stack?
Memory that is obtained by a program just prior to the program running, and released by the
program only when the program has finished running. Variables in the stack are stored together
in a tight, neat block, hence the name stack.
2. Why would you want to bother with allocating and releasing memory for use by your
program?
Dynamic memory allocation enables your program to access blocks of memory as needed,
without tying up memory when it is not needed. Some programs might not be able to anticipate
how many data structures are needed in advance. For example, a program that generates and then
sorts a catalog listing of all of the files on a disk drive would not know how many files were on
the drive until the program was actually running (which is too late to allocate memory from the
stack). Without being able to dynamically allocate memory as needed, the program would need
to reserve, in advance, as much memory as it would ever require in the worst case scenario. Of
course, in any situation other than the worst case, this would be a waste of memory.
The C Preprocessor and the #include and #define Directives
Lesson Objectives
In this lesson, you will:

· Define a preprocessor directive.

· When using the #include directive, explain the difference between enclosing the file's
name in angle brackets and double-quotes.

The Preprocessor

So far in this course, you've used the preprocessor statements #include and #define. These,
and other statements that begin with the # character are preprocessor directives. Preprocessor
directives aren't actually part of the C language; rather, they're instructions to the compiler.
Before the program is compiled, the directives are examined and the specified actions are
performed during the compilation.
Some typical actions indicated by preprocessor directives include:

· The replacement of symbolic notations,

· The inclusion of outside files,

· The definition of error messages; and

· The determination of code segments in the program that will be compiled.


The ANSI C Preprocessor Directives
The ANSI standard defines not only the C language, but also the preprocessor directives that can
be included in a program. The ANSI standard directives include #define, #include, #if,
#else, #elif, #ifdef, #ifndef, #endif, #undef, and a few more that we haven't listed
here. Refer to your reference manuals for the names and implementations of the preprocessor
directives that your compiler supports.
Use of Preprocessor Directives
There are several rules and guidelines for preprocessor directives:

· The directive must begin with the # character.

· A directive can appear anywhere in the program, but some, especially the #include
and #define directives, are usually located at the beginning of the program.

· The directive is in effect from its location in the program to the end of the program.

· A directive is limited to one logical line in length, or until the first new line is
encountered. You can extend it beyond one physical line by using the backslash (\)
character.
The #include Directive

The #include directive indicates to the compiler that it is to look for and read the specified
source-code file and add the file to your program.
Use of the #include Statement
You have been using the #include directive in the previous lessons, and in particular in the
section on the standard library files. There are two formats for use of the #include directive.
The format for the directive that searches a compiler-designated directory uses the angle
brackets:

#include <filename>
The format for searching the current directory for a file uses the double quotes, rather than the
angle brackets:

#include "filename"
On some implementations, it is possible to include a path to the file:

#include "/pathname/filename"
Check your compiler documentation to see if it supports this last convention.
The #define Directive

The #define directive lets you create an identifier that will be replaced with a user definition.
The code created by the #define directive can be referred to as a symbolic constant, a macro
definition, or even a defined constant. The format for the #define directive is:

#define macro_namedefinition
The ANSI standard refers to the identifier as the macro_name. The macro_name will be
replaced by the definition at each occurrence in the program. No semi-colon is used to terminate
the statement.

TIP You don't include semi-colons after preprocessor directives because they're not C
: commands.
Uses of the #define Statement
The directive's definition can consist of a character, a string, a number, or a calculation. The
directive can also be passed a value to be used as an argument. In the previous lessons, you used
the #define directive to hold values to be tested in conditional statements. While it is not
required, the macro_name is generally in capital letters and indicates the nature of the
directive. This improves readability and meaning. For example, in the following directives:

#define TRUE 1
#define FALSE 0
#define MAX_NUM 1000
#define MESG "End of Program"
TRUE is typically defined as being 1, FALSE is 0, a maximum value is defined as 1000, and a
message is defined as the string "End of Program" (the double quotes are part of the message).
Expansion of Macros
During the compilation process, wherever the macro_name appears in the program, it is
replaced with its definition. Given the above #define directives, the C code segment:

if(int1 <= MAX_NUM)


printf("\n%s", MESG);
else
int2 = TRUE;
is expanded during compilation to:

if(int1 <= 1000)


printf("\n%s", "End of Program");
else
int2 = 1;
#define Directives that Perform Calculations

You can also define directives to perform calculations. You can even use previously-defined
macro names in those calculations. In the following example:

#define MAX_ACCTS 4
#define MAX_LOAN_VAL 10000
#define MAX_LOAN MAX_ACCTS*MAX_LOAN_VAL
A banking institution might define the maximum number of accounts a person can have as 4, the
maximum value per account as 10,000, and the maximum total loan value as the number of
accounts times the maximum value per account; in this case, 40,000.
What makes this technique powerful is that by modifying either of the first two definitions, the
third is automatically changed. As such, #define -based calculations provide you with a
simple method for changing programs without searching through the code for references to the
MAX_LOAN value. Any of the standard C mathematic operators can be used in the definition.

Using Arguments with #define Macros

Performing the above type of calculation can be limiting. The data used in those calculations had
to be defined before the directives. You can do one better; you can pass an argument to your
directive to improve its power. The format for passing an argument to a #define directive is:

#define macro_name(variable) definition(variable)


The variable to be passed is enclosed in parentheses in the macro_name and in the
definition. For example, in the following code:

#define PI 3.1415
#define AREA(X) PI*(X*X)
#define PRINT_AR(Y) printf("The area is %f", Y)

void main(void)
{
float radius, area;
.
.
radius = 20.5;
area = AREA(radius);
PRINT_AR(area);
}
The value of PI is defined as 3.1415, and used in the area calculation, while X is used for the
radius passed to the AREA macro.

Expansion of Macros with Arguments


After the preprocessor has finished, the macro-expanded code appears as follows:

#define PI 3.1415
#define AREA(X) PI*(X*X)
#define PRINT_AR(X) printf("The area is %f", X)

void main(void)
{
float radius, area;
.
.
radius = 20.5;
area = 3.1415*(radius*radius);
printf("The area is %f", area);
}
Use of Functions or Macros
With a bit of ingenuity, your #define directives can quickly become complex. Where is the
break point in your decision to use a preprocessor directive or to create a C function? There is no
clear criteria to make this decision. Instead, consider the following:

· When you're using a preprocessor directive, the code that replaces the macro_name is
inserted into the program wherever the directive appears.

· The preprocessor directive generally executes faster than an equivalent function, but you
must consider the overhead of the additional code added to the program.

· With a function, the code exists only in one location; the program transfers control to the
function.

· Because there is overhead involved in maintaining the transfer of control to the function,
generally, execution of a function is slower than that of an equivalent preprocessor
directive.
When you're determining whether to use a function or a directive, also take into consideration the
complexity and number of calls.

TIP When you're developing and using complex macros, the results obtained from the
: use of a macro can be different than you expect. Test complex macros thoroughly
before you include them in the final version of a program.

The #undef Directive

Generally, if you try to redefine a #define symbolic constant or macro, you will receive a
compiler message that indicates a problem. What you need to use is the #undef directive,
which enables you to undefine the macro or symbolic constant. The format for the #undef
directive is:

#undef macro_name
Using the #undef directive enables you to ignore the previous #define definition and, if
desired, redefine the macro_name to a new value. Keep in mind that the macro_name
remains undefined until you redefine it.

1. What is a preprocessor directive?


Statements that are instructions to the compiler, which takes action upon the statements as it's
compiling your program. Preprocessor directives aren't really part of the C language.
2. When using the #include directive, what's the difference between enclosing the file's name
in angle brackets and double-quotes?
When you enclose the file name in angle brackets, you instruct the compiler to look for the file in
a directory designated by the compiler's configuration. When you use the quotes, you tell it to
look in the current directory (where your .c source file is) to find the file to be included.
The Conditional Compilation Directives
Lesson Objectives
In this lesson, you will:

· The #if, #elif, #endif conditional compilation directive is comparable to the C if...else
statement. Explain why the preprocessor directive includes an end-of-block marker (the
#endif) statement whereas the C statement does not.
Conditional Compilation

Conditional compilation directives allow you to specify the selective compilation of your
program's source code. For example, you can use conditional compilation directives when you're
porting code from one system to another or when you're developing software packages that will
be used on different systems. In either example, a conditional compilation directive lets you
specify that some code should run on only a certain platform.

The #if, #endif Sequence

The #if and #endif directives are similar to the C-language if statement. The format for the
#if, #endif sequence is:

#if
expression

.
.

C program statements

.
.
#endif
As with all the preprocessor directives, the #if, #endif sequence must begin with the #
character. Each #if must be terminated with a #endif. If the expression is true, then the
code between the #if and the #endif will be compiled. If the expression is false, the code is
ignored.
The #if Expression
Because the preprocessor evaluates the conditional compilation statements and their expressions
prior to compilation, only defined identifiers and constants can be evaluated. You can't use
program variables in the expression. In the following example:

#define TRUE 1
#define PC 1

#if PC == TRUE
.
C code for
PC;
.
.
#endif
The #if directive checks to see if the system you're using is a PC ("Wintel" personal computer,
as opposed to a UNIX or Macintosh computer). Because TRUE and PC are defined to 1 and are
equal, the C code will be compiled. If you were using a different system, you could define PC to
0, and the code would be ignored.
The #if, #else, #endif Sequence

As with the C-language if statement, you can use an "else" statement to your directives with the
#else directive. The format for the #if, #else, #endif sequence is:

#if
expression

.
C program statements
.
#else
.
C program statements
.
#endif
If the expression is true, the first set of C statements will be compiled and the second set
ignored. If the statement is false, the opposite occurs.
In the following example:

#define TRUE 1
#define PC 0

#if PC == TRUE
.
C code for PC;
.
#else
.
C code for
UNIX;
.
#endif
The symbolic constant PC is defined as 0, so the #else section (the UNIX code), will be
compiled.
The #if, #elif, #endif Sequence

You can add a conditional expression to the "else" code segment by using the #elif directive.
The format for the #if, #elif, #endif sequence is:

#if
expression

.
C program statements
.
#elif expression
.
C program statements
.
#endif
The following example demonstrates the use of this construct:

#define TRUE 1
#define PC 0
#define UNIX 1

#if PC == TRUE
.
C code for PC;
.
#elif UNIX == TRUE
.
C code for UNIX;
.
#endif
Instead of a default to the second C code segment, a specific test is made to determine if the
system is a UNIX system.
Nested and Combined Directives

Just as you can nest the C if statements, you can nest the #if, #else, #elif, and #endif
directives, as in the following example:
#define TRUE 1
#define PC 0
#define UNIX 0
#define X 0
#define Y 0

#if PC == TRUE
.
C code for PC;
.
#elif UNIX == TRUE
.
C code for UNIX;
.
#elif X == TRUE
.
C code for X;
.
#elif Y == TRUE
.
C code for Y;
.
#else
.
C code for default;
.
#endif
Expressions test for PC, UNIX, X, and Y systems. If these aren't true, the code following the
#else directive is compiled. In the above example, PC, UNIX, X, and Y are defined to be 0 (or
false); consequently, the C code for the default is compiled. Expressions can also be nested, as
with the C language if...else...else-if statement.

The #ifdef Directive

The #ifdef directive is a more concise version of the #if sequence. If a macro_name, or
symbolic constant, has been defined, then the code will be compiled. If no definition exists, the
code is ignored. The value of the definition isn't significant. The format for the #ifdef
directive is:

#ifdef macro_name
.
C code
.
#endif
Each #ifdef is terminated with an #endif. An example of the #ifdef directive follows:

#define PC 100

#ifdef PC
.
C code for
PC
.
#endif
In this example, the C code for the PC will be compiled because PC has been defined (though its
value doesn't matter, it happens to be 100). If you had not included the PC definition, the code
would be ignored by the compiler.
The #ifndef Directive

The #ifndef directive is the converse of #ifdef. Code within the #ifndef block is
compiled only if the associated macro_name has not been defined.

In the following example where the PC definition is commented out:

/* #define PC 100 */

#ifndef PC
.
C code for PC
.
#endif
The C code will be compiled. If the comment notation were removed and PC was a valid
#define, the C code in the #ifndef sequence would be ignored.

The #ifdef, #ifndef, #else, #endif Sequence

Just as you can with the #if directive, you can include #else directives with the #ifdef
and #ifndef directives, for example:

/* #define PC 100 */

#ifndef PC
.
C code for UNIX
.
#else
.
C code for PC
.
#endif
In this example, the C code for UNIX block will be compiled. In this case, the PC #define is
commented out, so the #ifndef test is met. Had the PC #define statement not been
commented out, the #else block would have compiled.

As with all of the Boolean relationships, be careful not to confuse the logic when you're using
the #ifdef and #ifndef directives.
1. The #if, #elif, #endif conditional compilation directive is comparable to the C if...else
statement. Why does the preprocessor directive includes an end-of-block marker (the #endif)
statement whereas the C statement does not?
The preprocessor directive syntax does not include the use of the curly brackets to enclose blocks
of commands. Thus, it needs some marker to define the end of the conditional statement. The C
block is clearly ended at the closing curly brace, thus doesn't require an explicit statement to
mark the end of the if statement.
Global Variables and Variable Storage Classes
Lesson Objectives
In this lesson, you will:

· Describe the basic differences between an automatic and external variable.

· Define a register variable.


Global Variables

A global variable is a variable that is accessible from any portion of a program. Global variables
can be of any valid C type, including user-defined structure types. Furthermore, global variables
can be modified by any portion of a program.
The opposite of a global variable is a local variable. A local variable is accessible just within the
function in which it was created. Local variables can also be of any of the C data types, including
the user-defined structure types. The variables you've used so far in this course have all been
local variables.
You should be cautious when you're working with global variables, because:

· They are stored in a fixed region of memory. Once you have declared global variables,
they take up this memory for the entire duration of the program.

· Unless you're careful, it's easy to make unwanted changes in their values. This is because
the values of global variables can be modified at any point in the program.

· A benefit of a structured language is its compartmentalization of actions. Using global


variables diminishes this benefit.
Despite these drawbacks, there are times when you should use global variables. For example,
when you're compiling and linking separate files, you might need to share data among the files.
You can use global variables, but not local variables, to accomplish this.
Global variables represent a specific storage class defined by the C language. Although you
might work with globals and not be aware of their storage class, it is certainly advisable to have
an understanding of the relationships that take place behind the scenes.
Creating Global Variables
You create global variables by placing their declarations outside of any of the program's
functions, including main(). The following is an example of a program with global variable
declarations:

#include <stdio.h>

char ch;
int num1, num2;

/* function prototypes */
void main(void);

void main(void)
{
int x,y;
.
.
program statements
.
.
}
Assigning Values to Global Variables
You can assign values to global variables from any location in main() or any other function
declared in the program. It isn't necessary to pass the variable as an argument or to use a pointer
to the variable's location. The fact that values can be assigned from any location, while
convenient, requires you to take care to avoid placing unwanted values in unexpected locations
The Variable Storage-Class Types

The C language allows you to fine-tune many of the elements of your programs. One tool you
can use for fine-tuning is selecting the storage-class type for a variable. The type of storage class
determines two aspects of the variable:

· The scope, or which functions have access to the variable.

· A variable's lifespan, or extent, or how long a variable will be present in memory during a
program's execution.
Four identifiers or keywords are used to designate storage class:

· auto

· extern

· register

· static

Using the Storage-Class Keywords


You use these keywords with the declaration of the variable. The format is:

storage_classdata_typevariable_name;
For example:

static int num1,


num2;
Creates two type int variables of the static storage class. Use this format with all of the
storage-class keywords. (You will learn about the static storage class specifically later in this
topic.)

Automatic Variables

The variables that you have been working with in this course have been of the automatic storage-
class type.
Scope
Automatic variables have a local scope (they are local variables). They are known only to the
function in which they're declared. Although you can pass their values or addresses to other
functions, you're doing so with an indirect reference.
Creation
Automatic variables are the default variable storage class type in a function. So, you don't need
to use the auto keyword to specify variables to be automatic variables. For example, using the
format:

typevariable_name;
will create an automatic variable. Of course, if you want, you can use the auto keyword to
explicitly designate the automatic class, as follows:

auto typevariable_name;
For example, the following code creates two automatic type int variables:

void main(void)
{
int num1, num2;
.
.
}
To emphasize that they are automatic class variables, you could also write that code like this:

void main(void)
{
auto
int num1, num2;
.
.
}
While in most cases, using the auto keyword is optional, you might need to use it to override a
different type storage-class variable with the same name. This is examined later.
Extent
An automatic variable is created when the function that declares it is called. The variable exists
in memory as long as the function is active. When the function ceases its processing, the variable
disappears and the memory it occupied is available for other use.
Thus, the extent of an automatic variable is the same as the lifespan of the function in which it is
defined.
Initialization
Automatic variables aren't initialized upon their creation. Instead, at first, the values that they
hold after their definition are the residual memory held by their current addresses (whatever
happens to be in that memory location when they're defined). To be sure they hold valid data,
you can initialize automatic variables during their definition, as follows:

int num1 = 0,
num2 = 100;

auto float real1 = .55;

External Variables

External variables are global variables that are defined outside of a function, usually outside of
main() and at the beginning of the program. To use an external variable within your functions,
including main(), you use the extern keyword. Using extern in this way you are said to
be "documenting your use of global variables," which means you're essentially "telling" the
compiler that you're using an external variable and therefore to not create a local (automatic)
variable of the same name within the function.
Scope
An external (or global) variable can be known throughout the program, and in every function,
with the use of the extern keyword, unless it is overridden by another variable with the same
name.

Creation
External variables are defined outside of a function and can be used within a function. The
statement outside of the function that creates the variable is the defining declaration. The
statement inside the function that documents the use of the external class variable is the
referencing declaration. The format for the external defining declaration is:

type variable_name;

some_function()
{
...
}
You don't use the extern keyword in the defining declaration. Then, the format for the
external referencing declaration is:

some_function()
{
extern type variable_name;
...
}
External Variables

The following code defines two external integers and shows the use of both the defining and
referencing declarations.

/* defining declaration */
int num1, num2;

void main(void)
{
/* referencing declarations */
extern int num1;
extern int num2;
.
/* now, the external variables num1 and num2
can be used within main() */
.
}
This code creates two new variables and then documents their use within the function main()
(with the referencing declaration statements). If the extern keyword had been omitted from
the referencing declaration statements, then local automatic integer variables of the same name
would have been declared. In that situation, the external versions of the variables wouldn't be
accessible from within main(), though they wouldn't be affected such an operation. Defining a
local variable with the same name as an external variable is sometimes called masking.
Even more broad-reaching, if you included the referencing declaration in a separate file, then the
scope of the external variable would be extended to other files.
Extent
External variables reside in memory for as long as the program is active, regardless of the
transfer of control between functions. That is a defining characteristic of a global variable. The
space in memory for external variables is set aside at the time the program is compiled.
Initialization
Upon their definition, external variables are initialized to 0. You can initialize external variables
to a value other than 0, as part of their defining declarations, as follows:

int num1 = 100,


num2 = 200;

void main(void)
{
extern int num1;
extern int num2;
.
.
.
}
But, you can't initialize an external variable as part of its referencing declaration, like this:

int num1, num2;

void main(void)
{
extern int num1 = 100;
.
.
.
}
The statement in main()is only a reference to the external variable; it isn't a definition of the
variable.
Static Variables

The static storage class has two purposes, one with automatic variables and another with external
variables. Adding the static keyword with either type of variable changes either the extent or
scope of the variable, as described in the following sections.
Automatic Static Variables
When used with automatic (local) variables, the static keyword changes the extent of the
variable. Normally, automatic variables live as long as the function that creates them is active.
After the function is done, its automatic variables are destroyed and their values are lost.
In this case, the static storage class gives you a way of preserving an automatic variable's value
between function calls. Let's say you had a function that you call repeatedly in your program. By
using a static storage class for a variable within that function, each time you call the function, the
variable will have the last value set by the previous call to that same function.
But, static automatic variables don't have any greater scope than "normal" automatic variables.
Other functions still can't "see" your automatic variable.
To define an automatic variable as static, add the static keyword, as follows:

some_function()
{

statictype variable_name;
...
}
Space for static variables is set aside at the time program is compiled. With the static variable,
the variable is retained from one function call to the next. They are said to have static extent. For
example, in the following code:

void main(void)
{
.
func1();
func1();
}

void func1(void)
{
int num1 = 0;
static int num2 = 0;

num1 += 10;
num2 += 10;
printf("\n The value in num1 is %d.", num1);
printf("\n The value in num2 is %d.", num2);
printf("\n");
}
The output of the two function calls would be:

The value in num1 is 10.


The value in num2 is 10.

The value in num1 is 10.


The value in num2 is 20.
The calculated value of the num2 static variable is retained from the first to the second function
call. The value placed in num1 (in the first call) disappears because it is a "normal" automatic
variable.
External Static Variables
You can also define external static variables. Defining an external variable to be static changes
the scope of the variable, but not its extent.
Normally, external variables can be known to any function in the current file and in other files.
Defining an external variable to be static limits its scope to the file in which it resides. Its value is
no longer available to outside files.
The extent of an external static variable does not change. It is set up when the program is
compiled and stays active through the life of the program, just as is done for a non-static external
variable.
To define an external static variable, use the following format:

static type variable_name;

some_function()
{
...
}
As with "normal" external variables, in most cases you will put the definition outside of all
functions, including main(), and at the beginning of the file.

Register Variables

Registers are special data storage locations inside the CPU itself. Typically, registers are used by
the processor itself (or the operating system) to store data as it's being processed. The CPU can
access values in a register much faster than it can retrieve values from regular memory locations.
C lets you request that your variable be stored in a register instead of a regular memory location.
Defining such a register variable is the equivalent of requesting a "fast" automatic variable.
The term request is used because the use of register variables is based on the availability of a
faster memory location. You can't require that variables be stored in a register. But, if the
operating system allows, that's where your variable will be stored.
Register variables have the same scope and extent, as do automatic variables. They are initialized
and used in the program in the same manner, as are automatic variables. Only the speed at which
they are accessed is different.
Creation
To create a register variable, use the register keyword in your variable declaration, as
follows:

register type variable_name;


You don't have to use the auto keyword in the definition. Register variables are basically a
variety of an automatic variable, not a different kind altogether.
Not all data types can be defined as a register variable. For example, your CPU's registers might
not be large enough to hold a type double value. Refer to your compiler documentation for
specifics on register types.
Examining the use of Global Variables and the Variable Storage Classes
Test your knowledge of global variables and the variable storage classes by clicking on the View
Code button below to observe a sample of code. The code will appear in a separate window. You
can move the window and use the scrollbar to view all of the code. While viewing the code, type
your answer in the box provided, then click on the check answer button to compare your answer
to ours.
#include <stdio.h> /* include files */
#include <stdlib.h>

#define CU_YD 46656


#define LEN 10
#define MAX_COUNT 3

/* function prototypes */
void main(void);
void get_side(void);
void calc_vol(void);
void print_vol(void);

/* global variables */
double side;
double volume;

void main(void) /* main function */


{
register int x;

for(x=0; x<MAX_COUNT; x++){


get_side();
calc_vol();
print_vol();
}
} /* end main() */

void get_side(void)
{
extern double side;
char tmp[LEN];

printf("\n\nPROGRAM TO CALCULATE THE VOLUME OF A CUBE (ENGLISH)");


printf("\nEnter the length of one side of the cube (inches): ");
side = (double)atof(gets(tmp));
}

void calc_vol(void)
{
extern double side;
extern double volume;

volume = (side * side * side);


}

void print_vol(void)
{
extern double volume;
static int count = 0;
double tot_vol = 0;

printf("\nThe volume of the cube is %.2f cubic inches.", volume);


printf("\nThe volume of the cube is %.4f cubic yards.", volume/CU_YD);
count++;
tot_vol += volume;
printf("\nThe number of volumes calculated is %d.", count);
printf("\nThe total of %d cubes is %.2f cubic inches.", count, tot_vol);
}

1. Record the type and storage class of the following variables:

Storage Class Data Type


________________________________________________

tot_vol
side
volume
X
count
________________________________________________
Storage Class Data Type ________________________________________________ tot_vol
local double side external double volume external double X register int count static local int
________________________________________________

1. What is the basic differences between an automatic and external variable?


An automatic variable is a local variable, accessible to only the function that defines it. External
variables are global variables available throughout your program and possibly even to other files.
2. What is a register variable?
A variable whose value you request be stored in the CPU's registers rather than in normal
memory. The operating system may choose another "fast" location instead of a register.

Program Organization and Multi-File Compilation


Lesson Objectives
In this lesson, you will:

· Explain how you might divide your program's code into multiple files and how you might
arrange those divisions
Program Organization
As your programs become larger and more complex, you'll need to pay closer attention to
planning how they're organized. Being structured and modular, the C language enables you to
develop techniques to create efficient and concise programs. When organizing and planning a
program, you should remember that:

· A program should be divided into small, well-defined, and understandable functions. A


general guideline is that a function should not exceed one page in length. Many functions
will be shorter and will consist of only a few lines.

· Each function should handle a single, clearly-defined task.

· Logical units of the program can be and should be organized into separate files. In such
an arrangement, each file can contain several functions, all relating to the specific task at
hand.
In addition to those considerations, you should consider how the following items will affect your
organization of the functions and files of your program:

· The flow of information through a program. Which functions will gather input, process
data, and produce output will determine the organization of the functions in the program.

· The formats of the data that flow through the program. You'll need a clear understanding
of the data formats in your program if it will be subdivided into separate files.

· How errors will be handled and whether error-handling will be required in more than one
location in your program. If data of incorrect format is entered during the input-gathering
process, will you handle the situation in the input function or with a centralized error-
handling function? If an error is encountered during the processing phase, what is the
most graceful and consistent method of recovering from this situation?

· The format of output. If intermediate output is produced during the processing phase, you
should make sure that the format of the data and the user interface is consistent with other
intermediate or final output formats.
The mechanics of working with separate files is the subject of this topic. Although the specific
program-planning process is unique to each project, the formats you should use when you're
working with functions in multiple files is a defined part of the C language. The following
concepts and tasks provide you with the tools to work successfully with the multiple-file-
compilation process.
Compiling Multiple Files

You can divide your programs into multiple modules. You can store those units in separate files
and even compile them individually. This modularization provides you, whether you're working
alone or as part of a programming team, with a way to work on different components of a
program with reduced worries over inadvertently modifying code other than the code you're
specifically working with. Furthermore, each time that you want to test a modification of one of
a program's modules, you'll need to compile only the file you're modifying. Such incremental
compilations can help you avoid a lengthy compilation of an entire program. Of course, you'll
need to test all of the code once you've made the final changes, just to be sure everything works
as intended.
Function Storage Classes

In addition to defining the storage classes for variables, you can define a storage class for your
functions. You can define functions as either static or external. By default, if you don't specify a
storage class, your functions will be of the external storage class.
External Functions
You would use code similar to the following to define a function as an external function:

#include <stdio.h>

/* function prototypes */

void main(void);
float get_data(void);
extern double calc_rad(float radius);

void main(void)
{
.
.
}
All three functions defined in the preceding code sample are external, though only one was
explicitly noted that way (with the extern keyword). As with external variables, you can
access external functions that reside in separate files.
Static Functions
As with external variables, by defining a function to be static you limit its scope to the file it
resides in. Remember, functions are external by default, and therefore you should expect them to
follow the rules for external variables. You could define a static function with code like the
following example:

#include <stdio.h>

/* function prototypes */
void main(void);

static float get_data(void);

extern double calc_area(float radius);

void main(void)
{
.
.
}

static float get_data(void)

{
.
.
}
By placing the static keyword before the get_data() function prototype, you've limited the
scope of get_data() to the current file. Functions that reside in separate files can no longer
access get_data().

The Format for Multiple Compilation of


Files

To consider how you can compile and work with multiple files, let's start with a monolithic
example. In the following example, which is just an expanded version of the preceding code
samples, all of the code is stored within a single file. This program is designed to calculate the
area of a circle:

1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #define PI 3.1415
5
6 /* function prototypes */
7 void main(void);
8 float get_data(void);
9 double calc_area(float rad);
10
11 void main(void)
12 {
13 float radius = 0;
14 double area = 0;
15
16 radius = get_data();
17 area = calc_area(radius);
18 printf("\nThe area is %.4f.", area);
19 }
20
21 float get_data(void)
22 {
23 char tmp[20];
24 float tmp_rad;
25
26 printf("\nEnter the radius of the circle: ");
27 tmp_rad = atof(gets(tmp));
28 return tmp_rad;
29 }
30
31 double calc_area(float rad)
32 {
33 return(double)(PI*(rad*rad));
34 }
With this example code, you might decide to separate this program into three files, as described
in the following table and illustrated in the subsequent figure.

Filename Function

area.c The function main().

get_data.c The function get_data(), which you use to get the radius from the user.

calc_ar.c The function calc_area(), which you use to calculate the area.

Each of the files ends with the .c extension because each file will be compiled as a separate C
program. The following three code listings (included here as figures), provide code listings for
each of these sample files.

1 /* file area.c */
2 #include <stdio.h>
3
4 /* function prototypes */
5 void main(void);
6 extern float get_data(void);
7 extern double calc_area(float rad);
8
9 void main(void)
10 {
11 float radius = 0;
12 double area = 0;
13
14 radius = get_data();
15 area = calc_area(radius);
16 printf("\nThe area is %.4f.", area);
17 }
The file area.c.

1 /* file get_data.c */
2 #include <stdio.h>
3 #include <stdlib.h>
4
5 float get_data(void)
6 {
7 char tmp[20];
8 float tmp_rad;
9
10 printf("\nEnter the radius of the circle: ");
11 tmp_rad = atof(gets(tmp));
12 return(tmp_rad);
13 }

The file get_data.c.


1 /* file calc_area.c */
2 #define PI 3.1415
3
4 double calc_area(float rad)
5 {
6 return(double)(PI*(rad*rad));
7 }
The file calc_area.c.
The area program has been separated into three files: area.c, get_data.c, and calc_area.c. At the
beginning of area.c, the functions get_data()and calc_area() are designated as external,
indicating their residence in a separate file. The #include <stdlib.h> directive is in the
get_data.c file, and the #define PI directive is in the calc_area.c file. In the function call to
calc_area(), the value of radius is passed by value, as an argument.

Passing Data as Arguments to the


Functions in Separate Files

Rather than use the return()statement to send values back to main() function, you can use
pointer arguments. The following version of the area program (shown in three numbered code
listings) illustrates how arguments are used to pass pointer and address arguments to functions in
separate files:

1 /* file area.c */
2 #include <stdio.h>
3
4 /* function prototypes */
5 void main(void);
6 extern void get_data(float *rad);
7 extern void calc_area(float rad, double *ar);
8
9 void main(void)
10 {
11 float radius = 0;
12 double area = 0;
13
14 get_data(&radius);
15 calc_area(radius, &area);
16 printf("\nThe area is %.4f.", area);
17 }

1 /* file get_data.c */
2 #include <stdio.h>
3 #include <stdlib.h>
4
5 void get_data(float *rad)
6 {
7 char tmp[20];
8 float tmp_rad;
9
10 printf("\nEnter the radius of the circle: ");
11 *rad = atof(gets(tmp));
12 }

1 /* file calc_ar.c */
2 #define PI 3.1415
3
4 void calc_area(float rad, double *ar)
5 {
6 *ar = (double)(PI*(rad*rad));
7 }
In this example, pointers are declared in the external functions, and the addresses of radius
and area are passed as arguments.

Sharing Global Data Between Files

Another way you can share data between functions in separate files is to use external variables.
Such a situation is shown in the following figure. Actual code listings for these programs follow
the figure.
Sharing Global Data Between Files

1 /* file area.c */
2 #include <stdio.h>
3
4 /* function prototypes */
5 void main(void);
6 extern void get_data(void);
7 extern void calc_area(void);
8
9 /* defining declarations */
10 float radius;
11 double area;
12 void main(void)
13 {
14
15 get_data();
16 calc_area();
17
18 printf("\nThe area is %.4f.", area);
19 }

1 /* file get_data.c */
2 #include <stdlib.h>
3
4 /* referencing declaration */
5 extern float radius
6
7 void get_data(void)
8 {
9 char tmp[20];
10
11 printf("\nEnter the radius of the circle: ");
12 radius = atof(gets(tmp));
13 }

1 /* file calc_ar.c */
2 #define PI 3.1415
3
4 /* referencing declarations */
5 extern float radius;
6 extern double area;
7
8 void calc_area(void)
9 {
10 area = (double)(PI*(radius*radius));
11 }
For this example, the area.c file contains defining declarations for the variables radius and
area. They aren't preceded by the extern keyword. The get_data.c file contains referencing
declarations for radius, and calc_ar.c contains referencing declarations for radius and
area. They are preceded by the extern keyword.

No arguments are passed to the functions, and the program statements in all of the functions use
the variable names as they appear in the defining declaration.
Based on this example, you may feel that the use of global external variables is a convenient
method for avoiding the use of pointers and indirect references; in some instances, it is the
preferred method. However, consider the ease with which values can be changed, and the
potential for modifying a variable without realizing it. You'll especially want to consider this
when your programs begin to become larger and more complex than the simple example shown
here.

1. Why might you divide your program's code into multiple files and how might you arrange
those divisions?

Typically, files are divided across multiple files to facilitate development and maintenance. By
locating related code into separate files, maintaining a particular aspect of a program involves
working with a smaller, more logically condensed file than if all the code were in one big file.
Also, teams of programmers can more easily work with multiple files than they can with a single
file.
Command-Line Arguments
Lesson Objectives
In this lesson, you will:

· Define the argc argument.

· Define the argv argument


Command-Line Arguments

Generally, the programs that you create will use input from the user, a file, or some other input
location. However, on occasion, you might need to develop a program that accepts input from
the command line. With such programs, typically the user enters program name at the command
line followed by the program arguments, which becomes the input to your program.
With a DOS computer, that might take the appearance of:

C:\> program_name argument1 argument2 [... argument#]


From a UNIX prompt, it might appear as one of the following:

$ program_name argument1 argument2 [... argument#]


# program_name argument1 argument2 [... argument#]
The arguments are generally, but not always, separated by spaces. Check the requirements for
your operating system to determine the appropriate delimiter.
If you've entered operating system commands at the command line, you've probably used this
format. For example, the UNIX command to produce a long listing of a file is:

ls -l filename
In this example, ls is the program's name, and -l and filename are the arguments. If you
had a program named add_nums that added a series of numbers, you could enter them at the
command line, as follows:

add_nums 12 24 36
The numbers 12, 24, and 36 would be the program arguments.
The argc and argv Command-Line
Arguments

You can write your programs to accept command-line arguments by declaring arguments for
main()to handle such command-line input. You do so just as you would when declaring any
argument that you would pass to a function, as part of the function definition. Traditionally, you
use the following names as the function arguments that handle command-line input:
Name Meaning
int argc argument count
char *argv[] argument value

You would include them in main() as follows:

/* function prototype */
void main(int, char *[]);

void main(int argc, char *argv[])


{
...
}
Although argc and argv are the usual names used with command-line input, they're not
required. You can use any equivalent declarations of valid names.
The argc Argument
The argc argument's value represents the number of arguments entered at the command line.
The name of the program is considered an argument, so the value of argc will always be at least
1. If three arguments followed the program name, as follows:

add_nums 12 24 36
the value of argc would be 4.

You can use argc, for example, to confirm that users have entered the command-line
parameters your program requires.
The argv Argument
Knowing that a user has included a command-line argument is nice, but you'll need to know
what they entered if you're going to make use of that data. That's where the argv argument,
char *argv[], comes in. It's a pointer to an array of pointers to character strings, each of
which contains the address of the first element in the string. You leave the braces empty because
the number of arguments is undetermined until the program is started.
As with any array, the first element, argv[0], points to the first character string, which in this
case is the name of the program. The subsequent elements, argv[1], argv[2] through
argv[x], point to arguments that follow the program name.

TIP The maximum number of arguments that can be used with a program will probably
: be determined by your operating system. If this is a consideration, refer to your
system's documentation.
Using Command-Line String Arguments

You use the argc and argv variables in your program in the same way you would use any
variables. There is nothing magical about the way they function. Though, because the program
using command-line arguments relies on input that follows the program name, you should check
that the arguments are present before you attempt to use them. Otherwise, your program may fail
unpleasantly. Usually, you make such checks at the beginning of your program and report back to
the user and exit the program if no arguments are found, as in the following program:

1 #include <stdio.h>
2 #include <stdlib.h>
3 #define NO_ARGS 1
4
5 /* function prototypes */
6 void main(int, char *[]);
7
8 void main(int argc, char *argv[])
9 {
10 int x = 0;
11
12 if(argc == NO_ARGS){
13 printf("\nNo arguments for the %s program", argv[0]);
14 exit(1);
15 }
16 printf("\nThe program name is %s.", argv[0]);
17 for(x=1; x <= argc; x++){
18 printf("Argument %d is %s.",x, argv[x-1]);
19 }
20 }
This program checks to see that there is at least one argument in addition to the program name. If
not, it prints a message to that effect and exits the program. If there is, it prints the name of the
program, argv[0], and then the argument number and the character string of each subsequent
argument.
Although the program name is number 1 in the argument count (argc), it is referred to by
element 0 (argv[0]) in the array of character pointers. The matching element number is one
less than the argument count.
Using Numeric Command-Line Arguments

Command-line arguments are stored as character strings. Numeric values entered are also stored
as strings; as such, they cannot be used in mathematical calculations. To use the arguments as
numeric values, you must make the appropriate conversion to the string. The following example
adds a series of integers entered as arguments and prints the total:

1 #include <stdio.h>
2 #include <stdlib.h>
3 #define NO_ARGS 1
4
5 /* function prototypes */
6 void main(int, char *[]);
7
8 void main(int argc, char *argv[])
9 {
10 int x = 0,
11 total = 0;
12
13 if(argc == NO_ARGS){
14 printf("\nNo arguments for the %s program", argv[0]);
15 exit(1);
16 }
17 for(x=2; x <= argc; x++){
18 total += atoi(argv[x-1]);
19 }
20 printf("\nThe sum of the arguments is %.d.", total);
21 }
The stdlib.h header file has been included to provide access to the string-conversion functions
and to the exit() function. The atoi()string-conversion function converts each argument
following the program name to an integer value. The for loop begins with the argument count
of 2, while the argv element is always one less.

1. What is the argc argument?


The argc argument's value represents the number of arguments entered at the command line.
2. What is the argv argument?
The argv argument is a pointer to an array of pointers to character strings, each of which contains
the address of the first element in the sting.
File Input and Output
Lesson Objectives
In this lesson, you will:

· List the ways you can transfer data by a stream to a memory or file location.

· Explain the difference between low-level I/O facilities and high-level I/O facilities
File Input and Output

Accepting input as command-line arguments is nice, but it is rather lacking. Can you imagine
trying to enter a large data set, say a customer list, at the command line? Not only that, how
would you save results from one session of the program to another? Fortunately, you can access
files with your C programs.
The ANSI standard includes definitions that standardize the procedures you use to access files.
Before you investigate the specific C functions that enable file access, let's take a look at how C
views the concept of a file, the different types of files, and the different levels of file access.
Streams and Files
As you saw when you learned about keyboard input and screen output, a stream is a flow of data
to or from a file. Such a stream of data can be input or output to your programs.
At that time, you also learned that C treats any input source or output (including storage) location
as a file. Officially, according to C, a file is a location at which data resides. A file might be on a
floppy disk, tape, or CD-ROM drive. Devices are also treated as files by the C language, so the
screen, keyboard, and other I/O devices are also represented as files.
With C, file input and output is buffered, meaning your data is held and transferred to the
destination as a batch rather than character-by-character.
The three standard streams in the C language are referred to by the following pointers:

· stdin : standard input stream,

· stdout : standard output stream, and

· stderr : standard error stream.

Generally, standard input reads from the console (the keyboard) while standard output and
standard error write to the console (the screen).
Text and Binary Files
In your programs, you can transfer data by a stream to a memory or file location as either a:

· Text stream or

· Binary stream.
The subsequent file that holds your data will be either a text or binary file.
A binary stream is a series of bytes that represent data. Thus, a binary file is a byte-by-byte
duplication of your original data.
A text stream is a series of characters that can be terminated with the newline character. The
resulting text file, or version of the data that resides in memory, might be different than the
version in the text stream. You can consider a text file to be "customized" to its particular
environment. For example, in C, the null character is the end-of-string terminator. In DOS
systems, the carriage return/line-feed character is used to represent an end-of-line. As a result,
the representation of your data in the text stream in the text file is somewhat different.
Fortunately, these differences are, for the most part, transparent to you as you write your
programs. In some cases, however, these differences might be important, especially when you're
working with significant numeric data that must be represented in its entirety. For example, if
you used a text file to store your numeric data, you might find that because numeric values are
represented as characters they have been truncated and a portion of their value lost. If you used a
binary file instead, with its mirror image of the data stream, those values would be retained.
The stream/file format of transferring data to a file enables you to ignore any conversions of data
that might take place. To you, the changes are transparent and are taken care of by the compiler.
You can, however, specify to work with a file as either a binary or text file. You indicate which
format (text or binary) you want to work with when you open the file.
File-Access Levels

One of the more confusing aspects of working with file I/O is determining which set of file-
access routines to use. In your C programs, you'll have to work with one of two levels of file I/O
functions:

· Low-level I/O facilities and

· High-level I/O facilities.


The low-level I/O facilities are provided by and are specific to the operating-system
environment; the high-level I/O facilities are defined by the ANSI standard for use with the C
language.
The Operating-system File I/O Facilities
The operating system on which you're working provides the set of low-level file I/O procedures.
Those functions are specific to your operating system and might not work correctly if you were
to port your program to a different platform.
Covering the file I/O functions provided by different systems is beyond the scope of this course.
If you need additional information on your system's I/O facilities, refer to your system's
reference manuals.
The ANSI C Standard File I/O Facilities
The C language itself does not contain I/O functions and definitions. Instead, these facilities
reside in the standard library and header files. The file I/O functions and definitions reside in the
standard library stdio.h. To guarantee access to these facilities, your program should contain the
following directive:

#include <stdio.h>
The stdio.h file contains the declarations for the file I/O functions and the macros that are
defined and used with file I/O. Some of the #define directives in stdio.h include:

Name Meaning
FILE Defines file-pointer type.
EOF Defines end-of-file.
SEEK_SET Offset from beginning of a file.
SEEK_CUR Offset from current position.
SEEK_END Offset from end of a file.

File I/O functions generally begin with "f," which should help make it clear to you that they
involve file I/O. Some of the file-function declarations in stdio.h include:

Name Operation
fopen() Open a file.
fclose() Close a file.
fgetc() Read a file character.
fputc() Write a file character.
fgets() Read a file string.
fputs() Write a file string.
fprintf() Print formatted output to a file.
fseek() Go to a specific byte in a file.
rewind() Go to the beginning of a file.
fflush() Flush the buffer.

The preceding list is not exhaustive; additional file I/O directives and functions are contained in
stdio.h. We'll be looking at the more common directives and functions in this lesson.
File Pointers

A file pointer is a pointer variable of the type FILE that you use to direct the actions of the file
functions to a specific file. For example, you'll need to use file pointers to read from and write to
files on disk drives, tape drives, and other devices.
Creating File Pointers
To create a file pointer, follow this syntax:

FILE *pointer_name;
Many programmers use the name fp for their file pointers, like this:

FILE *fp;
Of course, you can use other names as file pointers; for example, if you want to create a pointer
to designate where data will be read from and a pointer you will use to direct where data is
written to, you could use the following declaration:

FILE *in_data, *out_data;


As with any pointer, you must assign it a location. Generally, you'll assign a value to your file
pointer with the file open function, fopen().

Opening and Closing Data Files

The functions you use to open and close files are fopen()and fclose().

The fopen( ) Function


Use the fopen()function to establish a stream and file connection. It returns a pointer to the
file. You'd use it with this syntax:

fp = fopen("filename","mode");
You must enclose the two literals, filename and mode, within double quotes. Obviously,
filename represents the name of the file to open. In addition to providing the value as a
literal, you can use a #define symbolic constant or even a command-line argument (using
argv[]) as the filename argument.

The mode parameter is perhaps less obvious, it indicates the type of operation you'll be
performing on the file. Valid modes of operation include:

Mode Operation

r Open text file for reading.

w Open text file for writing.

a Append to (add to) a text file.

rb Open binary file for reading.

wb Open binary file for writing.

ab Append to binary file.

Actually, you can go a bit further by placing a + sign after any of the modes. Doing so expands
the meaning to mean read or write. For example, the mode:

rb+
indicates the read or write a binary file mode.
As stated before, fopen()returns a pointer, which you should assign to your file pointer
variable, as in the following code snippet:
FILE *fp;

fp = fopen("sample","r");
In this example, a file pointer fp is declared. Then, a stream/file connection to a text file named
sample is established using read mode. A pointer to the file sample is returned and its value
assigned to the fp file pointer.

If the file you specify does not exist, fopen()returns a null value. That means you should
check for a null return value and act accordingly (perhaps to warn the user or even create the
missing file). Consider this code:

FILE *fp;

if((fp = fopen("sample","r")) == NULL){


printf("\nNo file opened.");
exit(1);
}
The fopen()function tries to open a file named sample for a read operation; if successful, a
pointer to that file is returned and its value assigned to fp. If the function is unable to open the
file, a null value is assigned to fp. In this case, the code tests for the null value and if it's found,
a message is issued and the program is exited.
Unlike with read operations, during a write or append operation, if the file doesn't exist, one will
be created automatically.
Opening and Closing Data Files

The fclose( ) Function


Once you're done with a file, you should close it. Closing a file clears any remaining data that
might be held (buffered). You can close files with the fclose()function, which is used with
this format:

fclose(file_pointer);
Use your file pointer as the argument to fclose(), as in the following example:

FILE *fp;

if((fp = fopen("sample","r")) == NULL){


printf("\nNo file opened.");
exit(1);
}
/* program statements */

fclose(fp);
TIP Warning: Failure to properly close the stream/file connection may result in lost data,
: lost files, or other problems with your program.

Reading from a File

Once you have established a stream/file connection and set the mode of operation, you have
several file I/O functions available to perform your programs' processing. With fgetc(), you
can read one character at a time. With fgets(), you can get one line at a time. There are other
functions besides these, but we'll stick to those two for the most part. Check with your compiler's
documentation for information about the other available file I/O functions
The fgetc( ) Function
There are actually two ways you can read data character-by-character from a file, with
getc()and fgetc(). They both perform in a similar manner, though primarily the
fgetc()function will be used in this lesson.

The fgetc()function reads from a file, one character at a time, until the end of the file is
encountered. When the end of file is encountered, fgetc()returns EOF (one of the symbolic
constants defined in stdio.h). The format for fgetc()is:

int_value = fgetc(file_pointer);
You pass the file pointer as the argument to the fgetc()function. As an example, the following
program reads a file, character-by-character, and prints each character to the screen until the end-
of-file (EOF) is encountered:

#include <stdio.h>

/* function prototypes */
void main(void);

void main(void)
{
FILE *fp;

char ch;

if((fp = fopen("sample","r")) == NULL){


printf("\nNo file opened.");
exit(1);
}

while((ch = fgetc(fp)) != EOF)


putchar(ch);

fclose(fp);
}
The file pointer containing the address of the sample file is passed as the argument to fgetc().
Characters are read and printed to the screen until the end-of-file marker is encountered.
Reading from a File

The fgets( ) Function


Reading character-by-character won't always be the most convenient way for you to read data.
Sometimes, reading entire lines will be more appropriate to your needs. For that purpose, you
can use the fgets()function-with it, you can read from a file, one line at a time. The format for
the fgets()function is:

fgets(string_name, length, file_pointer);


fgets()requires three arguments; string_name is the name of a string, declared within the
program, that will hold the string read from the file. length is the length of the string that will
be read. And finally, file_pointer points to the file to read from.

To determine where lines break, fgets()reads in a file until a newline character is encountered
or until it has read a string equal to one character less than the length in the argument list. The
last character of length 's value is left for the new line character added to the end of the string
by fgets().

#define MAX_LEN 80

void main(void)
{

FILE *fp;
char tmp[MAX_LEN];

if((fp = fopen("sample","r")) == NULL){


printf("\nNo file opened.");
exit(1);
}

fgets(tmp, MAX_LEN, fp);


puts(tmp);

fclose(fp);
}
In the preceding example, fgets()will read one line at a time from the file named sample as
long as those lines are 79 characters or less in length. A new line will be appended to the end of
the string, and it will be stored in the tmp[] character array. Each line will then be output to the
screen by usingputs(). Since puts()also appends a new line to the string, the output will
appear double-spaced. This is covered in the next section.
Writing to a File
You can output data to a file in either a character-by-character format or in a string format, just as
you can read with those two methods. Use fputc()to write character-by-character and
fputs()to write line-by-line. Your compiler might support additional commands (like
puts() and putc()). So, like we've said so many times before, check your compiler's
documentation for more information.

TIP Warning: When you're writing to an existing file, the file pointer is set to the
: beginning of the file. If you're not careful, any data in the file will be lost when you
start writing to the file.

The fputc( ) Function


Just as with getc()and fgetc(), there are two functions you can use to write data character-
by-character to a file: putc()and fputc(). They both perform in a similar manner, but
fputc()will be used in this lesson.

The format you use with fputc() is:

fputc(character, file_pointer);
You pass the character to be written and an appropriate file pointer to fputc().

As an illustration, the following program reads a file one character at a time and writes the
character to another file until the end-of-file character (EOF) is encountered in the first file:

#include <stdio.h>
#include <stdlib.h>

/* function prototypes */
void main(void);

void main(void)
{
FILE *file1, *file2;
char ch;

if((file1 = fopen("sample_in","r")) == NULL){


printf("\nNo input file opened.");
exit(1);
}
if((file2 = fopen("sample_out","w")) == NULL){
printf("\nNo output file opened.");
exit(1);
}

while((ch = fgetc(file1)) != NULL)


fputc(ch, file2);

fclose(file1);
fclose(file2);
}
In this example, file pointers are created for the input (sample_in) and output (sample_out) files.
Both files are opened, and a check is made to see that they exist. Data is read from sample_in,
one character at a time, and written to sample_out, one character at a time. The while loop is
terminated when the end-of-file notation, signaled by the NULL character, is encountered.
Finally, the last character is written and both files are closed.
Writing to a File

The fputs( ) Function


You can use fputs()to write data to a file one line at a time. The format for the
fputs()function is:

fputs(string_name, file_pointer);
The arguments you pass to fputs()are the name (address) of the character array that contains
the string to be written and a pointer to the file the string is to be written to. Consider the
following example:

void main(void)
{
FILE *file;
char tmp[80];
if((file = fopen("sample","w")) == NULL){
printf("\nNo file opened.");
exit(1);
}

printf("\nEnter the data to be written to file: ");


while (gets(tmp) != NULL){
strcat(tmp,"\n");
fputs(tmp,file);
printf("\nEnter the data to be written to file: ");
}

fclose(file);
In this example, data is read as a string from the keyboard. The code appends a newline character
to the end of the string, because gets()deletes the new line entered at the keyboard. The string
and new line are written to the file named sample. The input continues until the end-of-file
symbol is encountered.
Using File and Regular String I/O
Functions
In the preceding example, the code we demonstrated appended a newline character to the output
string. But in other examples, we didn't append the character. How do you know when to append
and when not to append a newline character to your strings?
The file I/O functions, gets(), fgets(), puts(), and fputs(), work in similar but not
identical ways. When combining their use, you should be aware of which function appends a
new line, and which function may need to have a new line appended to the string it references.
Following is a summary of the functions and how they handle newline characters at the end of
the string.

TIP Important: If you use the fgets() string function, you should test for the NULL
: character returned by fgets() on encountering EOF. If a newline character does not
end the file, you can get a very long(or infinite) loop.

Function New-line Operation


gets() Discards the newline character.
fgets() Does not discard the newline character.
puts() Appends a new line to the string.
fputs() Does not append a new line to the string.

Because of the different manner in which the functions handle the terminating new line, the
following chart provides you with a guide for when to append a new line or not.

Combination New-line Operation

gets() & puts() No action is needed.

gets() & fputs() Append a new line to the end of the string.

fgets() & puts() Remove a new line.

fgets() & fputs() No action is needed.

The fprintf( ) Function


You have yet another option for writing output to a file, the fprintf()function. With it, you
can write formatted output to a file. fprintf()is similar to printf(),except that it includes
a file-pointer argument to direct its output. The format for the fprintf() function is:

fprintf(file_pointer,"formatted string", arguments);


The first argument you must supply is the file pointer. You follow that with the regular formatted
output and the variables that represent data in the formatted output, just as you would with
printf(). The following sample code prints a formatted string plus the user input to a file:

void main(void)
{
FILE *file;
char tmp[80];
int count = 1;
if((file = fopen("sample","w")) == NULL){
printf("\nNo file opened.");
exit(1);
}

printf("\nEnter line %d: ", count);


while (gets(tmp) != NULL){
fprintf(file,"\nLine %d is: %s", count++, tmp);
printf("\nEnter line %d:", count);
}
fprintf()automatically adds a newline to the end of your string. So, you don't have to take
that step yourself. You might find fprintf()to be the more useful of the string file output
functions, not only because of this newline business, but also because of its similarity with
printf(), which you are probably quite familiar using for screen output.

Moving to the Beginning of a File by Using the rewind() Function

The rewind()function resets the file-position indicator to the beginning of the file without
destroying the file contents. Typically, you would use this function to return to the beginning of a
file to either read data or to re-read data that was already read. The format for the
rewind()function is:

rewind(file_pointer);
Here's an example of using rewind():

1 void main(void)
2 {
3 FILE *file;
4 char tmp[80];
5 int x,
6 count = 1;
7
8 if((file = fopen("sample","w+") == NULL){
9 printf("\nNo file opened.");
10 exit(1);
11
12 printf("\nSIMPLE TEXT PROCESSOR");
13 printf("\nEnter line %d : ", count);
14
15 while (gets(tmp) != NULL){
16 fprintf(file,"\nLine %d is: %s", count++, tmp);
17 printf("\nEnter line %d:", count);
18 }
19
20 rewind(file);
21
22 while (fgets(tmp, 80, file) != NULL){
23 fputs(tmp, stdout);
24 }
25 fclose(file);
In this example, in the file-open statement, the mode is w+, to indicate that the file should be
opened for reading and writing. Next, the file is created using input from the command line. The
file-position indicator is reset to the beginning of the file, then the contents are displayed on the
screen. To print the contents of the file on the screen, fputs() and a pointer to the screen
(stdout) were used.

1. What are the two ways you can transfer data by a stream to a memory or file location
In your programs, you can transfer data by a stream to a memory or file location as either a text
stream or a binary stream.
2. What is the difference between low-level I/O facilities and high-level I/O facilities?
The low-level I/O facilities are provided by and are specific to the operating-system
environment. The high-level I/O facilities are defined by the ANSI standard for use with the C
language.
Combining Command-Line Arguments and File I/O
Lesson Objectives
In this lesson, you will:

· In this topic, you're advised to document the use of arguments in any of your programs
that require command-line arguments. Explain how and where you might do so.
Combining Command-Line Arguments and File I/O

You'll probably find that using command-line arguments with file I/O can be a convenient
method of designating files and file parameters in a C program. For example, you could use a
command line argument to pass the name of a file to manipulate with your program. Or, you
could pass data via the command line that would be stored in a file.
As with any program that uses command-line arguments, you'll want to carefully and completely
document the use of arguments in programs like these.
The following program accepts the name of a data file as an argument from the command line
and, by using the file I/O function fgets()function, prints the data in the file to the screen.

1 #include <stdio.h>
2 #include <stdlib.h>/* function prototypes */
3 void main(int, char *[]);
4
5 void main(int argc, char *argv[])
6 {
7 FILE *file;
8 char tmp[80];
9
10 /* check for command line argument */
11 if(argc < 2){
12 printf("\nNo file argument");
13 exit(1);
14 }
15
16 /* check for file open */
17 if((file = fopen(argv[1], "r")) == NULL){
18 printf("\nCannot open input file");
19 exit(1);
20 }
21
22 while(fgets(tmp, 80, file) != NULL){
23 fputs(tmp, stdout);
24 }
25 fclose(file);
26 }
This program contains checks to see if there is an argument (file name) for the program, and if
the named file actually exists. If either of the above conditions are not true, the program
terminates. If there is a file argument and the file exists, the program will read the file line-by-
line and print each line to the screen. To print to the screen, the fputs() function is used in
combination with the stdout pointer. This might seem odd, but if you wanted to redirect the
output elsewhere (say, to another file), the mechanics are in place with this technique.
1. In this topic, you're advised to document the use of arguments in any of your programs that
require command-line arguments. How and where might you do so?
Since the user of your program will clearly benefit from such documentation, one way that you
might document your program's need for arguments is with screen output. In cases where users
don't enter the arguments correctly, or when they use a standard argument like /? or /help, you
could display a message detailing how many and of what form of arguments are expected by
your program. For programmers who might have to maintain your code, you can use comments
in your code.

You might also like