advanced c programming - yarbis

136
Department of Mathematical Engineering Programming Language II Course Material

Upload: others

Post on 12-Feb-2022

7 views

Category:

Documents


0 download

TRANSCRIPT

Department of Mathematical Engineering

Programming Language II Course Material

Course Syllabus

Overview of C Programming

Functions

Recursion

Pointers

Structures

File Handling Material Collected /Edited from:

C: How to Program (6th Edition) , Paul Deitel, Harvey M. Deitel, Prentice Hall

Schaum's Outline of Programming with C, Byron Gottfried, McGraw-Hill

Practical C Programming, Third Edition, Steve Oualline, O'Reilly Media

Richard G. Clegg’s C Programming Course Material, http://www.richardclegg.org/ccourse/

Joe Carthy, http://www.csi.ucd.ie/staff/jcarthy/home/2ndYearUnix

C ile Veri Yapıları, Ibrahim Akman, Sas Bilişim Yayınları

Bütün Yönleriyle Turbo C, Mesut Çoban, Türkmen Kitabevi

C Dersi: Programlamaya Giriş, Nergiz Ercil Çağıltay, Fügen C. Selbes, Gül Tokdemir, Çiğdem Turhan, Seçkin Yayınevi

Temel C Programlama, G. Murat Taşbaşı, Altaş Yayıncılık

İleri C Programlama, G. Murat Taşbaşı, Altaş Yayıncılık

C Programming Course Overview• What is to be taught?:

– How to program C stylishly and elegantly.– Small and fast mathematical programs.– Documenting code.– Writing good algorithms.– NO fancy graphics that go out of date quickly.

• Why teach programming?:– Some maths relies on computers.– Simulation lets us apply maths to the real world.– It might get you a job after you finish your maths course.– It can be fun to do.

Why teach C?• C is small (only 32 keywords).• C is common (lots of C code about).• C is stable (the language doesn’t change much).• C is quick running.• C is the basis for many other languages (Java, C++,

awk, Perl).• It may not feel like it but C is one of the easiest

languages to learn.• NOTE: Obviously programmers will find this course

easier than everyone else. BUT, this course is for non-programmers. If you cannot understand what I say, please please ask me either in the lecture or afterwards.

Some programmer jargon• Some words that will be used a lot:

– Source code: The stuff you type into the computer. The program you are writing.

– Compile (build): Taking source code and making a program that the computer can understand.

– Executable: The compiled program that the computer can run.

– Language: (Special sense) The core part of C central to writing C code.

– Library: Added functions for C programming which are bolted on to do certain tasks.

– Header file: Files ending in .h which are included at the start of source code.

More about Hello World

#include <stdio.h> /* My first C program which prints Hello World */ int main (int argc, char *argv[]) { printf ("Hello World!\n"); return 0; }

Preprocessor

Library command

main() means “start here”

Comments are good

Return 0 from main means our programfinished without errorsBrackets

define code blocks

C doesn’t care much about spaces#include <stdio.h>/* My first C program which prints Hello World */int main(){printf("Hello World!\n");return 0;}

#include <stdio.h>/* My firstC programwhich printsHello World */intmain(){printf("Hello World!\n");return0;}

Both of these programs are exactlythe same as the original as far asyour compiler is concerned.Note that words have to be kept togetherand so do things in quotes.In the next lecture we'll learn how weSHOULD lay out our C program tomake them look nice

Keywords of C• Flow control (6) – if, else, return, switch, case, default

• Loops (5) – for, do, while, break, continue• Common types (5) – int, float, double, char, void

• structures (3) – struct, typedef, union• Counting and sizing things (2) – enum, sizeof• Rare but still useful types (7) – extern, signed, unsigned, long, short, static, const

• Evil keywords which we avoid (1) – goto• Wierdies (3) – auto, register, volatile

Types of variable• We must declare the type of every variable we

use in C. • Every variable has a type (e.g. int) and a

name.• We already saw int, double and float.• This prevents some bugs caused by spelling

errors (misspelling variable names).• Declarations of types should always be

together at the top of main or a function (see later).

• Other types are char, signed, unsigned, long, short and const.

Naming variables• Variables in C can be given any name made from

numbers, letters and underlines which is not a keyword and does not begin with a number.

• A good name for your variables is important

• Ideally, a comment with each variable name helps people know what they do.

• In coursework I like to see well chosen variable names and comments on variables (I don’t always do this in notes because there is little space).

int a,b;double d;/* This isa bit cryptic */

int start_time;int no_students;double course_mark;/* This is a bit better */

The char type• char stores a character variable• We can print char with %c• A char has a single quote not a double quote.• We can use it like so:int main(){

char a, b;a= 'x'; /* Set a to the character x */printf ("a is %c\n",a);b= '\n'; /* This really is one character*/printf ("b is %c\n",b);return 0;

}

More types: Signed/unsigned, long, short, const

• unsigned means that an int or char value can only be positive. signed means that it can be positive or negative.

• long means that int, float or double have more precision (and are larger) short means they have less

• const means a variable which doesn't vary –useful for physical constants or things like pi or e

short int small_no; unsigned char uchar;long double precise_number;short float not_so_precise;const short float pi= 3.14;const long double e= 2.718281828;

A short note about ++• ++i means increment i then use it• i++ means use i then increment itint i= 6;printf ("%d\n",i++); /* Prints 6 sets i to 7 */

int i= 6;printf ("%d\n",++i); /* prints 7 and sets i to 7 */

Note this important difference

It is easy to confuse yourself and others with the difference between ++i and i++ - it is best to use them only in simple ways.

All of the above also applies to --.

Some simple operations for variables• In addition to +, -, * and / we can also use +=, -=, *=, /=, -- and % (modulo)

• -- (subtract one) e.g. countdown--;• += (add to a variable) e.g. a+= 5;• -= (subtract from variable) e.g. num_living-= num_dead;

• *= (multiply a variable) e.g. no_bunnies*=2;• /= (divide a variable) e.g. fraction/= divisor;• (x % y) gives the remainder when x is divided by y• remainder= x%y; (ints only)

Casting between variables• Recall the trouble we had dividing ints• A cast is a way of telling one variable

type to temporarily look like another.int a= 3;int b= 4;double c;c= (double)a/(double)b;

By using (type) in front of a variable we tell the variable toact like another type of variable. We can cast between anytype. Usually, however, the only reason to cast is to stopints being rounded by division.

Cast ints a and b to be doubles

What is a function?• The function is one of the most basic things to

understand in C programming.• A function is a sub-unit of a program which

performs a specific task.• We have already (without knowing it) seen one

function from the C library – printf.• We need to learn to write our own functions.• Functions take arguments (variables) and may

return an argument.• Think of a function as extending the C

language to a new task.• Or perhaps variables are NOUNS functions are

VERBS.

An example function#include <stdio.h> int maximum (int, int); /* Prototype – see later in lecture */ int main(int argc, char*argv[]) { int i= 4; int j= 5; int k; k= maximum (i,j); /* Call maximum function */ printf ("%d is the largest from %d and %d\n",k,i,j); printf ("%d is the largest from %d and %d\n",maximum(3,5), 3, 5); return 0; } int maximum (int a, int b) /* Return the largest integer */ { if (a > b) return a; /* Return means "I am the result of the function"*/ return b; /* exit the function with this result */ }

Prototype the function

Call the function

The function itself

function header

Functions can access other functions• Once you have written a function, it can be

accessed from other functions. We can therefore build more complex functions from simpler functions

int max_of_three (int, int, int); /* Prototype*/ . . /* Main and rest of code is in here */ . int max_of_three (int i1, int i2, int i3) /* returns the maximum of three integers */ { return (maximum (maximum(i1, i2), i3)); }

void functions• A function doesn't have to take or return

arguments. We prototype such a function using void.

void print_hello (void); void print_hello (void) /* this function prints hello */ { printf ("Hello\n"); }

Prototype (at top of file remember)

void odd_or_even (int num) /* this function prints odd or even appropriately */ { if ((num % 2) == 0) { printf ("Even\n"); return; } printf ("Odd\n"); }

void odd_or_even (int);

Function takes and returnsvoid (no arguments)

Function which takes oneint arguments and returns none

Another prototype

Notes about functions

• A function can take any number of arguments mixed in any way.

• A function can return at most one argument.• When we return from a function, the values of

the argument HAVE NOT CHANGED.• We can declare variables within a function just

like we can within main() - these variables will be deleted when we return from the function

Where do functions go in the program

• Generally speaking it doesn't matter too much. • main() is a function just like any other (you could

even call it from other functions if you wanted.• It is common to make main() the first function in

your code.• Functions must be entirely separate from each

other.• Prototypes must come before functions are used.• A usual order is: Prototypes THEN main THEN

other functions.

What are these prototype things?• A prototype tells your C program what to

expect from a function - what arguments it takes (if any) and what it returns (if any)

• Prototypes should go before main()• #include finds the prototypes for library

functions (e.g. printf)• A function MUST return the variable type we

say that it does in the prototype.

What is scope?• The scope of a variable is where it can be used

in a program• Normally variables are local in scope - this

means they can only be used in the function where they are declared (main is a function)

• We can also declare global variables.• If we declare a variable outside a function it

can be used in any function beneath where it is declared

• Global variables are A BAD THING

The print stars example#include <stdio.h> void print_stars(int); int main() { int i; for (i= 0; i < 5; i++) print_stars(5); return 0; } void print_stars (int n) { int i; for (i= 0; i < n; i++) printf ("*"); printf ("\n"); }

This program prints five rows offive stars

This prints 'n' stars and thena new line character

Loop around 5 times to print the stars

*************************

Variables here are LOCAL variables

Why global is bad

#include <stdio.h> void print_stars(int); int i; /* Declare global i */ int main() { for (i= 0; i < 5; i++) print_stars(5); return 0; } void print_stars (int n) { for (i= 0; i < n; i++) printf ("*"); printf ("\n"); }

This program onlyprints ONE rowof five stars

Variable here is global variable

Thinking like the computer for debugging

• A good technique for "debugging" code is to think of yourself in place of the computer.

• Go through all the loops in the program and ask "what is in each variable?"

• Each time you go through a loop ask "is the condition met" and "should I continue"

• The factorial program shows how to do this.

Factorial program (and thoughts)int main(){

int number= 4;int answer;int count;

answer= 1;count= number;while (count >= 0) {

answer= answer* count;count--;

}printf ("%d! = %d\n",

number,answer);return 0;

}

number= 4answer= 1count= 4enter while loopanswer= 1*4=4count=3enter while loopanswer=4*3= 12count=2enter while loopanswer=12*2= 24count= 1enter while loopanswer= 24*1= 24count= 0enter while loopanswer= 24*0= 0AHA – I see!!!

Other techniques for debugging• Check missing brackets and commas.• Check that you have a semicolon at the end of

every line which needs one.• Put in some printfs– if you know what your

program is DOING you will know what it is DOING WRONG.

• Try to explain to someone else what the program is meant to do.

• Take a break, get a cup of coffee and come back to it fresh. (Debugging is FRUSTRATING).

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

5.13 Recursion

• Recursive functions – Functions that call themselves– Can only solve a base case– Divide a problem up into

• What it can do• What it cannot do

– What it cannot do resembles original problem– The function launches a new copy of itself (recursion

step) to solve what it cannot do– Eventually base case gets solved

• Gets plugged in, works its way up and solves whole problem

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

5.13 Recursion

• Example: factorials– 5! = 5 * 4 * 3 * 2 * 1

– Notice that• 5! = 5 * 4!

• 4! = 4 * 3! ...

– Can compute factorials recursively – Solve base case (1! = 0! = 1) then plug in

• 2! = 2 * 1! = 2 * 1 = 2;

• 3! = 3 * 2! = 3 * 2 = 6;

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

5.13 Recursion

5!

(a) Sequenc e of rec ursive c a lls. (b) Values re turned from eac h recursive c a ll.

Fina l va lue = 120

5! = 5 * 24 = 120 is returned

4! = 4 * 6 = 24 is re turned

2! = 2 * 1 = 2 is re turned

3! = 3 * 2 = 6 is re turned

1 re turned

5 * 4!

1

4 * 3!

3 * 2!

2 * 1!

5!

5 * 4!

1

4 * 3!

3 * 2!

2 * 1!

Outline

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

fig05_14.c (Part 1 of 2)

1 /* Fig. 5.14: fig05_14.c 2 Recursive factorial function */ 3 #include <stdio.h> 4 5 long factorial( long number ); /* function prototype */ 6 7 /* function main begins program execution */ 8 int main() 9 { 10 int i; /* counter */ 11 12 /* loop 10 times. During each iteration, calculate 13 factorial( i ) and display result */ 14 for ( i = 1; i <= 10; i++ ) { 15 printf( "%2d! = %ld\n", i, factorial( i ) ); 16 } /* end for */ 17 18 return 0; /* indicates successful termination */ 19 20 } /* end main */ 21

Outline

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

fig05_14.c (Part 2 of 2)

1! = 12! = 23! = 64! = 245! = 1206! = 7207! = 50408! = 403209! = 362880

10! = 3628800

22 /* recursive definition of function factorial */ 23 long factorial( long number ) 24 { 25 /* base case */ 26 if ( number <= 1 ) { 27 return 1; 28 } /* end if */ 29 else { /* recursive step */ 30 return ( number * factorial( number - 1 ) );

31 } /* end else */ 32 33 } /* end function factorial */

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

5.14 Example Using Recursion: The Fibonacci Series

• Fibonacci series: 0, 1, 1, 2, 3, 5, 8...– Each number is the sum of the previous two – Can be solved recursively:

• fib( n ) = fib( n - 1 ) + fib( n – 2 )

Code for the fibonacci functionlong fibonacci( long n ){

if (n == 0 || n == 1) // base case

return n;else

return fibonacci( n - 1) +fibonacci( n – 2 );

}

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

5.14 Example Using Recursion: The Fibonacci Series

• Set of recursive calls to function fibonacci

f( 3 )

f( 1 )f( 2 )

f( 1 ) f( 0 ) return 1

return 1 return 0

return +

+return

Outline

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

fig05_15.c (Part 1 of 2)

1 /* Fig. 5.15: fig05_15.c 2 Recursive fibonacci function */ 3 #include <stdio.h> 4 5 long fibonacci( long n ); /* function prototype */ 6 7 /* function main begins program execution */ 8 int main() 9 { 10 long result; /* fibonacci value */ 11 long number; /* number input by user */ 12 13 /* obtain integer from user */ 14 printf( "Enter an integer: " ); 15 scanf( "%ld", &number ); 16 17 /* calculate fibonacci value for number input by user */ 18 result = fibonacci( number ); 19 20 /* display result */ 21 printf( "Fibonacci( %ld ) = %ld\n", number, result ); 22 23 return 0; /* indicates successful termination */ 24 25 } /* end main */ 26

Outline

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

fig05_15.c (Part 2 of 2)

Program OutputEnter an integer: 0Fibonacci( 0 ) = 0

Enter an integer: 1Fibonacci( 1 ) = 1

Enter an integer: 2Fibonacci( 2 ) = 1

Enter an integer: 3Fibonacci( 3 ) = 2

Enter an integer: 4Fibonacci( 4 ) = 3

27 /* Recursive definition of function fibonacci */ 28 long fibonacci( long n ) 29 { 30 /* base case */ 31 if ( n == 0 || n == 1 ) { 32 return n; 33 } /* end if */ 34 else { /* recursive step */ 35 return fibonacci( n - 1 ) + fibonacci( n - 2 );

36 } /* end else */ 37 38 } /* end function fibonacci */

Outline

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

Program Output (continued)

Enter an integer: 5Fibonacci( 5 ) = 5

Enter an integer: 6Fibonacci( 6 ) = 8

Enter an integer: 10Fibonacci( 10 ) = 55

Enter an integer: 20Fibonacci( 20 ) = 6765

Enter an integer: 30Fibonacci( 30 ) = 832040

Enter an integer: 35Fibonacci( 35 ) = 9227465

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

5.14 Example Using Recursion: The Fibonacci Series

fibonacci( 3 )

return

return

+

+ return 1

return 1

fibonacci( 2 ) fibonacci( 1 )

fibonacci( 1 ) fibonacci( 0 )

return 0

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

5.15 Recursion vs. Iteration

• Repetition– Iteration: explicit loop– Recursion: repeated function calls

• Termination– Iteration: loop condition fails– Recursion: base case recognized

• Both can have infinite loops• Balance

– Choice between performance (iteration) and good software engineering (recursion)

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

7.2 Pointer Variable Definitions and Initialization

• Pointer variables– Contain memory addresses as their values– Normal variables contain a specific value (direct reference)

– Pointers contain address of a variable that has a specific value (indirect reference)

– Indirection – referencing a pointer value

count

7

count

7countPtr

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

7.2 Pointer Variable Definitions and Initialization

• Pointer definitions– * used with pointer variables

int *myPtr;

– Defines a pointer to an int (pointer of type int *)

– Multiple pointers require using a * before each variable definition

int *myPtr1, *myPtr2;

– Can define pointers to any data type– Initialize pointers to 0, NULL, or an address

• 0 or NULL – points to nothing (NULL preferred)

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

7.3 Pointer Operators

• & (address operator)– Returns address of operand

int y = 5;

int *yPtr;

yPtr = &y; /* yPtr gets address of y */

yPtr “points to” y

yPtr

y

5yptr

500000 600000

y

600000 5

Address of yis value of yptr

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

7.3 Pointer Operators

• * (indirection/dereferencing operator)– Returns a synonym/alias of what its operand points to– *yptr returns y (because yptr points to y)– * can be used for assignment

• Returns alias to an object*yptr = 7; /* changes y to 7 */

– Dereferenced pointer (operand of *) must be an lvalue(no constants)

• * and & are inverses – They cancel each other out

OutlineOutline

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

1 /* Fig. 7.4: fig07_04.c 2 Using the & and * operators */ 3 #include <stdio.h> 4 5 int main() 6 { 7 int a; /* a is an integer */ 8 int *aPtr; /* aPtr is a pointer to an integer */ 9 10 a = 7; 11 aPtr = &a; /* aPtr set to address of a */ 12 13 printf( "The address of a is %p" 14 "\nThe value of aPtr is %p", &a, aPtr ); 15 16 printf( "\n\nThe value of a is %d" 17 "\nThe value of *aPtr is %d", a, *aPtr ); 18 19 printf( "\n\nShowing that * and & are complements of " 20 "each other\n&*aPtr = %p" 21 "\n*&aPtr = %p\n", &*aPtr, *&aPtr ); 22 23 return 0; /* indicates successful termination */ 24 25 } /* end main */

fig07_04.cThe address of a is the value of aPtr.

The * operator returns an alias to what its operand points to. aPtrpoints to a, so *aPtr returns a.

Notice how * and & are inverses

OutlineOutline

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

Program Output

The address of a is 0012FF7CThe value of aPtr is 0012FF7C

The value of a is 7The value of *aPtr is 7

Showing that * and & are complements of each other.&*aPtr = 0012FF7C*&aPtr = 0012FF7C

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

Operators Associativity Type () [] left to right highest + - ++ -- ! * & (type) right to left unary * / % left to right multiplicative + - left to right additive < <= > >= left to right relational == != left to right equality && left to right logical and || left to right logical or ?: right to left conditional = += -= *= /= %= right to left assignment , left to right comma Fig. 7.5 Operator precedence.

7.3 Pointer Operators

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

7.4 Calling Functions by Reference

• Call by reference with pointer arguments– Pass address of argument using & operator– Allows you to change actual location in memory– Arrays are not passed with & because the array name is

already a pointer

• * operator – Used as alias/nickname for variable inside of function

void double( int *number )

{

*number = 2 * ( *number );

}

– *number used as nickname for the variable passed

OutlineOutline

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

fig07_06.c

1 /* Fig. 7.6: fig07_06.c 2 Cube a variable using call-by-value */ 3 #include <stdio.h> 4 5 int cubeByValue( int n ); /* prototype */ 6 7 int main() 8 { 9 int number = 5; /* initialize number */ 10 11 printf( "The original value of number is %d", number ); 12 13 /* pass number by value to cubeByValue */ 14 number = cubeByValue( number ); 15 16 printf( "\nThe new value of number is %d\n", number ); 17 18 return 0; /* indicates successful termination */ 19 20 } /* end main */ 21 22 /* calculate and return cube of integer argument */ 23 int cubeByValue( int n ) 24 {

25 return n * n * n; /* cube local variable n and return result */ 26 27 } /* end function cubeByValue */

The original value of number is 5The new value of number is 125

OutlineOutline

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

1 /* Fig. 7.7: fig07_07.c 2 Cube a variable using call-by-reference with a pointer argument */ 3 4 #include <stdio.h> 5 6 void cubeByReference( int *nPtr ); /* prototype */ 7 8 int main() 9 { 10 int number = 5; /* initialize number */ 11 12 printf( "The original value of number is %d", number ); 13 14 /* pass address of number to cubeByReference */ 15 cubeByReference( &number ); 16 17 printf( "\nThe new value of number is %d\n", number ); 18 19 return 0; /* indicates successful termination */ 20 21 } /* end main */ 22 23 /* calculate cube of *nPtr; modifies variable number in main */ 24 void cubeByReference( int *nPtr ) 25 { 26 *nPtr = *nPtr * *nPtr * *nPtr; /* cube *nPtr */ 27 } /* end function cubeByReference */

fig07_07.c

Notice how the address of number is given -cubeByReference expects a pointer (an address of a variable).

Inside cubeByReference, *nPtr is used (*nPtr is number).

Notice that the function prototype takes a pointer to an integer.

The original value of number is 5The new value of number is 125

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

int main(){

int number = 5;

number=cubeByValue(number);

}

int cubeByValue( int n )

{

return n * n * n;}

number

5

n

Before main calls cubeByValue :

undefined

int main()

{

int number = 5;

number = cubeByValue( number );

}

int cubeByValue( int n )

{

return n * n * n;

}

number

5

n

After cubeByValue receives the call:

5

125

int cubeByValue( int n )

{

return n * n * n;

}

int main()

{

int number = 5;

number = cubeByValue( number );

}

number

5

n

After cubeByValue cubes parameter n and before cubeByValue returns to main :

5

Fig. 7.8 Analysis of a typical call-by-value. (Part 1 of 2.)

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

125

int main()

{

int number = 5;

number = cubeByValue( number );

}

int cubeByValue( int n )

{

return n * n * n;

}

number

5

n

After cubeByValue returns to main and before assigning the result to number:

undefined

125125

int main()

{

int number = 5;

number = cubeByValue( number );

}

int cubeByValue( int n )

{

return n * n * n;

}

number

125

n

After main completes the assignment to number:

undefined

Fig. 7.8 Analysis of a typical call-by-value. (Part 2 of 2.)

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

Fig. 7.9 Analysis of a typical call-by-reference with a pointer argument.

125

void cubeByReference( int *nPtr )

{

*nPtr = *nPtr * *nPtr * *nPtr;

}

void cubeByReference( int *nPtr )

{

*nPtr = *nPtr * *nPtr * *nPtr;

}

int main()

{

int number = 5;

cubeByReference( &number );

}

void cubeByReference( int *nPtr )

{

*nPtr = *nPtr * *nPtr * *nPtr;

}

int main()

{

int number = 5;

cubeByReference( &number );

}

int main()

{

int number = 5;

cubeByReference( &number );

}

number

5

nPtr

number

5

nPtr

number

125

nPtr

Before main calls cubeByReference :

After cubeByReference receives the call and before *nPtr is cubed:

After *nPtr is cubed and before program control returns to main :

undefined

call establishes this pointer

called function modifies caller’s variable

C Arrays and Pointers• In C, pointers are more challenging

– You will need to know • when to use a pointer• when to dereference the pointer• when to pass an address to a variable rather than the variable

itself• when to use pointer arithmetic to change the pointer • how to use pointers without making your programs

unreadable– Basically, you have to learn how to not “shoot yourself

in the foot” with pointers

Pointer Arithmetic• Recall from the previous lectures that we

declare a pointer with a * use an & to get the "address of" (and convert a variable to a pointer) and use a * to get the value "pointed at"int *p;int q= 5;p= &q;*p= 6;printf ("q is now %d\n",q);printf ("p is now %d\n",*p);

Arrays and Pointers• We declare an array using [ ] in our declaration following the variable name

– int x[5];• You must include the size of the array in the [ ] when declaring unless you

are also initializing the array to its starting values as in:– int x [ ] = {1, 2, 3, 4, 5};– you can also include the size when initializing as long as the size is >= the

number of items being initialized (in which case the remaining array elements are uninitialized)

• Arrays in C are interesting because they are pointed to– the variable that you declare for the array is actually a pointer to the first array

element• You can interact with the array elements either through pointers or by using

[ ]• One of the intriguing features of pointers in C is the ability to manipulate the

pointers through pointer arithmetic – a pointer is an int value, so we can add or subtract– this will be used for stepping through arrays rather than using array indices

Example Codeint x = 1, y = 2, z[10];int *ip; // ip is a pointer to an int, so it can point to x, y, or an element of z

ip = &x; // ip now points at the location where x is storedy = *ip; // set y equal to the value pointed to by ip, or y = x*ip = 0; // now change the value that ip points to to 0, so now x = 0

// but notice that y is unchangedip = &z[0]; // now ip points at the first location in the array z

*ip = *ip + 1; // the value that ip points to (z[0]) is incremented

int x, *y, z, *q;x = 3;y = &x; // y points to xprintf("%d\n", x); // outputs 3printf("%d\n", y); // outputs x’s address, will seem like a random number to usprintf("%d\n", *y); // outputs what y points to, or x (3)printf("%d\n", *y+1); // outputs 4 (print out what y points to + 1)printf("%d\n", *(y+1)); // this outputs the item after x in memory – what is it?z = *(&x); // z equals 3 (what &x points to, which is x)q = &*y; // q points to 3 – note *& and &* cancel out

Using Pointers with Arrays• Recall in an example, we did ip =

&z[0];• This sets our pointer to point at the

first element of the array– In fact, z is a pointer as well and

we can access z[0] either using z[0], *ip, or *z

• What about accessing z[1]?– We can do z[1] as usual, or we

can add 1 to the location pointed to by ip or z, that is *(ip+1) or *(z+1)

– While we can reset ip to be ip = ip+1, we cannot reset z to be z = z+1

– adding 1 to ip will point to z[1], but if z = z + 1 were legal, we would lose access to the first array location since z is our array variable

• Notice that ip=ip+1 (or ip++) moves the pointer 4 bytes instead of 1 to point at the next array location – this is done no matter what size

the element is • if the array were an array of

doubles, the increment would move ip to point 8 bytes away, if the array were chars, then ipwould be 1 byte further

• We could declare our arrays as pointers – notably, we might do this for

our formal parameters as this better describes what we are dealing with)

• For instance– function1(int *array) rather

than function1(int array[])

Pointers and Arrays

int *P, List[N]={1,2,3,4,5}; P = &List[0];

1 2 3 4 5

[1] [2] [3] [4][0]

List

P

Pointers and Arrays

• *P is 1• We can use array notation: P[0] is 1 ; P[1] is 2 ;

etc.

1 2 3 4 5

[1] [2] [3] [4][0]

List

P

Pointer Arithmetic

• P++ increases P by 1 integer address

1 2 3 4 5

[1] [2] [3] [4][0]

List

P

Iterating Through the Array• Here we see two ways to iterate through an array, the usual way,

but also a method using pointer arithmetic

• Let’s consider the code on the right:– pj is a pointer to an int– We start with pj pointing at a, that is, pj points to a[0]– The loop iterates while pj < a + n

• pj is a pointer, so it is an address• a is a pointer to the beginning of an array of n elements so a + n is the size of the array• pj++ increments the pointer to point at the next element in the array• The instruction (*pj)++ says “take what pj points to and increment it”

– NOTE: (*pj)++; increments what pj points to, *(pj++); increments the pointer to point at the next array element

• what do each of these do? *pj++; ++*pj;

int j; for(j = 0; j < n; j++)

a[j]++;

int *pj;for(pj = a; pj < a + n; pj++)

(*pj)++;

Array Example Using a Pointer

int x[4] = {12, 20, 39, 43}, *y;y = &x[0]; // y points to the beginning of the arrayprintf("%d\n", x[0]); // outputs 12 printf("%d\n", *y); // also outputs 12printf("%d\n", *y+1); // outputs 13 (12 + 1)printf("%d\n", (*y)+1); // also outputs 13printf("%d\n", *(y+1)); // outputs x[1] or 20y+=2; // y now points to x[2]printf("%d\n", *y); // prints out 39*y = 38; // changes x[2] to 38printf("%d\n", *y-1); // prints out x[2] - 1 or 37*y++; // sets y to point at the next array elementprintf("%d\n", *y); // outputs x[3] (43)(*y)++; // sets what y points to to be 1 greaterprintf("%d\n", *y); // outputs the new value of x[3] (44)

Pointer Arithmetic 2• A pointer is another type of array and we

can mix between them in certain arrays• If we define an array we can use pointers to

access itint i[7]; /* An array of 7 ints */int *j; /* A pointer to an int*/j= i; /* j points at the start of i*/*j= 3; /* Same as i[0]= 3 */j= &i[0]; /* Same as j= i */j= j+1; /* Move j to point at i[1]*/

Dynamic Memory Allocation• Dynamic memory allocation

– Obtain and release memory during execution• malloc

– Takes number of bytes to allocate• Use sizeof to determine the size of an object

– Returns pointer of type void *• A void * pointer may be assigned to any pointer• If no memory available, returns NULL

– ExamplenewPtr = malloc( sizeof( int) );

• free

– Deallocates memory allocated by malloc– Takes a pointer as an argument– free ( newPtr );

Why bother?

• Pointers allow for dynamic memory management.

• That is, pointers allow us to construct arrays (and other objects) whose size can be determined at run time.

#include <stdio.h> #include <stdlib.h>

int main(int argc, char *argv[]) {

int x = 1, y = 2, z[10]; int *ip,*arrayp; // ip is a pointer to an int, so it can point to x, y, or an element of z z[0]=25; z[1]=5; ip = &x; // ip now points at the location where x is stored y = *ip; // set y equal to the value pointed to by ip, or y = x *ip = 0; // now change the value that ip points to to 0, so now x = 0

// but notice that y is unchanged ip = &z[0]; // now ip points at the first location in the array z *ip = *ip + 1; // the value that ip points to (z[0]) is incremented

printf("z[0]...=%d\t%d\t%d\n",z[0],*ip,*z); printf("z[1]...=%d\t%d\t%d\n",z[1],*(ip+1),*(z+1)); arrayp=(int *)malloc(10*sizeof(int)); arrayp[0]=50; printf("arrayp[0]...=%d\t%d\t\n",arrayp[0],*arrayp); *(arrayp+1)=250; arrayp++; arrayp[-1]=1; printf("arrayp[0]...=%d\t%d\t%d\n",arrayp[-1],*(arrayp-1),*arrayp); printf("arrayp[0]...=%d\t%d\t%d\n",arrayp[0],*arrayp); printf("z[1]...=%d\t%d\t%d\n",z[1],*(ip+1),*(z+1));

/* int x[4] = {12, 20, 39, 43}, *y; y = &x[0]; // y points to the beginning of the array printf("%d\n", x[0]); // outputs 12printf("%d\n", *y); // also outputs 12 printf("%d\n", *y+1); // outputs 13 (12 + 1) printf("%d\n", (*y)+1); // also outputs 13 printf("%d\n", *(y+1)); // outputs x[1] or 20 y+=2; // y now points to x[2] printf("%d\n", *y); // prints out 39 *y = 38; // changes x[2] to 38 printf("%d\n", *y-1); // prints out x[2] - 1 or 37 *y++; // sets y to point at the next array element printf("%d\n", *y); // outputs x[3] (43) (*y)++; // sets what y points to to be 1 greater printf("%d\n", *y); // outputs the new value of x[3] (44) */

/* int *p, *q;

p = (int *)malloc(sizeof(int)); q = p; *p = 10; printf("%d\n", *q); *q = 20; printf("%d\n", *q);

*/

/* int *p, *q; p = (int *)malloc(sizeof(int)); q = (int *)malloc(sizeof(int)); *p = 10; *q = 20; *p = *q;

printf("%d\n", *p);*/

system("PAUSE"); return 0;

}

OutlineOutline

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

fig07_20.c (Part 1 of 2)

1 /* Fig. 7.20: fig07_20.cpp 2 Using subscripting and pointer notations with arrays */ 3 4 #include <stdio.h> 5 6 int main()

7 { 8 int b[] = { 10, 20, 30, 40 }; /* initialize array b */ 9 int *bPtr = b; /* set bPtr to point to array b */ 10 int i; /* counter */ 11 int offset; /* counter */ 12 13 /* output array b using array subscript notation */ 14 printf( "Array b printed with:\nArray subscript notation\n" ); 15 16 /* loop through array b */ 17 for ( i = 0; i < 4; i++ ) { 18 printf( "b[ %d ] = %d\n", i, b[ i ] ); 19 } /* end for */ 20 21 /* output array b using array name and pointer/offset notation */ 22 printf( "\nPointer/offset notation where\n" 23 "the pointer is the array name\n" ); 24

OutlineOutline

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

fig07_20.c (Part 2 of 2)

25 /* loop through array b */ 26 for ( offset = 0; offset < 4; offset++ ) { 27 printf( "*( b + %d ) = %d\n", offset, *( b + offset ) ); 28 } /* end for */ 29 30 /* output array b using bPtr and array subscript notation */ 31 printf( "\nPointer subscript notation\n" ); 32 33 /* loop through array b */ 34 for ( i = 0; i < 4; i++ ) { 35 printf( "bPtr[ %d ] = %d\n", i, bPtr[ i ] ); 36 } /* end for */ 37 38 /* output array b using bPtr and pointer/offset notation */ 39 printf( "\nPointer/offset notation\n" ); 40 41 /* loop through array b */ 42 for ( offset = 0; offset < 4; offset++ ) { 43 printf( "*( bPtr + %d ) = %d\n", offset, *( bPtr + offset ) ); 44 } /* end for */ 45 46 return 0; /* indicates successful termination */ 47 48 } /* end main */

OutlineOutline

© Copyright 1992–2004 by Deitel & Associates, Inc. and Pearson Education Inc. All Rights Reserved.

Program Output

Array b printed with:Array subscript notationb[ 0 ] = 10b[ 1 ] = 20b[ 2 ] = 30b[ 3 ] = 40

Pointer/offset notation wherethe pointer is the array name*( b + 0 ) = 10*( b + 1 ) = 20*( b + 2 ) = 30*( b + 3 ) = 40

Pointer subscript notationbPtr[ 0 ] = 10bPtr[ 1 ] = 20bPtr[ 2 ] = 30bPtr[ 3 ] = 40

Pointer/offset notation*( bPtr + 0 ) = 10*( bPtr + 1 ) = 20*( bPtr + 2 ) = 30*( bPtr + 3 ) = 40

Data Structures (struct)• Arrays require that all elements must be of the

same data type. Many times it is necessary touse and group information of different datatypes. An example could be a simple addressbook which usually includes a name for eachentry , an email address, and phone number.

• C supports data structures that can storecombinations of character, integer floatingpoint and enumerated type data. They arecalled a structs.

Structures (struct)• A struct is a derived data type composed of

members that are each fundamental or derived data types.

• A single struct would store the data for one object. An array of structs would store the data for several objects.

• A struct can be defined in several ways as illustrated in the following examples:

Declaring Structures (struct)Reserves Space

struct example{

int label;char letter;char fname[20];

} mystruct ;

Does Not Reserve Space

struct example{

int label;char letter;char fname[20];

} ;

/* The name “example" is called a structure tag */

The struct statement• Here is an example struct statement.

#include <stdio.h>struct line {

int x1, y1; /* coordinates of a point lying on the line*/int x2, y2; /* coordinates of a point lying on the line */};

main(){struct line line1;..}

This defines the variable line1 to bea variable of type line

Typedef & Struct• Generally, typedef is used in combination with struct to

declare a synonym for a structure:

typedef struct example /* Define a structure */{

int label ;char letter; char fname[20] ;

} MyStructure ; /* The "alias" is MyStructure */

MyStructure MyStruct ; /* Create a struct variable */

Typedef• Typedef allows us to associate a name with

a structure (or other data type).• Put typedef at the start of your program.

typedef struct line {int x1, y1;int x2, y2;} LINE;

int main(){LINE line1;

}

line1 is now a structure of line typeThis is what was happening with allthat FILE * stuff

Accessing Struct Members• Individual members of a struct variable may be accessed using

the structure member operator (the dot, “.”):MyStruct.letter ;

• Or , if a pointer to the struct has been declared and initializedMyStructure *myptr = &MyStruct ;

by using the structure pointer operator (the “->“):myptr -> letter ;

which could also be written as:(*myptr).letter ;

LINE line1;line1.x1= 3;line1.y1= 5;line1.x2= 7;if (line1.y2 == 3) {

printf ("Y co-ordinate of end is 3\n");}

What else can we do with structs?

• We can pass and return structs from functions. (But make sure the function prototype is _AFTER_ the struct is declared)typedef struct line {

int x1,y1;int x2,y2;

} LINE;

LINE find_perp_line (LINE);/* Function takes a line and returns aperpendicular line */

Example: Complex numberstypedef struct complex {

float imag;float real;

} CPLX;

CPLX mult_complex (CPLX, CPLX);

int main(){/* Main goes here */}

CPLX mult_complex (CPLX no1, CPLX no2){

CPLX answer;answer.real= no1.real*no2.real - no1.imag*no2.imag;answer.imag= no1.imag*no2.real + no1.real*no2.imag;return answer;

}

What's the point of all this with structs?

• It makes your programming easier and it makes it easier for other programmers.

• FILE * was a typedef struct but you could use it without knowing what was inside it.

• You can extend the language - for example, if you use complex numbers a lot then you can write functions which deal with complex numbers.

Structs which contain themselves• Sometimes programmers want structs in C

to contain themselves.• For example, we might design an electronic

dictionary which has a struct for each word and we might want to refer to synonyms which are also word structures.

word1:runsynonym1synonym2

word2: sprintsynonym1synonym2

word3: jogsynonym1synonym2

How structs can contain themselves

• a struct cannot literally contain itself.

• But it can contain a pointer to the same type of struct

struct silly_struct { /* This doesn't work */struct silly_struct s1;

};

struct good_struct { /* This does work */struct *good_struct s2;

};

Linked Lists

• A linked list is a series of connected nodes• Each node contains at least

– A piece of data (any type)– Pointer to the next node in the list

• Head: pointer to the first node• The last node points to NULL

A ∅

Head

B C

A

data pointer

node

A Simple Linked List Structure Using Pointers• A node in a linked list is usually a struct

typedef struct node {

int item;struct node *next;

}List;• A node is dynamically allocated

List *p;p = (List *)malloc(sizeof(List));

A Simple Linked List Structure Using Pointers

• The head pointer points to the first node in a linked list

• If head is NULL, the linked list is empty

int is_empty( LIST *head ) {

if (head == NULL){

printf(“Empty List”);return 1;

}return 0;

}

A Simple Linked List Structure Using Pointersint main(){

List *head,*newnode;head = NULL;newnode = (List *)malloc(sizeof(List));newnode->item = 5;newnode->Next = NULL;head = newnode;return 0;

} newnode

How would it be if we wanted to add n nodes to the list?

int main(int argc, char *argv[]){

int i; List *header;List *curr ;header = NULL;curr = (List *)malloc(sizeof(List));header = curr;for(i=1;i<=10;i++){

curr->item=i*10;curr->next=(List *) malloc(sizeof(List));curr=curr->next;

} printf("Linked List\n");curr =header;for(i=0;i<10;i++){

printf("%dth element...=%d\n",i,curr->item);curr=curr->next;

}system("PAUSE");return 0;

}

The effect of the assignment cur = cur->next

A Simple Linked List Structure Using PointersInsertion Algorithm

To insert a node between two nodesnewPtr->next = cur;prev->next = newPtr;

To insert a node at the beginning of a linked listnewPtr->next = head;head = newPtr;

Inserting at the end of a linked list is not aspecial case if cur is NULL

newPtr->next = cur;prev->next = newPtr;

List* Insert(List *head, int index,int newitem){

if (index < 0){printf("Entered Index is smaller than 0\n");printf("No insertion will take place\n");return head;}

int currIndex = 1;List *currNode = head;while (currNode && index > currIndex){

currNode = currNode->next;currIndex++;}

if (index > 0 && currNode == NULL) {printf("Entered Index is bigger than the list size, no insertion\n");return head;}

List *newNode = (List *)malloc(sizeof(List));newNode->item = newitem;if (index == 0){

newNode->next = head;head = newNode;}

else {newNode->next = currNode->next;currNode->next = newNode;}

return head;}

A Simple Linked List Structure Using PointersInsertion Algorithm

Try to locate index’th node. If it doesn’t exist, return the List without insertion.

newNode

currNode

head

newNode

A Simple Linked List Structure Using PointersDelete Algorithm

Deleting a node from a linked list

Deleting the first node

List* Delete(List *head, int index) {

int i;List* prevNode = NULL;List* currNode = head;for (i=1;i<=index;i++) {

prevNode = currNode;currNode = currNode->next;

}if (currNode) {

if (prevNode) {prevNode->next = currNode->next;free(currNode);

}else {

head = currNode->next;free(currNode);

}return head;

}return head;

}

A Simple Linked List Structure Using PointersDelete Algorithm

currNodeprevNode

currNodehead

Data Structures -- Stacks

• A stack is a list in which insertion and deletion take place at the same end– This end is called top– The other end is called bottom

Data Structures -- Stacks

• Attributes of Stack– maxTop: the max size of stack– top: the index of the top element of stack– values: points to structure storing elements of stack

• Operations of Stack– empty: return true if stack is empty, return false otherwise– full: return true if stack is full, return false otherwise– top: return the element at the top of stack– push: add an element to the top of stack– pop: delete the element at the top of stack– displayStack: print all the data in the stack

• Refer to lecture notes for details

Data Structure -- Stacks• Stacks are known as LIFO (Last In, First Out) lists:

The last element inserted will be the first to be retrieved, using Push and Pop

• Push– Add an element to the top of the stack

• Pop– Remove the element at the top of the stack

top

empty stack

Atop

push an element

top

push another

A

Btop

pop

A

Stacks: Implementation• Any list implementation could be used to implement a stack

struct stackNode { int data; /* define data as an int */struct stackNode *nextPtr; /* stackNode pointer */

}; typedef struct stackNode StackNode; /* synonym for struct stackNode */typedef StackNode *StackNodePtr; /* synonym for StackNode* *//* Insert a node at the stack top */StackNodePtr push( StackNodePtr topPtr, int info ){

StackNodePtr newPtr; /* pointer to new node */newPtr = (StackNodePtr)malloc( sizeof( StackNode ) );/* insert the node at stack top */if ( newPtr != NULL ) {

newPtr->data = info;newPtr->nextPtr = topPtr;topPtr = newPtr;

}else { /* no space available */

printf( "%d not inserted. No memory available.\n", info );}

return topPtr;} /* Remove a node from the stack top */StackNodePtr pop( StackNodePtr topPtr, int *popValue ){

StackNodePtr tempPtr; /* temporary node pointer */tempPtr = topPtr;*popValue = topPtr->data;topPtr = topPtr->nextPtr;free( tempPtr );return topPtr;

}/* Return 1 if the stack is empty, 0 otherwise */int isEmpty( StackNodePtr topPtr ){

return topPtr == NULL;}

#include <stdio.h> #include <stdlib.h> typedef struct node { int item; struct node *next; }List; List* Insert(List *,int,int); List* Delete(List *, int); int NumberofNodes(List *); void PrintList(List *); int main(int argc, char *argv[]) { int i,n=10,NewItem,Pos; List *header; List *curr ; header = NULL; curr = (List *)malloc(sizeof(List)); header = curr; for(i=1;i<=n;i++) { curr->item=i*10; curr->next=(List *) malloc(sizeof(List)); curr=curr->next; } curr->next = NULL; printf("Linked List\n"); curr =header; PrintList(curr); printf("which Position would you like to add a new entry? (between 0 and %d)...=",NumberofNodes(curr)-1); scanf("%d",&Pos); printf("What is the value of the new entry?...="); scanf("%d",&NewItem); curr = Insert(header,Pos,NewItem); printf("After Insertion\n"); PrintList(curr); printf("which Position would you like to remove entry? (between 0 and %d)...=", NumberofNodes(curr)-1); scanf("%d",&Pos); curr = Delete(curr,Pos); printf("After Deleting\n"); PrintList(curr); system("PAUSE"); return 0; } List* Insert(List *head, int index,int newitem) {

if (index < 0) { printf("Entered Index is smaller than 0\n"); printf("No insertion will take place\n"); return head; } int currIndex = 1; List *currNode = head; while (currNode != NULL && index > currIndex) { currNode = currNode->next; currIndex++; } if (index > 0 && currNode == NULL) { printf("Entered Index is bigger than the list size\n"); printf("No insertion will take place\n"); return head; } List *newNode = (List *)malloc(sizeof(List)); newNode->item = newitem; if (index == 0) { newNode->next = head; head = newNode; } else { newNode->next = currNode->next; currNode->next = newNode; } return head; } List* Delete(List *head, int index) { int i; List* prevNode = NULL; List* currNode = head; for (i=1;i<=index;i++) { prevNode = currNode; currNode = currNode->next; } if (currNode != NULL) { if (prevNode != NULL) { prevNode->next = currNode->next; free(currNode); } else {

head = currNode->next; free(currNode); } return head; } return head; } int NumberofNodes(List *Head) { int Counter = 0; List *tmp; tmp = Head; while(tmp->next != NULL) { tmp = tmp->next; Counter++; } return Counter; } void PrintList(List *Head) { int i,ListSize; ListSize = NumberofNodes(Head); printf("ListSize...=%d\n",ListSize); for(i=0;i<ListSize;i++) { printf("%dth element of the list...=%d\n",i,Head->item); Head = Head->next; } }

#include <stdio.h> #include <stdlib.h> #include <conio.h> /* *******STACKS**** */ /* self-referential structure */ struct stackNode { int data; /* define data as an int */ struct stackNode *nextPtr; /* stackNode pointer */ }; /* end structure stackNode */ typedef struct stackNode StackNode; /* synonym for struct stackNode */ typedef StackNode *StackNodePtr; /* synonym for StackNode* */ /* prototypes */ StackNodePtr push( StackNodePtr, int ); StackNodePtr pop( StackNodePtr, int * ); int isEmpty( StackNodePtr ); void printStack( StackNodePtr ); /* function main begins program execution */ int main() { StackNodePtr stackPtr = NULL; /* points to stack top */ int value,popValue; do { printf( "Enter an integer:\n" ); scanf( "%d", &value ); stackPtr = push( stackPtr, value ); printStack( stackPtr ); printf( "Would you like to enter more elements to the stack?(y/n):\n"); }while (getch()=='y'); /* pop value off stack */ /* if stack is not empty */ if ( !isEmpty( stackPtr ) ) { stackPtr = pop(stackPtr, &popValue); printf( "The popped value is %d.\n", popValue ); } printStack( stackPtr ); system("PAUSE"); return 0; } /* Insert a node at the stack top */ StackNodePtr push( StackNodePtr topPtr, int info ) { StackNodePtr newPtr; /* pointer to new node */

newPtr = (StackNodePtr)malloc( sizeof( StackNode ) ); /* insert the node at stack top */ if ( newPtr != NULL ) { newPtr->data = info; newPtr->nextPtr = topPtr; topPtr = newPtr; } /* end if */ else { /* no space available */ printf( "%d not inserted. No memory available.\n", info ); } /* end else */ return topPtr; } /* Remove a node from the stack top */ StackNodePtr pop( StackNodePtr topPtr, int *popValue ) { StackNodePtr tempPtr; /* temporary node pointer */ tempPtr = topPtr; *popValue = topPtr->data; topPtr = topPtr->nextPtr; free( tempPtr ); return topPtr; } /* end function pop */ /* Print the stack */ void printStack( StackNodePtr currentPtr ) { /* if stack is empty */ if ( currentPtr == NULL ) { printf( "The stack is empty.\n\n" ); } /* end if */ else { printf( "The stack is:\n" ); /* while not the end of the stack */ while ( currentPtr != NULL ) { printf( "%d --> ", currentPtr->data ); currentPtr = currentPtr->nextPtr; } /* end while */ printf( "NULL\n\n" ); } /* end else */ } /* end function printList */ /* Return 1 if the stack is empty, 0 otherwise */ int isEmpty( StackNodePtr topPtr ) { return topPtr == NULL; } /* end function isEmpty */

File handling in C

• In C we use FILE * to represent a pointer to a file. • fopen is used to open a file. It returns the special

value NULL to indicate that it couldn't open the file.

FILE *fptr;char filename[]= "file2.dat";fptr= fopen (filename,"w");if (fptr == NULL) {

fprintf (stderr, “ERROR”);

/* DO SOMETHING */}

Modes for opening files

• The second argument of fopen is the mode in which we open the file. There are three

• "r" opens a file for reading• "w" opens a file for writing - and writes over all

previous contents (deletes the file so be careful!)• "a" opens a file for appending - writing on the

end of the file

The exit() function• Sometimes error checking means we want

an "emergency exit" from a program. We want it to stop dead.

• In main we can use "return" to stop.• In functions we can use exit to do this.• Exit is part of the stdlib.h library

exit(-1);in a function is exactly the same asreturn -1;

in the main routine

Writing to a file using fprintf

• fprintf works just like printf and sprintf except that its first argument is a file pointer.

• We could also read numbers from a file using fscanf – but there is a better way.

FILE *fptr;fptr= fopen ("file.dat","w");/* Check it's open */fprintf (fptr,"Hello World!\n");

Reading from a file using fgets• fgets is a better way to read from a file• We can read into a string using fgets

FILE *fptr;char line [1000];/* Open file and check it is open */while (fgets(line,1000,fptr) != NULL) {

printf ("Read line %s\n",line);}

fgets takes 3 arguments, a string, a maximumnumber of characters to read and a file pointer.It returns NULL if there is an error (such as EOF)

Closing a file

• We can close a file simply using fclose and the file pointer. Here's a complete "hello files".FILE *fptr;char filename[]= "myfile.dat";fptr= fopen (filename,"w");if (fptr == NULL) {

printf ("Cannot open file to write!\n");exit(-1);

}fprintf (fptr,"Hello World of filing!\n");fclose (fptr);

Great Muck-Ups in C #72 of 100

• We use the file pointer to close the file - not the name of the file

FILE *fptr;fptr= fopen ("myfile.dat","r");/* Read from file */fclose ("myfile.dat");/* Ooops - that's wrong */

Three special streams• Three special file streams are defined in the stdio.h header

• stdin reads input from the keyboard• stdout send output to the screen• stderr prints errors to an error device

(usually also the screen)• What might this do:

fprintf (stdout,"Hello World!\n");

Reading loops• It is quite common to want to read every

line in a program. The best way to do this is a while loop using fgets./* define MAXLEN at start using enum */FILE *fptr;char tline[MAXLEN]; /* A line of text */fptr= fopen ("sillyfile.txt","r");/* check it's open */while (fgets (tline, MAXLEN, fptr) != NULL) {

printf ("%s",tline); // Print it}fclose (fptr);

Using fgets to read from the keyboard

• fgets and stdin can be combined to get a safe way to get a line of input from the user

#include <stdio.h>int main(){

const int MAXLEN=1000;char readline[MAXLEN];fgets (readline,MAXLEN,stdin);printf ("You typed %s",readline);return 0;

}

Getting numbers from strings• Once we've got a string with a number in it

(either from a file or from the user typing) we can use atoi or atof to convert it to a number

• The functions are part of stdlib.hchar numberstring[]= "3.14";int i;double pi;pi= atof (numberstring);i= atoi ("12");

Both of these functions return 0 if they have a problem

Great Muck-Ups in C #11 of 100• fgets includes the '\n' on the end• This can be a problem - for example if in

the last example we got input from the user and tried to use it to write a file:

FILE *fptr;char readfname[1000];fgets (readfname,1000,stdin);fptr= fopen (readfname,"w");/* oopsie - file name also has \n */

Even experienced programmers can makethis error

1

File Handling in C We frequently use files for storing information which can be processed by our programs. In order to store information permanently and retrieve it we need to use files. Files are not only used for data. Our programs are also stored infiles. The editor which you use to enter your program and save it, simply manipulates files for you. The Unix commands cat, cp, cmp are all programs which process your files. In order to use files we have to learn about File I/O i.e. how to write information to a file and how to read information from a file. We will see that file I/O is almost identical to the terminal I/O that we have being using so far. The primary difference between manipulating files and doing terminal I/O is that we must specify in our programs which files we wish to use. As you know, you can have many files on your disk. If you wish to use a file in your programs, then you must specify which file or files you wish to use. Specifying the file you wish to use is referred to as opening the file.

2

When you open a file you must also specify what you wish to do with it i.e. Read from the file, Write to the file, or both. Because you may use a number of different files in your program, you must specify when reading or writing which file you wish to use. This is accomplished by using a variable called a file pointer. Every file you open has its own file pointer variable. When you wish to write to a file you specify the file by using its file pointer variable. You declare these file pointer variables as follows: FILE *fopen(), *fp1, *fp2, *fp3; The variables fp1, fp2, fp3 are file pointers. You may use any name you wish. The file <stdio.h> contains declarations for the Standard I/O library and should always be included at the very beginning of C programs using files. Constants such as FILE, EOF and NULL are defined in <stdio.h>. You should note that a file pointer is simply a variable like an integer or character.

3

It does not point to a file or the data in a file. It is simply used to indicate which file your I/O operation refers to. A file number is used in the Basic language and a unit number is used in Fortran for the same purpose. The function fopen is one of the Standard Library functions and returns a file pointer which you use to refer to the file you have opened e.g. fp = fopen( “prog.c”, “r”) ; The above statement opens a file called prog.c for reading and associates the file pointer fp with the file. When we wish to access this file for I/O, we use the file pointer variable fp to refer to it. You can have up to about 20 files open in your program - you need one file pointer for each file you intend to use.

4

File I/O The Standard I/O Library provides similar routines for file I/O to those used for standard I/O. The routine getc(fp) is similar to getchar() and putc(c,fp) is similar to putchar(c). Thus the statement c = getc(fp); reads the next character from the file referenced by fp and the statement putc(c,fp); writes the character c into file referenced by fp.

5

/* file.c: Display contents of a file on screen */ #include <stdio.h> void main() { FILE *fopen(), *fp; int c ; fp = fopen( “prog.c”, “r” ); c = getc( fp ) ; while ( c != EOF ) { putchar( c ); c = getc ( fp ); } fclose( fp ); } In this program, we open the file prog.c for reading. We then read a character from the file. This file must exist for this program to work. If the file is empty, we are at the end, so getc returns EOF a special value to indicate that the end of file has been reached. (Normally -1 is used for EOF) The while loop simply keeps reading characters from the file and displaying them, until the end of the file is reached. The function fclose is used to close the file i.e. indicate that we are finished processing this file.

6

We could reuse the file pointer fp by opening another file. This program is in effect a special purpose cat command. It displays file contents on the screen, but only for a file called prog.c. By allowing the user enter a file name, which would be stored in a string, we can modify the above to make it an interactive cat command: /* cat2.c: Prompt user for filename and display file on screen */ #include <stdio.h> void main() { FILE *fopen(), *fp; int c ; char filename[40] ; printf(“Enter file to be displayed: “); gets( filename ) ; fp = fopen( filename, “r”); c = getc( fp ) ; while ( c != EOF ) { putchar(c); c = getc ( fp ); } fclose( fp ); }

7

In this program, we pass the name of the file to be opened which is stored in the array called filename, to the fopen function. In general, anywhere a string constant such as “prog,c” can be used so can a character array such as filename. (Note the reverse is not true). The above programs suffer a major limitation. They do not check whether the files to be used exist or not. If you attempt to read from a non-existent file, your program will crash!! The fopen function was designed to cope with this eventuality. It checks if the file can be opened appropriately. If the file cannot be opened, it returns a NULL pointer. Thus by checking the file pointer returned by fopen, you can determine if the file was opened correctly and take appropriate action e.g. fp = fopen (filename, “r”) ; if ( fp == NULL) { printf(“Cannot open %s for reading \n”, filename ); exit(1) ; /*Terminate program: Commit suicide !!*/ } The above code fragment show how a program might check if a file could be opened appropriately. The function exit() is a special function which terminates your program immediately.

8

exit(0) mean that you wish to indicate that your program terminated successfully whereas a nonzero value means that your program is terminating due to an error condition. Alternatively, you could prompt the user to enter the filename again, and try to open it again: fp = fopen (fname, “r”) ; while ( fp == NULL) { printf(“Cannot open %s for reading \n”, fname ); printf(“\n\nEnter filename :” ); gets( fname ); fp = fopen (fname, “r”) ; } In this code fragment, we keep reading filenames from the user until a valid existing filename is entered. Exercise: Modify the above code fragment to allow the user 3 chances to enter a valid filename. If a valid file name is not entered after 3 chances, terminate the program. RULE: Always check when opening files, that fopen succeeds in opening the files appropriately. Obeying this simple rule will save you much heartache. Example 1: Write a program to count the number of lines and characters in a file. Note: Each line of input from a file or keyboard will be terminated by the newline character ‘\n’. Thus by counting newlines we

9

know how many lines there are in our input./*count.c : Count characters in a file*/ #include <stdio.h> void main() /* Prompt user for file and count number of characters and lines in it*/ { FILE *fopen(), *fp; int c , nc, nlines; char filename[40] ; nlines = 0 ; nc = 0; printf(“Enter file name: “); gets( filename ); fp = fopen( filename, “r” ); if ( fp == NULL ) { printf(“Cannot open %s for reading \n”, filename ); exit(1); /* terminate program */ } c = getc( fp ) ; while ( c != EOF ) { if ( c == ‘\n’ ) nlines++ ; nc++ ; c = getc ( fp ); } fclose( fp ); if ( nc != 0 ) { printf(“There are %d characters in %s \n”, nc, filename ); printf(“There are %d lines \n”, nlines ); } else printf(“File: %s is empty \n”, filename ); }

10

Example 2: Write a program to display file contents 20 lines at a time. The program pauses after displaying 20 lines until the user presses either Q to quit or Return to display the next 20 lines. (The Unix operating system has a command called more to do this ) As in previous programs, we read the filename from user and open it appropriately. We then process the file: read character from file while not end of file and not finished do begin display character if character is newline then linecount = linecount + 1; if linecount == 20 then begin linecount = 1 ; Prompt user and get reply; end read next character from file end

11

/* display.c: File display program */ /* Prompt user for file and display it 20 lines at a time*/ #include <stdio.h> void main() { FILE *fopen(), *fp; int c , linecount; char filename[40], reply[40]; printf(“Enter file name: “); gets( filename ); fp = fopen( filename, “r” ); /* open for reading */ if ( fp == NULL ) /* check does file exist etc */ { printf(“Cannot open %s for reading \n”, filename ); exit(); /* terminate program */ } linecount = 1 ; reply[0] = ‘\0’ ; c = getc( fp ) ; /* Read 1st character if any */ while ( c != EOF && reply[0] != ‘Q’ && reply[0] != ‘q’) { putchar( c ) ; /* Display character */ if ( c == ‘\n’ ) linecount = linecount+ 1 ; if ( linecount == 20 ) { linecount = 1 ; printf(“[Press Return to continue, Q to quit]”); gets( reply ) ; } c = getc ( fp ); } fclose( fp ); } The string reply will contain the users response. The first character of this will be reply[0]. We check if this is ‘q’ or ‘Q’.

12

The brackets [] in printf are used to distinguish the programs message from the file contents. Example 3: Write a program to compare two files specified by the user, displaying a message indicating whether the files are identical or different. This is the basis of a compare command provided by most operating systems. Here our file processing loop is as follows: read character ca from file A; read character cb from file B; while ca == cb and not EOF file A and not EOF file B begin read character ca from file A; read character cb from file B; end if ca == cb then printout(“Files identical”); else printout(“Files differ”); This program illustrates the use of I/O with two files. In general you can manipulate up to 20 files, but for most purposes not more than 4 files would be used. All of these examples illustrate the usefulness of processing files character by character. As you can see a number of Operating System programs such as compare, type, more, copy can be easily written using character I/O. These programs are normally called system programs as they come with the operating system. The important point to note is that these programs are in no way special. They are no different in nature than any of the programs we have constructed so far.

13

/* compare.c : compare two files */ #include <stdio.h> void main() { FILE *fp1, *fp2, *fopen(); int ca, cb; char fname1[40], fname2[40] ; printf(“Enter first filename:”) ; gets(fname1); printf(“Enter second filename:”); gets(fname2); fp1 = fopen( fname1, “r” ); /* open for reading */ fp2 = fopen( fname2, “r” ) ; /* open for writing */ if ( fp1 == NULL ) /* check does file exist etc */ { printf(“Cannot open %s for reading \n”, fname1 ); exit(1); /* terminate program */ } else if ( fp2 == NULL ) { printf(“Cannot open %s for reading \n”, fname2 ); exit(1); /* terminate program */ } else /* both files opened successfully */ { ca = getc( fp1 ) ; cb = getc( fp2 ) ; while ( ca != EOF && cb != EOF && ca == cb ) { ca = getc( fp1 ) ; cb = getc( fp2 ) ; } if ( ca == cb ) printf(“Files are identical \n”); else if ( ca != cb ) printf(“Files differ \n” ); fclose ( fp1 ); fclose ( fp2 ); } }

14

Writing to Files The previous programs have opened files for reading and read characters from them. To write to a file, the file must be opened for writing e.g. fp = fopen( fname, “w” ); If the file does not exist already, it will be created. If the file does exist, it will be overwritten! So, be careful when opening files for writing, in case you destroy a file unintentionally. Opening files for writing can also fail. If you try to create a file in another users directory where you do not have access you will not be allowed and fopen will fail. Character Output to Files The function putc( c, fp ) writes a character to the file associated with the file pointer fp. Example: Write a file copy program which copies the file “prog.c” to “prog.old” Outline solution: Open files appropriately Check open succeeded Read characters from prog.c and Write characters to prog.old until all characters copied Close files The step: “Read characters .... and write ..” may be refined to: read character from prog.c

15

while not end of file do begin write character to prog.old read next character from prog.c end /* filecopy.c : Copy prog.c to prog.old */ #include <stdio.h> void main() { FILE *fp1, *fp2, *fopen(); int c ; fp1 = fopen( “prog.c”, “r” ); /* open for reading */ fp2 = fopen( “prog.old”, “w” ) ; ../* open for writing */ if ( fp1 == NULL ) /* check does file exist etc */ { printf(“Cannot open prog.c for reading \n” ); exit(1); /* terminate program */ } else if ( fp2 == NULL ) { printf(“Cannot open prog.old for writing \n”); exit(1); /* terminate program */ } else /* both files O.K. */ { c = getc(fp1) ; while ( c != EOF) { putc( c, fp2); /* copy to prog.old */ c = getc( fp1 ) ; } fclose ( fp1 ); /* Now close files */ fclose ( fp2 ); printf(“Files successfully copied \n”); } }

16

The above program only copies the specific file prog.c to the file prog.old. We can make it a general purpose program by prompting the user for the files to be copied and opening them appropriately. /* copy.c : Copy any user file*/ #include <stdio.h> void main() { FILE *fp1, *fp2, *fopen(); int c ; char fname1[40], fname2[40] ; printf("Enter source file:") ; gets(fname1); printf("Enter destination file:"); gets(fname2); fp1 = fopen( fname1, "r" ); /* open for reading */ fp2 = fopen( fname2, "w" ) ; ../* open for writing */ if ( fp1 == NULL ) /* check does file exist etc */ { printf("Cannot open %s for reading \n", fname1 ); exit(1); /* terminate program */ } else if ( fp2 == NULL ) { printf("Cannot open %s for writing \n", fname2 ); exit(1); /* terminate program */ } else /* both files O.K. */ { c = getc(fp1) ; /* read from source */ while ( c != EOF) { putc( c, fp2); /* copy to destination */ c = getc( fp1 ) ; } fclose ( fp1 ); /* Now close files */ fclose ( fp2 ); printf("Files successfully copied \n"); } }

17

Command Line Parameters: Arguments to main() Accessing the command line arguments is a very useful facility. It enables you to provide commands with arguments that the command can use e.g. the command % cat prog.c takes the argument "prog.c" and opens a file with that name, which it then displays. The command line argumenst include the command name itself so that in the above example, "cat" and "prog.c" are the command line arguments. The first argument i.e. "cat" is argument number zero, the next argument, "prog.c", is argument number one and so on. To access these arguments from within a C program, you pass parameters to the function main(). The use of arguments to main is a key feature of many C programs. The declaration of main looks like this: int main (int argc, char *argv[]) This declaration states that

1. main returns an integer value (used to determine if the program terminates successfully)

2. argc is the number of command line arguments including the command itself i.e argc must be at least 1

3. argv is an array of the command line arguments

18

The declaration of argv means that it is an array of pointers to strings (the command line arguments). By the normal rules about arguments whose type is array, what actually gets passed to main is the address of the first element of the array. As a result, an equivalent (and widely used) declaration is: int main (int argc, char **argv) When the program starts, the following conditions hold true: oo argc is greater than 0. oo argv[argc] is a null pointer. oo argv[0], argv[1], ..., argv[argc-1] are pointers to strings with implementation defined meanings. oo argv[0] is a string which contains the program’s name, or is an empty string if the name isn’t available. Remaining members of argv are the program’s arguments.

19

Example: print_args echoes its arguments to the standard output – is a form of the Unix echo command. /* print_args.c: Echo command line arguments */ #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int i = 0 ; int num_args ; num_args = argc ; while( num_args > 0) { printf(“%s\n“, argv[i]); i++ ; num_args--; } } If the name of this program is print_args, an example of its execution is as follows: % print_args hello goodbye solong print_args hello goodbye solong % Exercise: Rewrite print_args so that it operates like the Unix echo command. Hint: You only need to change the printf statement.

20

The following is a version of the Unix cat command: /* cat1.c: Display files specified as command line parameters */ #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int i = 1 ; int c ; int num_args = 0 ; FILE *fp; if ( argc == 1 ) { fprintf(stderr, "No input files\nUsage: % cat file…\n"); exit(1); } if ( argc > 1 ) printf("%d files to be displayed\n", argc-1); num_args = argc - 1; while( num_args > 0) { printf("[Displaying file %s]\n", argv[i]); num_args--; fp = fopen( argv[i], "r" ) ; if ( fp == NULL ) { fprintf(stderr,"Cannot display %s \n", argv[i]); continue; /* Goto next file in list */ } c = getc(fp) ; while ( c != EOF ) { putchar( c ); c = getc( fp ); } fclose( fp ); printf("\n[End of %s]\n--------------\n\n", argv[i]); i++; } } Note: The continue statement causes the current iteration of the loop to stop and control to return to the loop test.

21

The following is a version of the Unix wc command called count which operates as follows % count prog.c prog.c: 300 characters 20 lines % count –l prog.c prog.c: 20 lines % count –w prog.c prog.c: 300 characters /*count.c : Count lines and characters in a file */ #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int c , nc, nlines; char filename[120]; FILE *fp, *fopen(); if ( argc == 1 ) { fprintf(stderr, "No input files\n"); fprintf(stderr, "Usage: \% count [-l] [w] file\n"); exit(1); } nlines = 0 ; nc = 0; if ((strcmp("-l", argv[1]) == 0) || (strcmp("-w", argv[1]) == 0) ) strcpy(filename, argv[2]) ; else strcpy(filename, argv[1]); fp = fopen( filename, "r" ); if ( fp == NULL ) { fprintf(stderr,"Cannot open %s\n", filename ); exit(1); }

22

c = getc( fp ) ; while ( c != EOF ) { if ( c == '\n') nlines++ ; nc++ ; c = getc ( fp ); } fclose( fp ); if ( strcmp(argv[1], "-w") == 0 ) printf("%s: %d characters \n", filename, nc ); else if ( strcmp(argv[1], "-l") == 0 ) printf("%s: %d lines \n", filename, nlines ); else printf("%s: %d characters %d lines\n", filename, nc, nlines ); Logical OR is represented by || in the code above. Logical AND is represented by && in C. The function strcpy() is one of many library string handling functions. It takes two strings as arguments and copies the second argument to the first i.e. it operates as a form of string assignment. In C you CANNOT assign strings as:

filename = "prog.c" /* WRONG */ strcpy( filename, "prog.c"); /* CORRECT */

The function strcmp() is another string handling function. It takes two strings as arguments and returns 0 if the two strings are the same. As for assignment, you cannot test string equality with == i.e. if (filename == "prog.c") /* WRONG */ if (strcmp(filename,"prog.c")==0) /* CORRECT */ Note: The above program crashes if you run it as: % count –w or % count –l This is because in these cases we failed to test if there was a 3rd argument containing the filename to be processed. We simply try to access this non-

23

existent argument and so cause a memory violation. This gives rise to a so-called "bus error" in a Unix environment. As an exercise, insert code to correct this failure. Exercise: Write a copy command to operate like the Unix cpcommand that takes it files from the command line: % copy file newfile