IntermediateC Course
IntermediateC Course
INTERMEDIATE C
PROGRAMMING
Course # TCPGR3001
Rev. 10/27/2016
Course Objectives
● At the conclusion of this course, students will be able to:
⏵ Master the use of pointers in a wide variety of problems.
⏵ Use sophisticated pointer techniques to solve problems involving
advanced data structures such as lists, stacks, queues and trees.
⏵ Choose from a wide variety of data structures to implement the most
efficient solution to a problem.
⏵ Apply the proper optimization technique to your C code.
⏵ Apply many portability techniques to your C code.
⏵ Use bit manipulation techniques for efficient solutions to problems.
⏵ Write programs which emphasize modern program design techniques
which emphasize code reuse.
⏵ Write powerful C programs which make "calls" directly into the UNIX
operating system through the use of system calls.
⏵ Decrease development time in writing applications through a more
thorough understanding of sophisticated concepts in C.
Table of Contents
Chapter 1: Review of C and Aggregate Data Types.....................................................9
Data Types.................................................................................................................10
Operators....................................................................................................................12
Operators....................................................................................................................13
Control Flow Constructs.............................................................................................14
Aggregate Data Types...............................................................................................18
Arrays.........................................................................................................................19
Structures...................................................................................................................22
typedef........................................................................................................................25
Structures Example..................................................................................................27
Unions.........................................................................................................................30
Bitfields.......................................................................................................................32
Enumerations.............................................................................................................34
Chapter 2: Building Larger Programs.........................................................................37
Compiling Over Several Files.....................................................................................38
Function Scope...........................................................................................................39
File Scope...................................................................................................................40
Program Scope...........................................................................................................41
Local static..................................................................................................................43
register and extern.....................................................................................................45
Object Files.................................................................................................................47
Example......................................................................................................................48
Libraries......................................................................................................................49
The C Loader..............................................................................................................50
Header Files...............................................................................................................51
Chapter 3: Functions.....................................................................................................55
Function Fundamentals..............................................................................................56
Function Prototypes..................................................................................................58
Function Invocation and Definition............................................................................59
Function Prototypes...................................................................................................60
Subprogram Examples..............................................................................................62
Functions Returning a Value.....................................................................................63
Return Value Considerations....................................................................................64
Return Value Considerations....................................................................................65
Recursive Functions..................................................................................................66
Recursive Functions..................................................................................................72
Evaluation of Function Arguments............................................................................73
Variable Number of Arguments..................................................................................75
Initialization.................................................................................................................77
Chapter 4: Bit Manipulation..........................................................................................81
Chapter 1:
Data Types
● int
10 256 3245
2456456L 32457U
'a' '?' '\007'
● float
23.45F 345.31f
● double
25.67 45.324E+05
● typedefs
typedef enum weekend WKEND;
typedef struct point POINT;
typedef union hash HASH;
Data Types
● For integers
int normally 2 or 4 bytes
long normally 4 or 8 bytes
short normally 2 or 4 bytes
unsigned sizeof (int) bit manipulation
unsigned long sizeof(long)
unsigned short sizeof(short)
Operators
● Logical
if (! ( c >= 'a' && c <= 'z'))
code;
● Relational
if( a == b || c != d)
code;
● Assignment
x *= a + b;
● Bitwise
unsigned a,b,c;
a = ~b | c;
● Structure Member
struct point a, *p;
p = &a;
printf("%d,%d\n", p > x, a.y);
● Conditional
x = a > b ? a: b;
Operators
● Arithmetic
+ * / %
● Logical
! && ||
● Relational
== != > >= < <=
● Assignment
= += = *= \= etc
● Bitwise
~ & | ^ >> <<
● Miscellaneous
++ ?: , sizeof (type) [ ] ( )
● if...else
if(condition) {
code for true
}
else {
code for false
}
● if...else if...else
if(condition_1) {
code for condition_1 true
}
else if(condition_2) {
code for condition_2 true
}
:
:
else if(condition_n) {
code for condition_n true
}
else {
code for all conditions false
}
● while
while(test_expr) {
● do while
do {
} while(test_expr);
c = getchar( );
switch(c) {
case 'a':
case 'A':
case '1':
add( );
break;
case 'd':
case 'D':
case '2':
delete( );
break;
default:
printf("Choose 'add' or 'delete'\n");
}
default:
code to be executed when no
cases match test_expression
}
● Note that break is optional and, if omitted, program will slide
through to code for next case.
● Conditional expression for a switch must evaluate to an int or
be a parameter of type int, short or char.
● Array of arrays
Each strings[i] is an array of STRING_SIZE many characters.
char strings[NUM_STRINGS][STRING_SIZE];
● Array of pointers
char *pointers[NUM_STRINGS];
● Pointer initialization
char line[STRING_SIZE];
char *p = line, *q, *r;
q = p;
r = malloc(STRING_SIZE);
fun(p);
fun(char *v)
{
Arrays
● Arrays collections of single elements traversed with subscripts
or pointers
● Array of arrays
● Pointer arrays
● Uses of pointers
⏵ traversing arrays
⏵ altering arguments through function calls
⏵ efficiency
⏵ creating high level data structures such as lists, queues, stacks, trees
● Initialization of pointers
⏵ with definition
⏵ explicitly through assignment
⏵ relationship between arguments and parameters
⏵ returned value from malloc
Arrays
● Observe the following analogy.
int x; // x is an integer variable
x = 5; // 5 is an integer constant
5 = x; // is illegal
line = pc; // is illegal
pc++; // is legal
x++; // is legal
Arrays
● An array is:
⏵ the name of a contiguous portion of memory, say x
⏵ the address in memory of the beginning of x
⏵ an address constant
● A pointer variable is a variable, which can hold the address of
another variable.
● The connection between an array and a pointer is that both of
them represent an address. However, an array is a pointer
constant while a pointer variable can be modified.
Structures
● A structure
struct point {
int x;
int y;
};
apoint.x = 5;
apoint.y = 10;
awindow.upper_left.x = 5;
awindow.upper_left.y = 10;
awindow.upper_left = apoint;
}
Structures
● Structures
⏵ aggregate data type
⏵ usually has many elements called members
⏵ programmer created type
● Structures should model aggregate data from problem space.
⏵ a payroll record
⏵ a window
⏵ a job in an operating system
● Using structures in a program
⏵ provide a description of the structure (header file)
⏵ allocate storage for one by definition or by malloc
⏵ fill in the defined storage
● Used heavily in dynamic data structures such as lists, trees,
stacks, queues
Structures
● Structures may be assigned to one another if they have the
same data type.
struct date a,b, *ps;
struct payroll person, manager;
a = b; // ok
person = manager // ok
a = manager; // illegal
a = manager.birthday; // ok
ps = &a;
b = *ps; // ok
typedef
● In programs using structures, the keyword struct must be
repeated often.
● There is a facility called typedef, which allows you to create a
new name for an existing type. A typedef can:
⏵ Simplify programs
⏵ Aid in documenting programs
⏵ Give the appearance of new types
● Conventionally, a typedef is named using upper case.
● The lines below give new names for existing types.
/* LENGTH and WIDTH are additional
names for int
*/
typedef int LENGTH, WIDTH;
typedef
● With these typedef's in place, you could code:
int main()
{
/* a, b, x, and y are of the same type */
int a, b;
WIDTH x;
LENGTH y;
char *p;
STRING pc;
int x[10];
ARRAY r;
...
}
Structures Example
● Assume the following is contained in payroll.h.
#define NAMESIZE 30
struct date {
int year;
int month;
int day;
};
struct payroll {
char name[NAMESIZE];
float pay;
struct date birthday;
struct date promotion;
char dept[10];
};
Structures Example
● Assume the following is contained in your driver program.
#include "payroll.h"
#include <stdlib.h>
main( )
{
RECORD person; // a struct payroll variable
RECORD bank[100] // array of 100 struct payroll
RECORD *pts; // a pointer to a struct payroll
RECORD fill_1( ); // function returning RECORD
RECORD *fill_2( ); // function returning RECORD *
Structures Example
● Structure members are referenced with the syntax:
⏵ structure_name.member_name
struct date feb14;
struct payroll person;
feb14.year = 1992;
feb14.month = 2;
feb14.day = 14;
person.pay = 42500.00;
strcpy(person.name,"tom smith");
person.birthday = feb14;
pts = &feb14;
pts > year = 1992
pts > month = 2;
pts > day = 14;
pp = &person;
pp > pay = 42500.00;
pp > birthday = feb14;
Unions
● A driver program that instantiates a union
#include "hash.h"
#include <stdio.h>
main( )
{
int probe;
char table[NUM_STRINGS][MAX_CHARS];
union hash string;
fgets(string.name, MAX_CHARS, stdin);
probe = string.index % NUM_STRINGS;
if ( strcmp(table[probe],string.name) == 0 )
printf("found %s\n", string.name);
else
....
}
Unions
● Unions
⏵ use structure syntax
⏵ have different interpretations of the same storage
⏵ sizeof(union) >= sizeof(largest interpretation)
⏵ have specialized uses such as:
• variant records
• non homogeneous arrays
⏵ create possible portability problems
● Using unions in a program
⏵ provide a description of the union (header file)
⏵ allocate storage for one
⏵ refer to storage by any of the collection of types in the union
Bitfields
● Assume the bitfield structure is defined in info.h.
struct information {
unsigned int is_male: 1;
unsigned int is_married: 1;
unsigned int how_many_kids: 5;
unsigned int is_manager: 1;
};
Bitfields
● Bitfields
⏵ allow easy access to portions of the wordsize
⏵ provide an alternative to bit manipulation operators
⏵ use structure syntax for easier access than bit manipulation operators
⏵ may create portability problems
● Using bitfields in a program
⏵ provide a description of the bit field type (usually kept in a header file)
⏵ allocate storage for one
⏵ refer to storage by structure syntax
Enumerations
● Assume enumeration is defined in sports.h.
enum sports
{ baseball, football, basketball, hockey };
/* baseball = 0; */
enum products { shoes = 10, rackets, shirts };
/* rackets = 11; */
activity = next_sport(baseball);
if ( activity == basketball )
function( );
....
Enumerations
● Enumerations
⏵ are modeled after pascal although not as strongly implemented (no
succ or pred functions)
⏵ makes programs easier to read
⏵ slightly stronger than #define mechanism
⏵ are implemented as small integers
⏵ are symbolic constants whose values start with 0 unless otherwise
specified and are incremented by 1.
● Using the enum type in a program
⏵ provide a description of the type together with possible values
(usually kept in a header file)
⏵ allocate storage for one
Exercises
1. Write a small program, which prints the sizes of various
integers on your machine.
2. For the following definitions and code, give the value of each
expression below:
char *pc;
char lines[10][20];
strcpy(lines[0], "mike");
pc = lines[0];
a) sizeof(lines)
b) sizeof(lines[1]))
c) sizeof(lines[1][3])
d) strlen(lines[0])
e) sizeof(pc)
Chapter 2:
Function Scope
● Variables have function scope if they are either:
⏵ Nonstatic variables defined inside of a function
⏵ Parameters.
● Storage for these variables is reserved when the function in
which they are defined is executed.
⏵ Storage is deallocated when the function terminates.
⏵ The keyword auto can be used for local variables that are not
parameters.
⏵ Local variables can only be referenced inside the function in which
they are defined.
● Parameters begin life with the value of the argument that
they represent.
⏵ Other local variables have unknown beginning values.
● a, b, and c have function scope. They are referred to as
local variables.
double hypot(double a, double b)
{
double c;
c = sqrt(a * a + b * b);
return(c);
}
File Scope
● A variable has file scope if it is defined in a file before a
function.
⏵ This is a less likely place to define a variable.
⏵ It is the source of many bugs.
⏵ Yet, there are some good uses for file scoped variables.
● Functions, which do not call one another but need to share
data, should use filescoped variables.
int v; /* file scope */
int x[100]; /* file scope */
int s; /* file scope */
int pop()
{
int s; // file scope 's' not visible here
}
void clear()
{
// body of clear
}
Program Scope
● Program scope means that a variable is known to all files of a
program.
⏵ Program scope differs from file scope when there is more than one
file being linked.
● This is usually not desirable because a change to a variable in
one file can have an effect on the same variable in another file.
● Often, the programmer wants privacy for a file scoped variable.
● The keyword static makes a file scoped variable private to
that file.
Program Scope
● x has program scope.
main.c one.c two.c
– –
extern int x; extern int x; Int x;
● If the three files above are compiled and linked together, there
will be one storage location named x.
⏵ Among all these files, exactly one of them must have a definition for
x. The others need an extern declaration for x.
● Any of the variables above can be made private to a particular
file by using the static keyword.
static int x; static int x; static int x;
Local static
● Whether they are static or not:
⏵ filescoped variables have initial values of 0; and
⏵ only one initialization for a file scoped variable is permitted.
● The static keyword has another important use.
⏵ Local static variables retain values across function invocations. They
are variables that are initialized once, regardless of how many times
the function is invoked.
● A local static variable is initialized when the program is
loaded.
Local static
starter.c
1. #include <stdio.h>
2.
3. #define HOWMANY 5
4.
5. void starter();
6.
7. main()
8. {
9. int i;
10.
11. for ( i = 0; i < HOWMANY; i++)
12. starter();
13. }
14.
15. void starter()
16. {
17. /*
18. Static storage is initialized with 0
19. by default. Here we do it explicitly
20. */
21. static int first = 0;
22.
23. if(first == 0)
24. {
25. first = 1;
26. printf ("Only once\n");
27. }
28. else
29. {
30. printf ("All but the first time\n");
31. }
32.
33. printf ("Every time\n");
34. }
} }
Object Files
● If each function is placed in a separate file, the functions can
be compiled separately and then linked into any application
that needs them.
● Every compiler has an option that directs the compiler to build
an object file rather than to build an executable file. The
resultant object file is named with the suffix .o on UNIX
machines and .obj on Windows machines.
● On UNIX for example, one might proceed as follows:
$ gcc #c fun1.c
$ gcc #c fun2.c
$ gcc prog.c fun1.o fun2.o
Example
prog.c
1. #include <stdio.h>
2.
3. void fun1();
4. void fun2();
5.
6. int x;
7.
8. main()
9. {
10. printf("MAIN: x = %d\n", x);
11. fun1();
12. printf("MAIN: x = %d\n", x);
13. fun2();
14. printf("MAIN: x = %d\n", x);
15. }
fun1.c
1. #include <stdio.h>
2.
3. int x;
4.
5. void fun1()
6. {
7. x++;
8. printf("FUN1: x = %d\n", x);
9. }
fun2.c
1. #include <stdio.h>
2.
3. int x;
4.
5. void fun2()
6. {
7. x++;
8. printf("FUN2: x = %d\n", x);
9. }
Libraries
● The previous strategy is cumbersome with a large number of
files. A better strategy is to use libraries.
● Each operating system has a utility (the librarian).
⏵ Compile each function as before.
⏵ Place the object files in the library.
● Place library name on the command line when building an
executable.
● When your C compiler was installed, many support tools were
also installed including:
⏵ The librarian utility
⏵ The actual libraries
● To build a library, one might proceed as follows.
⏵ For example, on UNIX:
$ gcc #c fun1.c
$ gcc #c fun2.c
$ ar r mylib.a fun1.o fun2.o
● Building executables:
$ gcc main.c mylib.a
$ gcc othermain.c mylib.a
The C Loader
● When you execute the C compiler, several steps occur.
⏵ Preprocessor
⏵ Compiler
⏵ Loader
● Loader looks for functions referenced but not supplied.
● In order to resolve external references, the loader looks various
places in the following order.
⏵ In the source file being compiled
⏵ In any files being linked
⏵ In the Standard C Library
⏵ In libraries named on the command line
● For any function not resolved by the above search, an error is
printed.
● A program with a misspelled function name would produce the
following error.
int main()
{
printf("Hello\n");
print("Loader\n"); /* oops */
}
Header Files
● Header files support applications and libraries. Header files
contain information needed by many programs.
● Header files save you typing. You can use the #include
feature of the preprocessor to import header files. Header files
should not contain executable statements.
● There is a relationship between header files and libraries.
● When you use a function from a library, you should include the
header file, which supports that library.
⏵ The header file will contain the prototype(s) for the function(s) from
that library that you are using.
⏵ This reduces the likelihood of errors, which could result if you
provided your own prototypes.
Header Files
● Information typically found in header files
⏵ #include statements
⏵ #define statements
⏵ Other preprocessor directives
⏵ Function prototypes
⏵ Structure descriptions
⏵ typedef's
● Each library on your system will have at least one header file
that supports it. Examples:
LIBRARY HEADER FILE
Standard C stdio.h
stdlib.h
string.h
Math math.h
Exercises
1. Take any program that you have written and spread it out so
there is one function per file. Compile and execute the
program.
2. Simulate an array by providing two functions, get and put.
Place these functions in the same file and define an array
externally to these two functions. Compile this file separately.
int array[1000];
void put(int value, int position)
{
x = get(5); // x = array[5];
}
Chapter 3:
Functions
Function Fundamentals
● Function Example
/* function prototype */
double average(int *table, int num_elements);
/* function definition */
double average(int *array, int limit)
{
double total = 0.0;
int i;
for( i = 0; i < limit; i++)
total += array[i];
return( total / limit );
}
● Note that the parentheses for the return statement are optional
when a value is being returned. The following are equivalent!
return( total / limit );
return total / limit;
Function Fundamentals
● Functions are the basic building blocks of C programs.
⏵ Generality
⏵ Modularity
⏵ Reuse
⏵ Readability
● Three pieces of information must associate correctly.
⏵ Function prototype
⏵ Function invocation
⏵ Function definition
Function Prototypes
● Function prototypes are usually kept in a header file.
⏵ Prototypes for I/O functions in the standard library are kept in
stdio.h.
⏵ Prototypes for the math functions in the standard library are kept in
math.h.
⏵ Prototypes for the string functions in the standard library are kept in
string.h.
● Prototype example
void initialize(int *table, int num_elements);
/* function definition */
void initialize(int *array, int limit)
{
int i;
for( i = 0; i < limit; i++)
array[i] = 0;
}
Function Prototypes
● A prototype tells the compiler the types of the arguments and
the return type of the function.
double average(int *table, int num_of_elements);
Function Prototypes
● Function prototypes convey two pieces of information.
⏵ the returned type of the function
⏵ the types of the parameters
● Note that the names of the parameters are not required in the
prototypes. If they are provided, it is purely for documentation
(readability) purposes.
● Before ANSI C, there were no prototypes. There were only
function declarations. A function declaration simply tells the
compiler the return type of the function.
● Function prototypes are not required by ANSI C, but you are
encouraged to use them! They allow the compiler to do type
checking. They provide documentation for the interface to
functions.
● In the absence of a prototype or a declaration, the compiler
assumes the function returns int.
Subprogram Examples
● Examples of functions from other languages
X = SQRT(Y);
J = SQRT( SQUARE(X) + SQUARE(Y));
Z = SQUARE(X) * CUBE(Y) * 100;
CALL DISPLAY(VALUES);
/* printing a structure */
print(person);
⏵ a subroutine
• arguments and parameters are related
• subroutine name does not represent a value
• there is no return statement
● C only has one subprogram type the function.
● Generally, if a function is to compute a value, it will be given a
return type.
● If the function is to behave like a subroutine from other
languages, it will be given the return type of void.
/* INVOCATION */
stats(numbers, howmany, &sum, &mean, &variance);
/* PROTOTYPE */
void stats(int*, int, int*, double*, double*);
void fun(int a)
{
if( a < 0 ) {
report();
return;
}
if( a == 0 ) {
display();
return;
}
/* rest of function */
}
Recursive Functions
● Iterative solution for computing factorial of an integer
int fact(int p)
{
int res = 1;
if( p <= 1 )
return(1);
for ( ; p > 1; p)
res *= p;
return(res);
}
Recursive Functions
● A recursive function is one that invokes itself.
● Compare the two functions, each of which computes a factorial.
● The iterative solution
⏵ takes more local space
⏵ is faster
● The recursive solution
⏵ takes more stack space
⏵ is easier to read
⏵ runs more slowly
Recursive Functions
● Fibonacci Sequence
int fib(int p)
{
if ( p <= 1 )
return(p);
return( fib(p 1) + fib(p 2));
}
FIB INVOCATIONS
2 3
3 5
4 9
5 15
Recursive Functions
● To see why recursion is usually slower than iterative versions
of the same function, notice the recursive version of the
Fibonacci sequence.
0,1,1,2,3,5,8,13,21
0,1,2,3,4,5,6, 7, 8
Recursive Functions
● Quicksort strategy
⏵ For the following data elements:
10 20 25 55 35 42 11 5 29 2 30
Recursive Functions
● Quicksort is the most studied sorting algorithm. For many
kinds of data, quicksort is the fastest sort.
⏵ Many embellishments
⏵ Difficult to code
● Divide and conquer strategy
⏵ Select a partitioning element (say the first element). Call this P.
⏵ Partition the data so that
• all elements to the left of P are lower than P
• all elements to the right are greater than P
● Recursively apply the same algorithm to each of the two
partitions!
Recursive Functions
● Code for quicksort
quicksort.c
1. #define SIZE 20
2. void quick(int *, int, int);
3. int main()
4. {
5. int a[SIZE];
6. fill(a,SIZE);
7. print(a,SIZE);
8. quick(a, 0, SIZE 1);
9. print(a,SIZE);
10. }
11. void quick(int *p, int low, int high)
12. {
13. if ( low < high) {
14. int lo = low;
15. int hi = high + 1;
16. int elem = p[low];
17. for ( ; ; ) {
18. while(p[++lo] < elem && lo < hi)
19. ;
20. while(p[hi]>elem && high>=lo)
21. ;
22. if ( lo < hi)
23. swap(&p[lo], &p[hi]);
24. else
25. break;
26. }
27. swap(&p[low], &p[hi]);
28. quick(p, low, hi 1);
29. quick(p, hi + 1, high);
30. }
31. }
● Example Program 2
prog2.c
1. int main()
2. {
3. int i = 5, j = 5;
4.
5. printf("%d %d\n", i++, i);
6. printf("%d %d\n", j, j++);
7. }
x = f(i) + g(i++);
Initialization
● Local static initialization example
static.c
1. #define MAX 10
2. #define LOOPS 100
3.
4. void fun(void);
5.
6. int main()
7. {
8. int i;
9.
10. for(i = 0; i < LOOPS; i++) {
11. fun();
12. }
13. }
14.
15. void fun()
16. {
17. static int x = 0;
18.
19. if((x % MAX) == (MAX 1)) {
20. printf("%d\n", x);
21. }
22. x++;
23. }
24.
Initialization
● Local variables are auto by default. They have unknown initial
values.
● External variables are extern by default. They have 0 as their
initial value.
● All static variables are initially 0 by default.
● Local static variables remember values between function
invocations.
● In pre ANSI C, aggregates (arrays and structures) cannot be
initialized, unless they are external or static. This is sensible
from an efficiency point of view. The ANSI standard has
relaxed this condition.
Exercises
1. Write a recursive version of the function add, which returns the
sum of two positive integer arguments.
2. Write a function record() which takes two integer
parameters. The function tracks jockey's wins (first place),
places (second place), and shows (third place). Use
#defines (or enums) liberally. Be careful of your choice of
values for the constants. Use a static int array local to the
record() function.
The call record(1, WIN) records a WIN for player 1.
The call record(2, SHOW) records a SHOW for player 2.
y = power(2,3); // y = 8
y = power(2,5); // y = 32
x = power(i, i++); // x = ???
Chapter 4:
Bit Manipulation
● unsigned int has no sign bit. You can use the unsigned
type to store numbers although they must be positive or zero.
Each piece (bit) of information could be stored in a single int,
but this would be wasteful.
● A more economical approach is to pack the information into an
unsigned int a bit at a time. The solution becomes a
matter of accessing single bits.
Bitwise Operators
● The following truth table summarizes the rules for the bitwise
logical operators.
OP OP | & ^
0 0 0 0 0
0 1 1 0 1
1 0 1 0 1
1 1 1 1 0
Bitwise Operators
● To access and manipulate single bits, C provides six bitwise
operators.
⏵ bitwise or |
⏵ bitwise and &
⏵ bitwise exclusive or ^
⏵ right shift >>
⏵ left shift <<
⏵ one's complement ~
● The bitwise logical or ( | ), obeys the following rule.
⏵ Bits are compared one at a time.
⏵ Each resultant bit is a 1 bit if either or both operand bits have the
value 1.
● The bitwise logical and ( & ), obeys the following rule.
⏵ Bits are compared one at a time.
⏵ Each resultant bit is a 1 bit if both operand bits have the value 1.
● The bitwise logical exclusive or ( ^ ), obeys the following
rule.
⏵ Bits are compared one at a time.
⏵ Each resultant bit is a 1 bit if either but not both operand bits have
the value 1.
Readability Aids
● Define the following constants for readability.
#define IS_MALE 01
#define IS_MAN 02
#define IS_VET 04
#define HAS_DEG 010
#define IS_MAR 020
#define IS_MIN 040
if( profile&IS_VET )
printf("IS VET\n");
if( profile&(IS_MAN|HAS_DEG))
printf("IS MAN OR HAS DEGREE\n");
if((profile&(IS_MAN|HAS_DEG))==(IS_MAN|AS_DEG))
printf("IS MAN AND HAS DEGREE\n");
if((profile&(IS_MAR|HAS_DEG))==(IS_MAR|AS_DEG))
printf("IS MAR AND HAS DEGREE\n");
}
}
return(count); // NO
}
Circular Shifts
● Example
cshift.c
1. unsigned int rcshift(unsigned int, int);
2. unsigned int lcshift(unsigned int, int);
3. int wordlength(void);
4. main()
5. {
6. unsigned int n = 0xffff;
7. int num = 4;
8. printf("%d\n", wordlength());
9. // %x is for hexadecimal
10. printf("BEFORE: %x\n",n);
11. n = rcshift(n,num);
12. printf("AFTER RIGHT: %x\n",n);
13. n = lcshift(n,num * num);
14. printf("AFTER LEFT: %x\n",n);
15. }
16. unsigned int rcshift
17. (unsigned int number, int howmany)
18. {
19. int i;
20. unsigned mask = ~0;
21. mask = ~(mask >> 1);
22. for( i = 0; i < howmany; i++) {
23. if ( number & 01)
24. number = (number >> 1) | mask;
25. else
26. number >>= 1;
27. }
28. return(number);
29. }
30. unsigned int lcshift
31. (unsigned int number, int howmany)
32. {
33. unsigned int r;
34. r = rcshift(number, wordlength() # howmany);
35. return(r);
36. }
Circular Shifts
● A circular shift fills the original with the shifted off bit.
ABCDEFGH // original
BCDEFGH0 // original LEFT SHIFT 1 BIT
BCDEFGHA // original LEFT CIRCULAR SHIFT 1
Direct Lookup
● The Standard C Library consists of many functions that do
character testing.
⏵ Some implement these as functions.
⏵ Others are implemented as macros.
● One look up technique can be provided by the following
scheme:
⏵ Each character has a unique 7 bit configuration that can be
considered a number.
⏵ For example, the character 'a' has the configuration.
⏵ 1 000 001 = 65 DECIMAL
● Provide an unsigned int array large enough to hold
information about every character.
⏵ At position 65, place symbolic information about the character 'a'.
U | A | H // UPPER, ALPHA, HEX
⏵ Do the same for each character in the set.
R/C 7 6 5 4 3 2 1 0
0 7 6 5 4 3 2 1 0
1 15 14 13 12 11 10 9 8
2 23 22 21 20 19 18 17 16
3 31 30 29 28 27 26 25 24
4 39 38 37 36 35 34 33 32
5 47 46 45 44 43 42 41 40
6 55 54 53 52 51 50 49 48
7 63 62 61 60 59 58 57 56
Radix Sort
● Consider the following data and binary equivalences:
13 12 10 4 11 7 9 2
1101 1100 1010 0100 1101 0111 1001 0010
Radix Sort
● Radix sorting differs widely from most sorts.
● It is more difficult to code.
⏵ Look at the right most bit, bit 0, of each key
⏵ Place all those whose value is zero in bin zero
⏵ Place all those whose value is one in bin one
⏵ Collect the two bins with one on the bottom
⏵ Repeat the process looking at bit number 1
⏵ Repeat the process looking at bit number 2 etc
Radix Sort
radixsort.c
1. #define SIZE 15
2. int wordlength(void);
3. void radix(int *v, int size);
4. void print(int * array, int size);
5. main()
6. {
7. int data[]={46,30,82,90,56,17,95,15,48,26,4,58,71,79,92};
8. printf("ORIGINAL:\n");
9. print(data, SIZE);
10. radix(data, SIZE);
11. printf("SORTED:\n");
12. print(data, SIZE);
13. }
14. void radix(int *v, int size)
15. {
16. int temp[SIZE], t_index, v_index, i, k = 0;
17. unsigned mask;
18. int wordsize = wordlength() 1;
19. // LOOP(WORDSIZE 1) TIMES
20. while (k < wordsize)
21. {
22. t_index = v_index = 0;
23. // mask for bit manipulation
24. mask = 1 << k;
25. // separate into v and temp
26. for ( i = 0; i < size; i++){
27. if ((v[i] & mask) == 0)
28. v[v_index++] = v[i];
29. else
30. temp[t_index++] = v[i];
31. }
32. //combine
33. for(i = 0; i < t_index; i ++)
34. v[v_index++] = temp[i];
35. // just for aid in displaying the output
36. //print(v, SIZE);
37. k++;
38. }
39. }
Radix Sort
● The three lines below show the data before and after the
program. The middle line shows the data after one pass
through the radix sort.
● Notice that all the even numbers are followed by all the odd
numbers.
ORIGINAL: 46 30 82 90 56 17 95 15 48 26 4 58 71 79 92
ONE PASS: 46 30 82 90 56 48 26 4 58 92 17 95 15 71 79
SORTED: 4 15 17 26 30 46 48 56 58 71 79 82 90 92 95
Exercises
1. Write a program which queries the user for the action on or off.
Then ask for a bit number b. The program should perform the
specified action on bit number b.
$ prog
action> on
bit number 2
0000000000000010
action> on
bit number 4
0000000000001010
action> off
bit number 2
0000000000001000
action> quit
$
Chapter 5:
Pointers (Part 1)
Pointer Arithmetic
● Examples of pointer arithmetic:
#define MAX 10
begin = numbers;
end = numbers + MAX; // pointer arithmetic
Pointer Arithmetic
● Pointer arithmetic has several forms.
⏵ pointer + integer => pointer result
⏵ pointer integer => pointer result
⏵ pointer pointer => integer result
● C supports specific pointer types rather than generic pointers.
⏵ automatic scaling for pointer arithmetic
⏵ used exclusively for array processing
● Example using pointers
pointer.c
1. #include <stdio.h>
2. #include <malloc.h>
3.
4. int main()
5. {
6. int *iPtr;
7. void *ptr = malloc(10);
8.
9. iPtr = ptr;
10. *iPtr = 10;
11.
12. printf("%d %p\n", *iPtr, iPtr);
13. printf("%d %p\n", *ptr, ptr);
14. printf("Sizeof *iPtr is %d\n", sizeof(*iPtr));
15. printf("Sizeof *ptr is %d\n", sizeof(*ptr));
16. }
Binary Search
● Another example of using pointers
binary.c
1. #define SIZE 5
2.
3. int * binary(int * table, int size, int value);
4.
5. int main()
6. {
7. // array must be sorted for a binary search
8.
9. int data[] = {10, 12, 34, 45, 65};
10. int lookup = 21;
11. int * found = binary(data, SIZE, lookup);
12.
13. if(found) {
14. printf("%d was found\n", *found); {
15. } else {
16. printf("%d was not found\n", lookup);
17. }
18. }
19.
20. int * binary(int * table, int size, int value)
21. {
22. int *begin = table;
23. int *end = begin + size 1, *mid;
24.
25. while (begin <= end) {
26. mid = begin + (end begin) / 2;
27. if (*mid > value) {
28. end = mid 1;
29. } else if(*mid < value) {
30. begin = mid + 1;
31. } else {
32. return mid;
33. }
34. }
35. return NULL;
36. }
Binary Search
● There are various methods for searching tables.
⏵ Linear Search
⏵ Binary Search
⏵ Hashing
● If a data structure has too many elements, then linear search is
too costly.
● You can lower the time it takes to find a value by using a
technique known as binary search.
● With binary search, the data must be kept in sorted order.
⏵ By probing at the middle element, you can determine which half to
search.
⏵ Use the same strategy on the remaining half.
int main( )
{
int a, b;
pi pi
Before 1000 After 1004
2000 2000
int main()
{
int numbers[10] = { 5, 10, 15 };
int *pi;
int numbers[MAX];
int *p = numbers, *end = numbers + MAX;
*p = &values[0][0];
*end = &values[ROWS 1][COLS 1];
⏵ Allocate the space for each row (say y elements per row).
p[i] = malloc(y * sizeof(int));
vector[3] = 10;
*(vector + 3) = 10
0 1 2 .... ....
numbers[2][1] = 20;
*(*(numbers + 2) + 1) 20 int
int numbers[ROWS][COLS];
char lines[NO_STRINGS][LENGTH];
● Arrays of arrays
⏵ numbers and lines are actually single dimensioned arrays whose
members are also arrays.
● The following are correct:
void fun(int *p);
int vector[COLS];
int *p, (*pv)[COLS];
fun(vector);
fun(numbers[i]);
p = vector;
pv = numbers;
Complex Declarations
● Simple
int pi; // int
● Combined
● Harder
int (*f[M])(); // M pointers to functions returning
// int
int *(*f)(); // pointer to function returning
// pointer to int
Complex Declarations
● Use the hierarchy of C operators.
() Comes before
[] Comes before
*
f[M] Array of M
*f[M] pointers to
(*f[M])() function returning
int *(*f[M])() pointer to int
surrogate.h
1. #define NAMESIZE 20
2.
3. struct records {
4. char name[NAMESIZE];
5. int age;
6. };
7.
8. typedef struct records R, *PR;
9.
10. void print(PR *, int);
11. void sort(PR *, int);
12. int input_rec(PR);
13. int get_the_data(PR *, int);
surrogatemain.c
1. #include "surrogate.h"
2.
3. #define NUM 100
4.
5. int main( ) {
6. PR record_pointers[NUM];
7. int number;
8.
9. number = get_the_data(record_pointers, NUM);
10. if(number > 0) {
11. sort(record_pointers, number);
12. print(record_pointers, number);
13. }
14. }
Exercises
1. Write a program which performs a frequency count of the
words in a file. Use the following "parallel" arrays to solve the
problem.
char words[NUMBER_OF_WORDS][WORD_LENGTH + 1];
int counts[NUMBER_OF_WORDS];
⏵ Note: A simple way to get words from an input file is to use the
function scanf as shown below:
#include <stdio.h>
int main()
{
char line[MAXLINE];
int main()
{
int a = 7, b = 2, c = 4;
// a = 2, b = 4, c = 7
}
©2016 UMBC Training Centers
121
INTERMEDIATE C PROGRAMMING CHAPTER 5: POINTERS (PART 1)
Chapter 6:
Pointers (Part 2)
pc = malloc(100);
fgets(pc, 100, stdin);
len = strlen(pc)
printf("%s\n", pc);
if ( strcmp(pc, "quit") == 0 ) {
printf("You entered 'quit'");
}
Initialization of Pointers
● A pointer must be given a value before it can be used properly.
This happens either explicitly or through a function call, which
returns a pointer, or by assignment from another pointer.
● The following sequence would cause a run time error, since the
pointer has an uninitialized value.
char *pc;
gets(pc);
● A pointer can be used in the same context as an array,
provided that the pointer has a meaningful value.
● Here are some legitimate pointer initializations.
⏵ Explicitly
char line[MAX];
char *pc, *p;
pc = line;
int main()
{
char line[MAX];
int number = 1;
● This code also uses the fact that fgets returns a pointer:
printf("Enter a number: ");
number = atoi(fgets(line, MAX, stdin));
printf("%s\n", lines[0]);
printf("%s\n", ptrs[0]);
⏵ In each printf above, the first string of the data structure is printed.
● However, the amount of memory used by each array is
different.
sizeof(lines) = ROWS * COLS
sizeof(ptrs) = ROWS * sizeof(char *)
argv argc
500 4
printargs.c
1. #include <stdio.h>
2.
3. void printArgs(char **a);
4.
5. int main(int argc, char **argv)
6. {
7. print(argv);
8. }
9.
10. void printArgs(char **a)
11. {
12. ++a; // Skip program name
13.
14. while(*a) {
15. printf("%s\n",*a++);
16. }
17. }
while(##argc > 0) {
s = *++argv;
while(*s) {
if (argc != 3 ) {
fprintf(stderr, "Usage: message\n");
exit(1);
}
base = atoi(argv[1]);
expo = atoi(argv[2]);
● Sample execution:
$ envptr
usage: envptr <env_variable>
$ envptr PATH
PATH=/bin/usr:/user/bin:/user/michael/bin
$ envptr XYZ
XYZ not found
Function Pointers
● With the exception of the array type, the name of any variable
is a reference to its value.
● The name of an array is a reference to its address.
● There is another type in C, which automatically generates an
address. A function name without the function invocation
operator is a reference to the address of the function.
● A function name can appear in argument lists.
int numbers[MAX], x = 25.0;
/* the two calls below are very different
*/
Function Pointers
● Study the following.
/* int pointer */
int *px;
Function Pointers
● Suppose you have the following.
int square(int p) { return p * p; }
int cube(int p) { return p * p * p; }
...
...
...
Function Pointers
● However, there is an easier way. square_them and
cube_them are exactly the same except for the function called
within them.
● If we could parameterize these functions, we could have one
function take the place of each of these two (or however many
there are).
● We will call this new function calc. It would be invoked as
follows.
int x[5] = { 12, 23, 14, 25, 16};
calc(x, 5, square);
calc(x, 5, cube);
Function Pointers
● This example uses an array of function pointers.
funs.c
1. #define MAX 3
2. #define SIZE 100
3.
4. int cube(int);
5. int square(int);
6. int times2(int);
7.
8. int getvalues(int * data, int size);
9. void print(int * data, int size);
10. void compute(int * data, int size, int (*ptf)(int));
11.
12. main( ) {
13. int j, numValues;
14. int (*p[MAX])();
15. int x[SIZE];
16.
17. p[0] = cube;
18. p[1] = square;
19. p[2] = times2
20.
21. for (j = 0; j < MAX; j++) {
22. numValues = getvalues(x,SIZE); // FILL x
23. print(x, numValues); // PRINT x
24. compute(x, numValues, p[i]); // COMPUTE
25. print(x, numValues); // PRINT x
26. }
27. }
28.
29. int cube(int x) {
30. return x * x * x;
31. }
32.
33. int square(int x) {
34. return x * x;
35. }
36.
37. int times2(int x) {
38. return 2 * x;
39. }
Function Pointers
funs.c (continued)
40. int getvalues(int * data, int size) {
41. int i;
42.
43. for(i = 0; i < size; i++)
44. data[i] = i + 1;
45.
46. return i;
47. }
48.
49. void print(int * data, int size) {
50. int i;
51.
52. for(i = 0; i < size; i++)
53. printf("%d ", data[i]);
54.
55. printf("\n");
56. }
57.
58. void compute(int * data, int size, int(*ptf)(int)) {
59. int i;
60.
61. for(i = 0; i < size; i++)
62. data[i] = ptf(data[i]);
63. }
Function Pointers
menu.c
1. #include <stdio.h>
2. #include <stdlib.h>
3.
4. #define MAX 100
5.
6. double pow(double, double);
7. double atof(char *);
8. double add (double, double);
9. double mult(double, double);
10.
11. struct commands {
12. char *name;
13. double (*pf)();
14. } c[] = {"add", add, "mult", mult, "power", pow };
15.
16. #define SIZE sizeof(c)/ sizeof(struct commands)
17.
18. main( )
19. {
20. char line[MAX];
21. double a,b, ans;
22. int i;
23.
24. while(1) {
25. printf("enter a command:\n");
26. printf("add\nmult\npower\nquit\n => ");
27. gets(line);
28.
29. if(strcmp(line, "quit") == 0)
30. break;
31.
32. for (i = 0; i < SIZE; i++) {
33. if(strcmp(c[i].name, line) == 0) {
34. printf("input first number ");
35. a = atof(gets(line));
36. printf("input second number ");
37. b = atof(gets(line));
38. ans = c[i].pf(a, b);
39. printf("ans is %f\n", ans);
40. break;
41. }
42. }
Function Pointers
menu.c (continued)
43. if( i == SIZE )
44. printf("%s not implemented\n",line);
45.
46. printf("press return to continue\n");
47. gets(line);
48. system("clear") && system("cls");
49. }
50. }
51.
52. double pow(double a, double b)
53. {
54. int i;
55. double temp = a;
56.
57. for(i = 0; i < b 1; i ++)
58. temp *= a;
59.
60. return temp;
61. }
62.
63. double add (double a, double b)
64. {
65. return a + b;
66. }
67.
68. double mult(double a, double b)
69. {
70. return a * b;
71. }
Function Pointers
sort.h
1. #define NO_LINES 20
2. #define ERROR #1
3. #define LINE_LENGTH 100
4.
5. int numcmp(const char *, const char *);
6. int revcmp(const char *, const char *);
7. int revnum(const char *, const char *);
8.
9. void printlines(char **lines, int howmany);
10. int readlines(char **lines, int maximum);
11.
12. void sort(char **, int,
13. int (*)(const char *, const char *));
Function Pointers
sort.c (continued)
22. int numcmp(const char *s, const char *t) {
23. return(atof(s) # atof(t));
24. }
25.
26. int revnum(const char *s, const char *t) {
27. return(atof(t) # atof(s));
28. }
29.
30. int revcmp(const char *s, const char *t) {
31. return(strcmp(t, s));
32. }
Function Pointers
mainsort.c
1. #include <stdio.h>
2. #include <stdlib.h>
3. #include <string.h>
4. #include <malloc.h>
5.
6. #include "sort.h"
7.
8. main(int argc,char **argv) {
9. char *ptrs[NO_LINES], reply[10];
10. int lines;
11.
12. if(argc != 2) {
13. printf("usage: sort #n numerical\n");
14. printf(" sort #a alphabetical\n");
15. printf(" sort #r reverse alpha\n");
16. printf(" sort #rn reverse numeric\n");
17. exit(3);
18. }
19.
20. if((lines = readlines(ptrs,NO_LINES)) == ERROR)
21. printf("too many lines or no room\n");
22. exit(1);
23. }
24.
25. if(strcmp(argv[1],"#n") == 0)
26. sort(ptrs,lines,numcmp);
27.
28. else if(strcmp(argv[1],"#a") == 0)
29. sort(ptrs,lines,strcmp);
30.
31. else if(strcmp(argv[1],"#r") == 0)
32. sort(ptrs,lines,revcmp);
33.
34. else if(strcmp(argv[1],"#rn") == 0)
35. sort(ptrs,lines,revnum);
36.
37. else {
38. printf("incorrect argument\n");
39. exit(4);
40. }
41. printlines(ptrs, lines);
42.
Function Pointers
mainsort.c (continued)
43.
44. // deallocate dynamically allocated memory
45.
46. for (i = 0; i < lines; i++) {
47. free(ptrs[i]);
48. }
49.
50. } // end of main
51.
52. int readlines(char **p,int limit) {
53. int n = 0;
54. char *pc, line[LINE_LENGTH];
55.
56. while(gets(line) != NULL) {
57. if(n > limit)
58. return(ERROR);
59.
60. else if((pc = malloc(strlen(line) + 1))== NULL)
61. return(ERROR);
62.
63. else {
64. strcpy(pc,line);
65. p[n++] = pc;
66. }
67. }
68. return(n);
69. }
Exercises
1. Write a program that takes three values from the command
line: a beginning temperature (celsius), an ending temperature
(celsius), and an increment value.
The program should produce a temperature conversion chart. For
example:
myprog 0 30 10
CELSIUS FAHRENHEIT
0 32.0
10 50.0
20 68.0
30 86.0
Exercises
Exercise 3 (continued):
4. Write a program which prints the sum of all the digit characters
(not the number of them) on the command line. For example:
programname 24a 3b5 23 c54 83
Output:
39
Chapter 7:
A Database Application
● In this section, we are interested in functions used to read and
write structures. The functions will be demonstrated by
developing a typical financial application dealing with
structures.
● The application uses the following header files:
employee.h
1. #ifndef EMPLOYEE_H
2. #define EMPLOYEE_H
3.
4. #define NAMESIZE 20
5.
6. struct employee
7. {
8. char name[NAMESIZE + 1];
9. double pay;
10. char dept;
11. };
12.
13. typedef struct employee WORKER, *PWKR;
14.
15. void fill(PWKR p);
16. void output(PWKR p);
17. void fill_emp(PWKR array, int size);
18. void print(PWKR array, int size);
19. PWKR lookup(char* name, PWKR array, int size);
20.
21. #endif
util.h
1. #ifndef UTIL_H
2. #define UTIL_H
3.
4. void stripnewline(char * str);
5.
6. #endif
A Database Application
dbfunc.h
1. #ifndef DBFUNC_H
2. #define DBFUNC_H
3.
4. #define FILENAMESIZE 14
5.
6. void print_db(void);
7. void create_db(void);
8. void retrieve_db(void);
9.
10. #endif
● The main function (shown on next page) drives the rest of the
program. It calls the menu function, which displays the
following menu:
SELECT CHOICE
1) CREATE DATABASE
2) PRINT DATABASE
3) RETRIEVE
4) QUIT
==>
A Database Application
mainprog.c
1. #include <stdio.h>
2. #include <stdlib.h>
3. #include "employee.h"
4. #include "dbfunc.h"
5.
6. #define MAXLINE 100
7. #define CREATE_DB 1
8. #define PRINT_DB 2
9. #define RETRIEVE 3
10. #define QUIT 4
11.
12. int menu(void);
13.
14. main()
15. {
16. char line[MAXLINE];
17. int selection;
18.
19. while(1)
20. {
21. selection = menu();
22. switch(selection)
23. {
24. case CREATE_DB:
25. create_db();
26. break;
27. case PRINT_DB:
28. print_db();
29. break;
30. case RETRIEVE:
31. retrieve_db();
32. break;
33. case QUIT:
34. exit(0);
35. default:
36. printf("IMPOSSIBLE\n");
37. }
38.
39. printf("Press RETURN to continue\n");
40. fgets(line, MAXLINE, stdin);
41. }
42. }
mainprog.c (continued)
43. int menu(void)
44. {
45. int choice;
46. char line[MAXLINE + 1];
47.
48. while(1)
49. {
50. printf("\n\n");
51. printf("SELECT CHOICE\n\n");
52. printf("1) CREATE DATABASE\n");
53. printf("2) PRINT DATABASE\n");
54. printf("3) RETRIEVE\n");
55. printf("4) QUIT\n==>");
56. choice = atoi(fgets(line, MAXLINE, stdin));
57.
58. if(choice < 1 || choice > QUIT)
59. {
60. printf("ILLEGAL CHOICE\n");
61. printf("PRESS RETURN TO CONTINUE\n");
62. fgets(line, MAXLINE, stdin);
63. }
64. else
65. return(choice);
66. }
67. }
fwrite
● fwrite writes some data to the disk. The data written to the
disk is in the exact format as it is in memory. This is called a
binary write. The fwrite prototype is:
int fwrite(void *data, int size, int num, FILE *fp);
/* write an int */
fwrite(&i, sizeof(int), 1, fp);
/* write 10 ints */
fwrite(n, sizeof(int), 10, fp);
/* write a struct */
fwrite(&x, sizeof(WORKER), 1, fp);
/* write 10 structs */
fwrite(a, sizeof(WORKER), 10, fp);
dbfunc.c
1. #include <stdio.h>
2. #include <string.h>
3. #include "employee.h"
4. #include "dbfunc.h"
5. #include "util.h"
6.
7. void create_db()
8. {
9. WORKER person;
10. FILE *fp;
11. char fname[FILENAMESIZE + 1], line[101];
12. int number, i;
13.
14. printf("Enter filename to write to: ");
15. fgets(fname, FILENAMESIZE, stdin);
16. stripnewline(fname);
17. if((fp = fopen(fname, "wb")) == NULL) {
18. printf("Can't open %s\n", fname);
19. return;
20. }
21.
22. printf("How many records to input? ");
23. number = atoi(fgets(line, 100, stdin));
24.
25. for (i = 0; i < number; i++)
26. {
27. fill(&person);
28. fwrite(&person, sizeof(WORKER), 1, fp);
29. }
30.
31. fclose(fp);
32. }
fread
● The fread function is the companion to fwrite. It has the
exact same interface. Use this function to read data that has
been written by fwrite. The fread prototype is:
int fread(void *data, int size, int num, FILE *fp);
dbfunc.c (continued)
33. void print_db()
34. {
35. WORKER person;
36. FILE *fp;
37. char fname[FILENAMESIZE + 1];
38.
39. printf("Enter filename: ");
40. fgets(fname, FILENAMESIZE, stdin);
41. stripnewline(fname);
42.
43. if((fp = fopen(fname,"rb")) == NULL) {
44. printf("Can't open %s\n", fname);
45. return;
46. }
47.
48. while(fread(&person, sizeof(WORKER), 1, fp) > 0 )
49. output(&person);
50.
51. fclose(fp);
52. }
fseek
● Files are often read and written sequentially. The operating
system keeps a pointer to the next record to be read or written.
You can control this pointer with the fseek function. The
prototype for fseek is:
int fseek(FILE *fp, long offset, int origin);
/* back up 1 record */
fseek(fp, size, SEEK_CUR);
fseek
● fseek can be used to update a record.
⏵ Note the use of the fflush function, which causes immediate output
to the file rather than having the output buffered.
WORKER person;
long int size = sizeof(WORKER);
fp = fopen(filename, "r");
dbfunc.c (continued)
53. void retrieve_db()
54. {
55. WORKER w;
56. FILE *fp;
57. char fname[FILENAMESIZE + 1],
58. char pname[NAMESIZE + 2];
59.
60. printf("Enter file name: ");
61. fgets(fname, FILENAMESIZE, stdin);
62. stripnewline(fname);
63. if ((fp = fopen(fname, "r")) == NULL)
64. {
65. printf("Can't open %s\n",fname);
66. return;
67. }
68.
69. while(1)
70. {
71. printf("Which name? ('quit' to exit) ");
72. fgets(pname, NAMESIZE, stdin);
73. stripnewline(pname);
74. if (strcmp(pname, "quit") == 0)
75. break;
76.
77. while((fread(&w, sizeof(WORKER),1, fp)) > 0)
78. {
79. if(strcmp(pname, w.name) == 0)
80. output(&w);
81. }
82. fseek(fp,0L,0);
83. }
84. fclose(fp);
85. }
util.c
1. #include <string.h>
2.
3. void stripnewline(char * str)
4. {
5. int len = strlen(str);
6.
7. if (str[len 1] == '\n')
8. {
9. str[len 1] = '\0';
10. }
11. }
Exercises
1. Extend the database application program by providing a menu
choice, which displays the number of records in the data file.
2. Add another menu choice so that the user can display any
record by specifying the record number.
3. Write a program, which exchanges the first and last records of
a file consisting of records of the WORKER type.
Chapter 8:
a = b + c; // ADD INTEGERS
x = y + z; // ADD FRACTIONS
fraction.c
1. #include <stdio.h>
2. #include <assert.h>
3.
4. #include "fraction.h"
5.
6. FRACTION input()
7. {
8. FRACTION p;
9. printf("input num then denom (n/d) ");
10.
11. // "%d/%d" format => allows
12. // input of the form n/d
13.
14. scanf("%d/%d", &p.n, &p.d);
15. while( getchar() != '\n'); // flush the
16. // newline
17. return(p);
18. }
19.
20. FRACTION create(int numer, int denom)
21. {
22. FRACTION p;
23. p.n = numer;
24. p.d = denom;
25. return(p);
26. }
fraction.c (continued)
27. void print(FPTR p)
28. {
29. int temp;
30.
31. assert(p > d != 0 ); // div by 0
32. temp = gcd(p>n, p>d); // reduce
33. assert(temp != 0); // sanity check
34.
35. p > n = p > n / temp;
36. p > d = p > d / temp;
37. if ( 1 == p > d) // easy reading
38. printf("%d\n", p > n);
39. else if ( 0 == p > n) // easy reading
40. printf("0\n");
41. else
42. printf("%d/%d\n", p > n, p > d);
43. }
44.
45. FRACTION add(FPTR f, FPTR s)
46. {
47. FRACTION p;
48.
49. p.n = f > n * s > d + f > d * s > n ;
50. p.d = f > d * s > d;
51. return(p);
52. }
fraction.c (continued)
53. int gcd(int top, int bot)
54. {
55. int quot, rem;
56.
57. quot = top / bot;
58. rem = top % bot;
59. while(rem != 0)
60. {
61. top = bot;
62. bot = rem;
63. quot = top / bot;
64. rem = top % bot;
65. }
66. return bot;
67. }
mainfrac.c
68. #include <stdio.h>
69. #include "fraction.h"
70.
71. #define MAX 5
72.
73. main()
74. {
75. int i;
76. FRACTION array[MAX];
77. FRACTION s;
78.
79. s = create(0,1);
80.
81. printf("Enter %d fractions\n", MAX);
82. for (i = 0; i < MAX; i++) {
83. printf("input fraction # %d ", i + 1);
84. array[i] = input();
85. }
86.
87. for ( i = 0; i < MAX; i++)
88. s = add(&s, &array[i]);
89.
90. print(&s);
91. }
⏵ initialized with:
• a = create(2,3);
• b = create(4,5);
⏵ added with:
• c = add(&a, &b);
● Complex calculations could proceed as:
c = (a + b) * (a / b);
FRACTION a, b, c;
c = mult(add(&a, &b), divide(&a, &b));
⏵ Note that add must return an fptr rather than a fraction to satisfy
mults arguments.
● Set intersection ^
⏵ A ^ B = { 3, 4 }
● Set union u
⏵ A u B = { 0, 1, 2, 3, 4, 5, 6 }
● Set difference
⏵ A B = { 0, 5, 2 }
● Compliment of a set ~
Set Representation
● There are many representations of sets.
⏵ Abstract sets have no bound.
⏵ Good candidate for dynamic representation
● Instead, we choose the following.
⏵ An array
⏵ A particular size
● Following the method of the fraction data type, we use
typedef to create some new names.
● Next, we choose the operations to be implemented.
set.c (continued)
36. SETP setunion(SETP a, SETP b)
37. {
38. SETP c;
39. int i, j;
40.
41. c = create();
42. for (i = 0; i < a #> howmany; i++)
43. add(a #> array[i], c);
44. for (i = 0; i < b #> howmany; i++) {
45. for (j = 0; j < a #> howmany; j++)
46. if(b #> array[i] == a #> array[j])
47. break;
48. if (j == a #> howmany)
49. add(b #> array[i], c);
50. }
51. return(c);
52. }
mainset.c
53. #include "set.h"
54.
55. main()
56. {
57. SETP c, a, b;
58.
59. a = create();
60. b = create();
61. add(10, b); // ADD ELEMENT 10 TO b
62. add(5, b);
63. add(5, a); // ADD ELEMENT 5 TO a
64. add(6, a);
65. print(a);
66. print(b);
67.
68. c = setunion(a,b); // UNION OF a AND b
69. print(c);
70.
71. free(a);
72. free(b);
73. free(c);
74. }
Exercises
1. Implement the fraction add function with the following function
prototype.
FPTR add(FPTR, FPTR);
Chapter 9:
Linked Lists
⏵ A window
struct window {
int x_upper_left;
int y_upper_left;
int x_lower_right;
int y_lower_right;
};
Lists as Arrays
● A list is a dynamic data structure. An array is fixed.
● This contradiction leads to inefficiencies in:
⏵ Adding to the list
• what about when there is no more room?
Lists as Arrays
● An array can be used to represent a list.
⏵ An array is a fixed size data type.
⏵ Size is based on a worstcase scenario.
⏵ The number of elements in the list would be kept in a separate
variable.
● Array representation could lead to inefficiencies.
⏵ Adding an element
• Since an array is a fixed data structure, there would be no way of
extending it.
⏵ Inserting an element
• All elements below the inserted one would need to be pushed
down one element.
⏵ Deleting an element
• Each element needs to be moved up a position, or, the position of
the deleted one could be marked with a special value.
● Give it a name.
typedef struct passenger PASS, *PP;
typedef int BOOLEAN;
Ordered Lists
● Below is an example of an ordered list.
#include "ordered.h"
main()
{
PP list, node;
initialize(&list);
node = create("mike", 100);
list = order(node, list);
node = create("zeke", 100);
list = order(node, list);
node = create("allen", 100);
list = order(node, list);
node = create("mikey", 100);
list = order(node, list);
print(list);
}
PP order(PP data, PP p)
{
PP prev;
PP save = p;
if(is_empty(p) ||
(strcmp(data #> name, p #> name) < 0 )) {
data #> link = p;
return(data);
}
while((p != NULL) &&
(strcmp(data #> name, p #> name) > 0)) {
prev = p;
p = p #> link;
}
data #> link = prev #> link;
prev #> link = data;
return(save);
}
Ordered Lists
● Same as previous linked list except insertion is a function of a
key field (name for example)
● The function order replaces the combination of insert and
append.
● Elements are now in order.
Circular Lists
● To demonstrate the circular list, devise the following problem.
⏵ Consider a set of integers in the range 0 to n.
⏵ Select a random number i in the range ( 1 n ).
⏵ Eliminate the ith integer from the beginning, then the 2ith integer etc.,
until only one integer remains.
⏵ For example, if i = 3, eliminate 2,5,8 etc.
⏵ For any integer > I, wrap around.
⏵ Which number remains for various values of i?
Circular Lists
circular.c
1. #include <stdio.h>
2. #include <string.h>
3. #include <malloc.h>
4.
5. #include "list.h"
6.
7. void initialize(PP *p)
8. {
9. *p = NULL;
10. }
11.
12. BOOLEAN is_empty(PP p)
13. {
14. return( p == NULL ? 1 : 0 );
15. }
16.
17. void print(PP p)
18. {
19. while (p != NULL) {
20. printf("%s %d\n", p > name, p > f_number);
21. p = p > link;
22. }
23. }
24.
25. PP create(char *name, int number)
26. {
27. PP p;
28.
29. p = (PP) malloc(sizeof(PASS));
30. if( p == NULL ){
31. printf("malloc: no more room\n");
32. return(NULL);
33. }
34.
35. p > f_number = number;
36. strcpy(p > name, name);
37. p > link = NULL;
38. return(p);
39. }
Circular Lists
circular.c (continued)
40. PP insert(PP data, PP p)
41. {
42. data > link = p;
43. return(data);
44. }
45.
46. PP append(PP data, PP p)
47. {
48.
49. PP temp = p;
50. PP prev;
51. if(is_empty(p)) {
52. p = insert(data, p);
53. return(p);
54. }
55. while(p != NULL) {
56. prev = p;
57. p = p > link;
58. }
59. // prev points to last element
60. prev > link = data;
61. return(temp);
62. }
circularmain.c
1. #include <stdio.h>
2. #include <malloc.h>
3.
4. #include "list.h"
5.
6. #define NUMBER 22
7.
8. main()
9. {
10. char map[NUMBER + 1];
11. int i, count;
12. PP list, node, p, prev, save;
13. initialize(&list);
Circular Lists
circularmain.c (continued)
14. for ( i = 0; i < NUMBER; i++) {
15. node = create("tom",i);
16. list = append(node,list);
17. map[i] = 'X';
18. }
19. map[NUMBER] = '\0';
20.
21. p = list;
22. for (i = 0; i < NUMBER 1; i++)
23. p = p > link;
24.
25. p > link = list;
26. p = list;
27.
28. for (count = 0; count < NUMBER 1; count++) {
29. for(i = 0; i < 2; i++) {
30. prev = p;
31. p = p > link;
32. }
33. save = p;
34. prev > link = p > link;
35.
36. map[p > f_number] = '_';
37. printf("%s\n",map);
38. free(p);
39. p = prev > link;
40. }
41. }
Circular Lists
● There are some basic differences between a circular list and a
one directional list.
⏵ There is no first element.
⏵ There is no end of the list.
● There will be a current element.
⏵ This is the starting point for the next list operation.
● Each element points to the next one.
● Circular lists are used heavily in memory management
schemes.
twoway.h
1. #define NAMESIZE 20
2.
3. struct passenger {
4. char name[NAMESIZE];
5. int f_number;
6. struct passenger *link;
7. struct passenger *blink;
8. };
9.
10. typedef struct passenger PASS, *PP;
11. typedef int BOOLEAN;
12.
13. BOOLEAN is_empty(PP); // IS THE LIST EMPTY?
14. PP order(PP, PP); // ADD IN ORDER
15. PP create(char *, int); // CREATE AND FILL ELEMENT
16.
17. void fprint(PP); // PRINT THE LIST FORWARD
18. void bprint(PP); // PRINT THE LIST BACKWARD
BEGIN END
DATA '\0' 'zzz'
LINK 200 NULL
BLINK NULL 100
100 200
twowaymain.c
1. #include <stdio.h>
2. #include <stdlib.h>
3. #include <string.h>
4. #include <malloc.h>
5.
6. #include "twoway.h"
7.
8. main()
9. {
10. int i;
11. PP begin, end, node, list;
12.
13. begin = (PP) malloc(sizeof(PASS));
14. end = (PP) malloc(sizeof(PASS));
15. if(begin == NULL || end == NULL) {
16. printf("malloc: no room\n");
17. exit(1);
18. }
19.
20. strcpy(begin > name, "\0");
21. strcpy(end > name, "z");
Nested Lists
struct flights {
char departure_city[20];
int flight_number;
char destination city[20];
struct flights *flight_pt;
struct passenger *passenger_pt;
};
struct passenger {
char name[20];
char other[100];
struct passenger *ptr;
};
Nested Lists
● There are many other kinds of linked lists applications.
● Consider a list of flights, each with a log of passengers.
6000 100
name pete jane
other data data
ptr &100 NULL
Exercises
1. Write a function for oneway linked lists which swaps two
elements.
void swap(char * name1, char *name2, PP head);
Appendix A:
Software Tools
The cc Command
● Starting the C compiler can be done from:
⏵ the command line
⏵ an integrated environment.
● The C compiler command consists of three phases.
⏵ C preprocessor (CPP)
⏵ C compiler (CC)
⏵ C linker/loader (LD)
Different C Compilers
● There are many environments where C code is compiled.
● UNIX systems have a cc command for command line
compiling.
$ cc program.c
Compiler Options
● By giving the proper option to the C compiler, you can produce
either
⏵ .i Results of the preprocessor
⏵ .c Results of the compile
⏵ .exe (a.out) Results of the loader
Compiler Options
● You can inform the compiler to stop whenever you wish:
standard options include
⏵ Preprocessing
⏵ Compiling
⏵ Assembly listing
⏵ Defining constants
⏵ Producing code for a profiler
Conditional Compilation
● ifdef
#ifdef __STDC__
void *malloc(int);
#else
char *malloc(int);
#endif
#ifndef NAME
#define NAME
#endif
● For debugging
#ifdef DEVELOPING
printf("entering sub1: I = %d\n", i);
#endif
Conditional Compilation
● C code can be conditionally compiled through the use of a few
preprocessor directives
● Debugging can be enabled by placing sequences of #ifdefs at
strategic places in your code
● Debugging levels can be established
⏵ printfs get executed depending upon level of debugging in effect
Libraries
● A library is a file maintained by a system utility
⏵ Consists of compiled files
⏵ Same subject matter
● Most C compilers come with several libraries
⏵ Portable I/O library
⏵ Math library
⏵ Graphics library
⏵ Others
Libraries
● A library on a computer system is a file. which has the following
format.
⏵ A table of contents (or index)
⏵ A set of modules (or members)
⏵ In DOS Windows, and OS/2, they can be found under the main
directory for your compiler.
/tc/lib /msvc/lib
Libraries
● To build a library, construct the source file.
// square.c
double square(double a)
{
return(a * a);
$ cc c square.c // COMPILE IT
// dist.c
#include <math.h>
double dist(int x1, int x2, int y1, int y2)
{
double x = x1 # y1;
double y = x2 # y2;
return(sqrt(sum_of_squares(x, y)));
}
Libraries
● We will use the unix ar command to illustrate basic principles
about libraries.
⏵ Saves compilation time
⏵ Easy to reuse functionality
● Suppose we want to build a library of math functions.
⏵ Build the first function and compile it.
$ cc c square.c
⏵ Place it in a library.
$ ar r mathlib.a square.o
● Note that failure to include the header file will leave strange
results
// A COMMON C BUG IS THE FAILURE TO
// MAKE VISIBLE THE PROTOTYPE FOR atof
//
main( )
{
char *s = "12345.65";
printf("%f\n", atof(s));
}
Libraries
● Create a header file with the function prototypes for the
functions placed in the library.
● When a program needs some of the functionality built into the
library, name the library on the command line:
$ cc distance.c mathlib.a
$ edit fun2.c
//
// EVERYTHING IS NEEDLESSLY RECOMPILED
//
$ cc prog.c fun1.c fun2.c fun3.c
$
$ edit fun2.c
//
// NOTICE THAT fun2.c IS RECOMPILED
//
$ cc prog.o fun1.o fun2.c fun3.o
$
An Example Makefile
fun1.o: fun1.c
cc c fun1.c
fun2.o fun2.c
cc c fun2.c
⏵ Rules lines
<TAB> SHELL COMMAND
prog
i initialize
s.software.c
CTRL software.c 1.0 CTRL
● Use the get e command to get a copy of the file for edit.
$ get e s.software.c
Appendix B:
Library Functions
#ifdef DOS
strcpy(command, "copy ");
#else
strcpy(command, "cp ");
#endif
system
● The system function allows you to execute from your C
program any command executable from the command line.
int system(char *);
system("cls");
system("dir");
void fun(void);
void fun1(void);
main()
{
atexit(fun);
atexit(fun2);
exit(0);
}
void fun()
{
printf("1\n");
}
void fun2()
{
printf("2\n");
}
signal
● The prototype for signal is in signal.h.
void signal(int sig_num, void (*response)(int));
signal
● A signal is a message (a small integer) sent to an executable
program by the operating system.
● Signals can be initiated by:
⏵ a user (CTRLC);
⏵ a program (ALARM); or
⏵ the operating system (KILL).
● When a program receives a signal, it can respond by:
⏵ ignoring the signal;
⏵ performing a system action (program termination); or
⏵ executing your own signal handler.
● The signal function is used to set a program response to a
particular signal.
● There is no uniform support of signals among all operating
systems.
● When your program begins, the default response (which is
program termination) is established for all signals
● At various places in your program, executing the signal function
sets a response to a particular signal.
strtok
● strtok tokenizes a string.
⏵ You specify the delimeters between the tokens.
#include <stdio.h>
#include <string.h>
main()
{
char *s = "this is a string";
char *x, *delim = " ";
x = strtok(s,delim);
while(x != NULL) {
printf("%s\n",x);
x = strtok(NULL,delim);
}
}
strtok
● The strtok function tokenizes a string with respect to a set of
delimeters that you supply.
⏵ Useful for breaking a string into words
⏵ Prototype for strtok from string.h
char *strtok(char *string, char *delimeters);
int main(void)
{
char src[] = "*****";
char dest[] = "abcdefghijlkmnopqrstuvwxyz";
char *ptr;
● memset
#define NAMESIZE 20
struct example {
char name[NAMESIZE];
int age;
char dept;
};
main()
{
struct example item;
memset(&item, 0, sizeof(struct example));
qsort
● A few invocations of the qsort function
#include <stdlib.h>
++argv;
##argc;
//
// SORT STRINGS FORM THE COMMAND LINE
//
qsort(argv,argc,sizeof(char *),qcomp);
for(i = 0; i < argc ; i++)
printf("%s\n",argv[i]);
}
qsort
● The qsort function performs a quicksort on the data you
supply.
● You must give qsort four arguments.
⏵ arg1 Address of the data to be sorted
⏵ arg2 How many items to be sorted
⏵ arg3 The sizeof each item
⏵ arg4 A comparison function
bsearch
#include <stdlib.h>
int numcmp(int *, int *);
bsearch
● The bsearch function performs a binary search.
● Elements must be in ascending order.
● The bsearch function requires five arguments.
⏵ arg1 Address of the key to be found
⏵ arg2 Address of the table to be searched
⏵ arg3 Number of elements in the table
⏵ arg4 sizeof each element
⏵ arg5 Comparison routine
ptr = bsearch(&val, x, 5, sizeof(int), numcmp);
strstr
● A simple version of a grep program
#include <string.h>
#include <stdio.h>
#define MAXLINE 100
strstr
● The strstr function takes two strings and determines whether
the second is contained in the first.
⏵ If it isn't, null is returned.
⏵ Else, a pointer at the containing string is returned.
char *strstr(char *string, char *substring);
#include <stdio.h>
#include <string.h>
if ( argc != 2) {
printf("Usage: env argument\n");
exit(1);
}
while(*envp != NULL) {
if(strstr(*envp, argv[1])) {
pc = strchr(*envp,'=');
if(strlen(argv[1]) == (pc # *envp)){
printf("%s\n", *envp);
exit(1);
}
}
++envp;
}
printf("%s NOT FOUND\n", argv[1]);
}
$ printenv ME
ME: not in the environment
$ set ME=Michael
$ printenv ME
michael
#include <stdio.h>
#include <string.h>
#define MAXLINE 100
main()
{
char line[MAXLINE], digits[11];
int n,d;
strcpy(digits,"0123456789");
while(1) {
printf("enter string ");
gets(line);
if(strcmp(line,"quit") == 0)
break;
else if((d=strspn(line, digits))==
strlen(line))
printf("%s is all digits\n", line);
else if((n=strcspn(line, digits))==
strlen(line))
printf("%s has no digits\n", line);
else {
printf("%s is mixed\n", line);
printf("first non digit is %d\n", d + 1);
printf("first digit is %d\n", n + 1);
}
}
}
Exercises
1. Write a function which determines if a string is of the exact
form.
.....ddddd.dd
Appendix C:
File Access
FILE TABLE
stdin
stdout
stderr
fp1 → input
fp 2 → myfile
fopen returns:
Access Modes
● Some of the access modes are:
MODE OPEN FILE FOR: COMMENTS
"r" read error if not there
"w" create truncate if already exists
"a" append write at end of file
"r+" read/write error if does not exist
"w+" read/write truncated if exists
"a+" read/write only at end of file
● Open examples
FILE *fp; // fp: POINTER TO A FILE
char fname[256]
char mode[20];
//
// watch out for \\ to get a \ for DOS and OS/2 files
//
fp = fopen("c:\\c\\cnotes\\chap22","r"); // DRIVE
fp = fopen("/usr/include/stdio.h","r"); // OR PATH
Access Modes
● An access mode specifies the type of I/O to be performed on a
file.
⏵ There are many combinations of access to a file.
● I/O is considered to be textual in mode.
⏵ If binary mode is desired, the character b must be part of the mode
argument.
⏵ This style is used mostly with structures (not necessary on unix).
● The information, required by fopen, can be obtained from the
user with the following sequence.
printf("enter a filename "); // REQUEST INTERACTIVELY
gets(fname); // USER SUPPLIES NAME
printf("enter the mode ");
gets(mode); // AND PERHAPS MODE
fp = fopen(fname, mode);
if( fp == NULL) {
printf("couldn't open 'myfile'\n");
exit(1);
}
OR
#include <stdio.h>
// ERROR CHECKING
// 1. Make sure argument count is correct
if ( argc != 3) {
fprintf(stderr, "usage: mycopy in out\n");
exit(1);
}
fpin = fopen(argv[1],"r");
fpout = fopen(argv[2],"w");
exit(0);
}
©2016 UMBC Training Centers
277
INTERMEDIATE C PROGRAMMING APPENDIX C: FILE ACCESS
⏵ Each function above performs I/O on the file associated with the file
pointer (fp).
⏵ Each has a direct analog with a function dedicated to either the
standard input or standard output.
● stdio.h defines three constants of type FILE *
stdin used with any input function
stdout used with any output function
stderr used with any output function
⏵ These names can be used as file pointer constants. In this way, the
functions above can also read from the standard files.
● Line at a time
define MAX 100
char line[MAX];
Interpreting Input
● Interpreting a line of input as a number
⏵ Get the line:
char line[100];
● Any of the input lines below will cause scanf to return the
value 1
mike jane
mike 253ab
mike ab352
scanf Variants
● sscanf is very useful for problems such as the one below.
int number;
char line[MAX];
char name[NAMESIZE];
char dum[DUMMYSIZE]; // GOBBLE EXTRA INPUT
scanf Variants
● The list below summarizes the scanf variations.
char info[100];
char name[NAMESIZE];
int x = 20;
FILE *fp;
scanf Variants
● fscanf example
FILE *fp;
char fname[20], lname[20];
fp = fopen("inputfile", "r");
if( fp == NULL) {
error("fopen error\n");
n = fscanf(fp,"%s %s\n", fname, lname);
if( n != 2)
error("scanf error\n");
● scanf example
#include <stdio.h>
#define WORDSIZE 40
main()
{
char word[WORDSIZE];
printf("%s\n",wd);
}
}
scanf Variants
● fscanf is like scanf except the input comes from the file
associated with the file pointer.
● While scanf does not provide reliable error checking, it is safe
to use if you need to input string information.
● A nice use of scanf is to read a file a word at a time.
printf Variants
● sprintf is useful for "gluing" unlike data together.
char string[100];
char temp1[10];
char temp2[10];
int number;
strcpy(temp1,"hello");
strcpy(temp2,"goodbye");
number = 356;
sprintf(string,"%s%d%s", temp1, number, temp2);
n = strlen(string); // n = 15
printf Variants
● printf has analogous variants to scanf.
⏵ printf sends data to the display.
● sprintf data to a string
⏵ useful for concatenating unlike data
int x;
char name[NAMSESIZE], info[SIZE];
sprintf(info,"data %d\n", x);
#include <stdio.h>
ct = 0;
while (( c = getc(fp)) != EOF)
if (c == '\n')
ct++;
Servicing Errors
● Some I/O functions return eof both on error and end of file.
● The functions feof and ferror can be used to distinguish
these two conditions.
⏵ feof returns true at endoffile/false otherwise
⏵ ferror returns true on error/false otherwise
while(! foef(fp)) // NOT EOF
if (( c = getc(fp)) == EOF)
// MUST BE ERROR
Servicing Errors
● Your error processing may look like this.
fp = fopen(filename,mode);
if(fp == NULL) {
if(errno == ENOENT)
do one thing
else
do another
}
Servicing Errors
● Exact system error messages and their codes can be found in
the file errno.h in the include directory.
● On Unix systems, this directory is always
/usr/include.
==>
//
//employee.h
//
#define NAMESIZE 20
struct employee {
char name[NAMESIZE];
float pay;
char dept;
};
Binary I/O
● There are two methods of writing data to a file.
⏵ Convert from in memory format to character (stream I/O)
• Human readable
#include "employee.h"
main( )
{
char line[MAXLINE];
int selection;
while(1) {
selection = menu();
switch(selection) {
case QUIT:
exit(0);
case CREATE_DB:
create_db();
break;
case PRINT_DB:
print_db( );
break;
case RETRIEVE:
retrieve_db( );
break;
default:
printf("SHOULD BE IMPOSSIBLE\n");
break;
}
printf("RETURN to continue\n");
fgets(line,MAXLINE,stdin);
}
}
void create_db(void)
{
WORKER person;
FILE *fp;
char fname[FILENAMESIZE], line[100];
int number, i;
fwrite(&i,sizeof(int),1,fp); // INT
fwrite(n,sizeof(int),10, fp); // AN ARRAY
fwrite(n,sizeof(int),5,fp); // FIRST HALF
fwrite(&x,sizeof(WORKER),1,fp); // WORKER
fwrite(a,sizeof(WORKER),10,fp); // ARRAY
fwrite(a,sizeof(WORKER),5,fp); // FIRST HALF
fwrite(a + 5,sizeof(WORKER),5,fp);// LAST HALF
fwrite
● Records are typed by the user and then copied to disk using
fwrite.
int fwrite(void *data, int size, int hmany, FILE
*file);
fclose(fp);
}
while((num=fread(bank,sizeof(WORKER),CHUNK,fp))>0)
for (i = 0; i < num; i++)
process each record!!
fread
● print_db reads the file created by create_db.
int fread(void *data, int size, int hmany, FILE
*file);
retrieve_db Function
void retrieve_db(void)
{
WORKER person;
char fname[FILENAMESIZE], pname[NAMESIZE];
while((fread(&person, sizeof(WORKER),1,fp))>0)
if(strcmp(pname,person.name) == 0)
output(&person);
fseek(fp,0L,0);
}
fclose(fp);
}
fseek
● retrieve_db is similar to print_db except that only
selected records are printed.
● Files are normally read and written sequentially.
⏵ The operating system keeps a pointer to the next record.
⏵ You can control this pointer with the fseek function
int fseek(FILE *file, long offset, int origin);
fp = fopen(file,mode);
fseek(fp,0L,2);
size = ftell(fp);
recs = size / sizeof(WORKER);
printf("file %s is %ld bytes\n", file, size);
printf("file %s has %ld records\n", file, recs);
Exercises
1. Write the program compare which displays information about
the two files named on the command line.
C> compare file1 file2
file1 and file2 are the same
C> compare file1 file3
file1 and file3 are different
C>