Вы находитесь на странице: 1из 41

NOTES

MCA-105 Problem Solving Using C

Lesson 1:
A Program is defined as a set of instructions executed sequentially. Any language used to
develop programs is called Programming Language. These languages have been broadly
divided into 2 categories High level programming Languages & Low level Programming
Languages. High level languages are the languages which are easily understood by
human beings but are slow at execution. Whereas the low level languages are difficult to
learn but faster at execution. To use the plus points of both these languages C was
designed which is called Middle Level Language.

Now to understand some topics of C like (Bitwise operators) something about the binary
arithmetic must be known as computer stores everything in binary states either 0 or 1.
This System is known as BINARY SYSTEM. Similarly OCTAL SYSTEM and
HEXADECIMAL SYSTEM will be discussed to understand how alphanumeric data is
stored in system.

History of C:

The C programming language was devised in the early 1970s as a system implementation
language for the nascent Unix operating system.

C came into being in the years 1969-1973, in parallel with the early development of the
Unix operating system; the most creative period occurred during 1972. Another spate of
changes peaked between 1977 and 1979, when portability of the Unix system was being
demonstrated. In the middle of this second period, the first widely available description
of the language appeared: The C Programming Language, often called the `white book'
or `K&R' [Kernighan 78]. Finally, in the middle 1980s, the language was officially
standardized by the ANSI X3J11 committee, which made further changes. Until the early
1980s, although compilers existed for a variety of machine architectures and operating
systems, the language was almost exclusively associated with Unix; more recently, its use
has spread much more widely, and today it is among the languages most commonly used
throughout the computer industry.

BCPL, B, and C all fit firmly in the traditional procedural family typified by Fortran and
Algol 60. They are particularly oriented towards system programming, are small and
compactly described, and are amenable to translation by simple compilers. They are
`close to the machine' in that the abstractions they introduce are readily grounded in the
concrete data types and operations supplied by conventional computers, and they rely on
library routines for input-output and other interactions with an operating system. With
less success, they also use library procedures to specify interesting control constructs
such as coroutines and procedure closures.
Lesson 2:

Standardization

By 1982 it was clear that C needed formal standardization. The best approximation to a
standard, the first edition of K&R, no longer described the language in actual use; in
particular, it mentioned neither the void or enum types. While it foreshadowed the newer
approach to structures, only after it was published did the language support assigning
them, passing them to and from functions, and associating the names of members firmly
with the structure or union containing them. Although compilers distributed by AT&T
incorporated these changes, and most of the purveyors of compilers not based on pcc
quickly picked up them up, there remained no complete, authoritative description of the
language.

Decimal To Binary Conversion


To convert a decimal number to binary, first subtract the largest possible power of two,
and keep subtracting the next largest possible power form the remainder, marking 1s in
each column where this is possible and 0s where it is not.

Example 1 - (Convert Decimal 44 to Binary)


Example 2 - (Convert Decimal 15 to Binary)

Decimal to Octal Conversion

Octal is base 8.
Base 8 is where the only numbers you can use are zero thru to seven. ie:
the decimal value for 1 is represented in octal as 1 but the octal value of
8 (Decimal) is shown as 10 the value of 9 (Decimal) is 11 in octal.

Decimal Octal Decimal Octal Decimal Octal


1 1 11 13 30 36
2 2 12 14 40 50
3 3 13 15 50 62
4 4 14 16 60 74
5 5 15 17 70 106
6 6 16 20 80 120
7 7 17 21 90 132
8 10 18 22 100 144
9 11 19 23 500 764
10 12 20 24 1000 1750
Lesson 3:

Structure of C Program: We’ll complete the following topics in this section:

1. C's character set


2. C's keywords
3. The general structure of a C program
4. All C statement must end in a ;

C's Character Set


C does not use, nor requires the use of, every character found on a modern
computer keyboard. The only characters required by the C Programming
Language are as follows:

• A-Z
• a -z
• 0-9
• space . , : ; ' $ "
• # % & ! _ {} [] () < > |
• +-/*=

The use of most of this set of characters will be discussed throughout the course.

The form of a C Program


All C programs will consist of at least one function, but it is usual (when your
experience grows) to write a C program that comprises several functions. The
only function that has to be present is the function called main. For more
advanced programs the main function will act as a controlling function calling
other functions in their turn to do the dirty work! The main function is the first
function that is called when your program executes.

C makes use of only 32 keywords which combine with the formal syntax to the form the
C programming language. Note that all keywords are written in lower case - C, like
UNIX, uses upper and lowercase text to mean different things. If you are not sure what to
use then always use lowercase text in writing your C programs. A keyword may not be
used for any other purposes. For example, you cannot have a variable called auto.
Lesson 4:

The layout of C Programs


The general form of a C program is as follows:

pre-processor directives
global declarations
main()
{
local variables to function main ;
statements associated with function main ;
}
f1()
{
local variables to function 1 ;
statements associated with function 1 ;
}
f2()
{
local variables to function f2 ;
statements associated with function 2 ;
}
.
.
.
etc

Note the use of the bracket set () and {}. () are used in conjunction with function names
whereas {} are used as to delimit the C statements that are associated with that function.
Also note the semicolon - yes it is there, but you might have missed it! a semicolon (;) is
used to terminate C statements. C is a free format language and long statements can be
continued, without truncation, onto the next line. The semicolon informs the C compiler
that the end of the statement has been reached. Free format also means that you can add
as many spaces as you like to improve the look of your programs.
LESSON 5:

Type Declaration Instruction:

This instruction is used to declare the type of variables being used in the program. A
variable is a location in RAM (main memory) for temporary storage of intermediate data.
Any variable used in the program must be declared before using it in any statement. The
type declaration statement is written at the beginning of main() function.

Ex: int bas;


float rs, grosssal;
char name, code;

These are several subtle variations of the type declaration instruction.

i) While declaring the type of variable we can also initialize it as shown below:

int i=10, j=25;


float a=1.5, b= 1.99+2.4 * 1.45 ;
ii) The order in which we define the variables is sometimes important sometimes not.
For exa:

int i=10,j=20;

is same as
int j=20, i=10;

However,

float a =1.2, b=a+3.1;


is alright, but

float b=a+3.1, a=1.2;

is not.this is because here we are trying to use a even before defining it.

iii) Similarly
int a,b,c,d;
a=b=c=10;
is O.K. but

int a=b=c=d=10;
will not work.( find out why ???)

LESSON 6:

ARITHMETIC INSTRUCTIONS:

A C arithmetic instruction consists of a variable name on the left hand side of = and
variable names & constants on the right han side of =. The variables & constants on the
right hand side of = are connected by arithmetic operators like + , -, *, /.

Exa:

int ad;
float kot, deta,alpha, beta, gamma;
ad=3200;
koi=0.0056;
.
.
.
.
.
deta= alpha* beta / gamma +3.2 * 2/5;

here

*, /, + are arithmetic operators.


= is assignment operator.
2,5 & 3200 are integer constans.
3.2,0.0056 are real constants.
ad is an integer variable.
Kot, deta, alpha, beta, gamma are real variables.

The variables & constants together are called OPERANDS that are operated upon
by the arithmetic operators and the result is assigned using the assignment operator, to the
variable on the left hand side.
LESSON 7:

HIERARCHY OF OPEATIONS:

While executing an arithmetic statement, which has two or more operators, we


may have some problems as to how exactly does it get executed. For example,
does the expression *x -3 *y correspond to (2x)-(3y) or 2(x-3y)?. To answer these
questions satisfactorily one has to understand the Hierarchy of operations.the
priority or precedence in which the operations in a statement are performed is
called the Hierarchy of operations.

_________________________________________________________________
_
PRIORITY OPERATORS DESCRIPTION
_________________________________________________________________
_

1ST * / %+ multiplication, division, modular


division
2nd +- addition , subtraction

3rd = assignment
________________________________________________________________

Few Tips:

a) Within parentheses the same hierarchy is operatie. Also if there are more than
set of parentheses, the operations within the innermost parentheses would be
performed first, followed by the operations within the second innermost
parentheses & so on.

b) We must always remember to use pair of parentheses. A careless imbalance


of the right and left parentheses is a common error.
LESSON 8:

CONTROL INSTRUCTIONS IN C:

They enable us to specify the order in which the various instructions in a program
are to be executed by the computer. They determine the “flow of control” in a
program.

4 types of control instructions are:


i) Sequence Control Instruction
ii) Decision Control Instruction
iii) Repetition or Loop Control Instruction
iv) Case Control Instruction

Sequence Control Instruction ensures that the instructions are executed in the
same order in which they appear in a program.

Decision Control Instruction allow the computer to take a decision as to which


instruction is to be executed next.

Repetition or Loop Control Instruction helps computer to execute a group of


statements repeatedly.

Case Control Instruction allows user to take the work of multiple ifs using
different cases.

Decision control Structure:

A decision control instruction can be implemented inn C using:


i) The if statement
ii) The if-else statement
iii) The conditional operators

Each of these types with enough examples will be discussed in detail during the
class.
LESSON 9:

The if else Statement


This is used to decide whether to do something at a special point, or to decide
between two courses of action. The following test decides whether a student has
passed an exam with a pass mark of 45

if (result >= 45)


printf("Pass\n");
else
printf("Fail\n");

It is possible to use the if part without the else.

if (temperature < 0)
print("Frozen\n");

Each version consists of a test, (this is the bracketed statement following


the if). If the test is true then the next statement is obeyed. If is is false then the
statement following the else is obeyed if present. After this, the rest of the
program continues as normal.If we wish to have more than one statement
following the if or the else, they should be grouped together between curly
brackets. Such a grouping is called a compound statement or a block.

if (result >= 45)


{ printf("Passed\n");
printf("Congratulations\n")
}
else
{ printf("Failed\n");
printf("Good luck in the resits\n");
}
Sometimes we wish to make a multi-way decision based on several conditions.
The most general way of doing this is by using the else if variant on the if
statement. This works by cascading several comparisons. As soon as one of these
gives a true result, the following statement or block is executed, and no further
comparisons are performed. In the following example we are awarding grades
depending on the exam result.
if (result >= 75)
printf("Passed: Grade A\n");
else if (result >= 60)
printf("Passed: Grade B\n");
else if (result >= 45)
printf("Passed: Grade C\n");
else printf("Failed\n");
In this example, all comparisons test a single variable called result. In other cases,
each test may involve a different variable or some combination of tests. The same pattern
can be used with more or fewer else if's, and the final lone else may be left out. It is up to
the programmer to devise the correct structure for each programming problem.
LESSON 10:

Loops
C gives you a choice of three types of loop, while, do while and for.

• The while loop keeps repeating an action until an associated test returns
false. This is useful where the programmer does not know in advance how
many times the loop will be traversed.
• The do while loops is similar, but the test occurs after the loop body is
executed. This ensures that the loop body is run at least once.
• The for loop is frequently used, usually where the loop will be traversed a
fixed number of times. It is very flexible, and novice programmers should
take care not to abuse the power it offers.

The while Loop


The while loop repeats a statement until the test at the top proves false.
As an example, here is a function to return the length of a string. Remember
that the string is represented as an array of characters terminated by a null
character '\0'.
int string_length(char string[])
{ int i = 0;

while (string[i] != '\0')


i++;
return(i);
}
The string is passed to the function as an argument. The size of the array is
not specified, the function will work for a string of any size.

The while loop is used to look at the characters in the string one at a time until the
null character is found. Then the loop is exited and the index of the null is
returned. While the character isn't null, the index is incremented and the test is
repeated.
LESSON 11:

The for Loop


The for loop works well where the number of iterations of the loop is known
before the loop is entered. The head of the loop consists of three parts separated
by semicolons.

• The first is run before the loop is entered. This is usually the initialisation
of the loop variable.
• The second is a test, the loop is exited when this returns false.
• The third is a statement to be run every time the loop body is completed.
This is usually an increment of the loop counter.

The example is a function which calculates the average of the numbers stored in
an array. The function takes the array and the number of elements as arguments.
float average(float array[], int count)
{ float total = 0.0;
int i;

for(i = 0; i < count; i++)


total += array[i];

return(total / count);
}
The for loop ensures that the correct number of array elements are added up
before calculating the average.
The three statements at the head of a for loop usually do just one thing each,
however any of them can be left blank. A blank first or last statement will mean
no initialisation or running increment. A blank comparison statement will always
be treated as true. This will cause the loop to run indefinitely unless interrupted by
some other means. This might be a return or a break statement.
It is also possible to squeeze several statements into the first or third position,
separating them with commas. This allows a loop with more than one controlling
variable. The example below illustrates the definition of such a loop, with
variables hi and lo starting at 100 and 0 respectively and converging.

for (hi = 100, lo = 0; hi >= lo; hi--, lo++)

The for loop is extremely flexible and allows many types of program
behaviour to be specified simply and quickly.
LESSON 12:

The do while Loop


This is very similar to the while loop except that the test occurs at the end
of the loop body. This guarantees that the loop is executed at least once before
continuing. Such a setup is frequently used where data is to be read. The test then
verifies the data, and loops back to read again if it was unacceptable.
do
{ printf("Enter 1 for yes, 0 for no :");
scanf("%d", &input_value);
} while (input_value != 1 && input_value != 0)

The break Statement


We have already met break in the discussion of the switch statement. It is
used to exit from a loop or a switch, control passing to the first statement beyond
the loop or a switch.
With loops, break can be used to force an early exit from the loop, or to
implement a loop with a test to exit in the middle of the loop body. A break within
a loop should always be protected within an if statement which provides the test
to control the exit condition.

The continue Statement


This is similar to break but is encountered less frequently. It only works within
loops where its effect is to force an immediate jump to the loop control statement.

• In a while loop, jump to the test statement.


• In a do while loop, jump to the test statement.
• In a for loop, jump to the test, and perform the iteration.

Like a break, continue should be protected by an if statement. You are unlikely to


use it very often.
LESSON 13:

The switch Statement


This is another form of the multi way decision. It is well structured, but can only
be used in certain cases where;

• Only one variable is tested, all branches must depend on the value of that
variable. The variable must be an integral type. (int, long, short or char).
• Each possible value of the variable can control a single branch. A final, catch
all, default branch may optionally be used to trap all unspecified cases.

Hopefully an example will clarify things. This is a function which


converts an integer into a vague description. It is useful where we are only
concerned in measuring a quantity when it is quite small.

estimate(number)
int number;
/* Estimate a number as none, one, two, several, many */
{ switch(number) {
case 0 :
printf("None\n");
break;
case 1 :
printf("One\n");
break;
case 2 :
printf("Two\n");
break;
case 3 :
case 4 :
case 5 :
printf("Several\n");
break;
default :
printf("Many\n");
break;
}
}
Each interesting case is listed with a corresponding action. The break statement prevents
any further statements from being executed by leaving the switch. Since case 3 and case 4
have no following break, they continue on allowing the same action for several values of
number.
Both if and switch constructs allow the programmer to make a selection from a number
of possible actions.
The other main type of control statement is the loop. Loops allow a statement, or block of
statements, to be repeated. Computers are very good at repeating simple tasks many
times, the loop is C's way of achieving this.
LESSON 14:

The goto Statement


C has a goto statement which permits unstructured jumps to be made. Its
use is not recommended, so we'll not teach it here. Consult your textbook for
details of its use.

SOME MORE KNOWLEDGE ABOUT C:

ANSI C
The American National Standards Institute defined a standard for C,
eliminating much uncertainty about the exact syntax of the language. This
newcomer, called ANSI C, proclaims itself the standard version of the language.
As such it will inevitably overtake, and eventually replace common C.
ANSI C does incorporate a few improvements over the old common C. The main
difference is in the grammar of the language. The form of function declarations
has been changed making them rather more like Pascal procedures.
This course introduces ANSI C since it is supported by the SUN workstation
compilers. Most C programming texts are now available in ANSI editions.

A Very Simple Program


This program which will print out the message This is a C program
#include <stdio.h>
main()
{
printf("This is a C program\n");
}
Though the program is very simple, a few points are worthy of note.
Every C program contains a function called main. This is the start point of the
program.
#include <stdio.h> allows the program to interact with the screen, keyboard and
filesystem of your computer. You will find it at the beginning of almost every C
program.
main() declares the start of the function, while the two curly brackets show
the start and finish of the function. Curly brackets in C are used to group
statements together as in a function, or in the body of a loop. Such a grouping is
known as a compound statement or a block.
printf("This is a C program\n");

prints the words on the screen. The text to be printed is enclosed in double quotes.
The \n at the end of the text tells the program to print a newline as part of the
output.

Most C programs are in lower case letters. You will usually find upper case letters
used in preprocessor definitions (which will be discussed later) or inside quotes as
parts of character strings. C is case sensitive, that is, it recognises a lower case
letter and it's upper case equivalent as being different.
LESSON 15:

In principle arrays in C are similar to those found in other languages. As we shall


shortly see arrays are defined slightly differently and there are many subtle
differences due the close link between array and pointers. We will look more
closely at the link between pointer and arrays in notes ahead.

Let us first look at how we define arrays in C:


int listofnumbers[50];

BEWARE: In C Array subscripts start at 0 and end one less than the array size.
For example, in the above case valid subscripts range from 0 to 49. This is a BIG
difference between C and other languages and does require a bit of practice to get
in the right frame of mind.

Elements can be accessed in the following ways:-


thirdnumber=listofnumbers[2];
listofnumbers[5]=100;

An array is a collection of variables of the same type which are referenced by a


common name. A specific element in an array is referenced by an index. The
most common array in C is the string, which is simply an array of characters
terminated by a null.

In C, arrays and pointers are closely related; a discussion of one usually refers to
the other.

C has no bounds checking on arrays.

So the general form for declaring a single-dimension array is:

type var_name[size];
In C, all arrays have zero as the index of their first element. Therefore a
declaration of char p[10]; declares a ten-element array (p[0] through p[9]).
LESSON 16:

Generating a Pointer to an Array


A pointer to the first element in an array may be generated by simply specifying
the array name, without any index. For example, given:
int sample[10];
a pointer to the first element may be generated by simply using the name sample.
For example, the following code fragment assigns p the address of the first
element of sample:
int *p;
int sample[10];

p = sample;
The address of the first element may also be specified using the & operator. For
example, sample and &sample[0] produce the same result. The former is usually
used.

Strings
A string is actually an array of characters. Because strings are terminated by a null
('\0'), character arrays must be declared with one extra element (to hold the null).
Although C does not have a string data type, it allows string constants. For
example, "hello there" is a string constant.
C supports a wide range of string manipulation functions, including:
Function Description
strcpy(s1,s2) Copies s2 into s1.

strcat(s1,s2) Concatenates s2 to s1.

strlen(s1) Returns the length of s1.

Returns 0 (false) if s1 and s2 are the same.


strcmp(s1,s2) Returns less than 0 if s1&lts2
Returns greater than 0 if s1>s2

strchr(s1,ch) Returns pointer to first occurrence ch in s1.

strstr(s1,s2) Returns pointer to first occurrence s2 in s1.

Since strcmp() returns false if the strings are equal, it is best to use the !
operator to reverse the condition if the test is for equality.
LESSON 17:

Two-Dimensional Arrays

In C, a two-dimensional array is declared as shown in the following example:


int d[10][20];

Two-dimensional arrays are stored in a row-column matrix. The first index


indicates the row. The row index can be thought of as a &quotpointer"to the
correct row.

When a two-dimensional array is used as an argument to a function, only a


pointer to the first element is passed. However, the receiving function must define
at least the length of the second dimension, e.g.:

function(int x[][20]

Arrays of Strings
Arrays of strings are created using a two-dimensional array. The left index
determines the number of strings. Each string is accessed using only the left index
(eg, gets(array[2] accesses the third string).

Multi-Dimensional Arrays
C allows arrays of more than two dimensions, the exact limit depending on the
individual compiler.

In C, pointers and arrays are closely related. As previously stated, an array


name without an index is a pointer to the first element. For example, given the
array char my_array[10];, my_array and &my_array[0] are identical.

Conversely, any pointer variable may be indexed as if it were declared as an


array. For example, in this program fragment:

int *p, i[10];


p = i;
p[5] = 100; // assignment using index
(p+5) = 100 // assignment using pointer arithmetic
both assignments achieve the same thing.

LESSON 18:
Array Initialization
Arrays may be initialized at the time of declaration. The following example
initializes a ten-element integer array:
int i[10] = { 1,2,3,4,5,6,7,8,9,10 };
Character arrays which hold strings allow a shorthand initialization, e.g.:
char str[9] = "I like C";
which is the same as:
char str[9] = { 'I',' ','l','i','k','e',' ','C','\0' };
When the string constant method is used, the compiler automatically supplies the
null terminator.
Multi-dimensional arrays are initialized in the same way as single-dimension
arrays, e.g.:
int sgrs[6][2] =
{
1,1,
2,4,
3,9,
4,16,
5,25
6,36
};

Unsized Array Initializations


If unsized arrays are declared, the C compiler automatically creates an array big
enough to hold all the initializers. This is called an unsized array. Example
declaration/initializations are as follows:
char e1[] = "read error\n";
char e2[] = "write error\n";
int sgrs[][2] =
{
1,1,
2,4,
3,9
4,16,
};

LESSON 19:

Pointers :

Pointer are a fundamental part of C. If you cannot use pointers


properly then you have basically lost all the power and flexibility that C allows.
The secret to C is in its use of pointers.
C uses pointers a lot. Why?:

• It is the only way to express some computations.


• It produces compact and efficient code.
• It provides a very powerful tool.

C uses pointers explicitly with:

• Arrays,
• Structures,
• Functions.

NOTE: Pointers are perhaps the most difficult part of C to understand. C's
implementation is slightly different DIFFERENT from other languages.

What is a Pointer?

A pointer is a variable which contains the address in memory of another variable.


We can have a pointer to any variable type.

The unary or monadic operator & gives the ``address of a variable''.

The indirection or dereference operator * gives the ``contents of an object


pointed to by a pointer''.

To declare a pointer to a variable do:

int *pointer;

NOTE: We must associate a pointer to a particular type: You can't assign the
address of a short int to a long int, for instance.

LESSON 20:

(Pointers Contd..)
Consider the effect of the following code:

int x = 1, y = 2;

int *ip;
ip = &x;
y = *ip;
x = ip;
*ip = 3;

It is worth considering what is going on at the machine level in memory to


fully understand how pointer work. Consider Fig. 9.1. Assume for the sake of this
discussion that variable x resides at memory location 100, y at 200 and ip at 1000.
Note A pointer is a variable and thus its values need to be stored somewhere. It is
the nature of the pointers value that is new.

Fig. 9.1 Pointer, Variables and Memory Now the assignments x = 1 and y = 2
obviously load these values into the variables. ip is declared to be a pointer to an
integer and is assigned to the address of x (&x). So it gets loaded with the value 100.

IMPORTANT: When a pointer is declared it does not point anywhere. You must
set it to point somewhere before you use it.

So ...

int *ip;
*ip = 100;
will generate an error (program crash!!).
The correct use is:

int *ip;
int x;

ip = &x;
*ip = 100;

LESSON 21:

Pointers and Arrays

Pointers and arrays are very closely linked in C.


Hint: think of array elements arranged in consecutive memory locations.
Consider the following:

int a[10], x;
int *pa;
pa = &a[0]; /* pa pointer to address of a[0] */

x = *pa;
/* x = contents of pa (a[0] in this case) */

Fig. 9.3 Arrays and Pointers

To get somewhere in the array (Fig. 9.3) using a pointer we could do:
pa + i a[i]

WARNING: There is no bound checking of arrays and pointers so you can easily
go beyond array memory and overwrite other things.

C however is much more subtle in its link between arrays and pointers.
For example we can just type
pa = a;
instead of
pa = &a[0]
and
a[i] can be written as *(a + i).
i.e. &a[i] a + i.
We also express pointer addressing like this:
pa[i] *(pa + i).

However pointers and arrays are different:

• A pointer is a variable. We can do pa = a and pa++.

• An Array is not a variable. a = pa and a++ ARE


ILLEGAL.

LESSON 22:

(Pointers and Arrays in more detail)

pointer is a variable suitable for keeping memory addresses of other variables,


the values you assign to a pointer are memory addresses of other variables (or
other pointers).

How useful are pointers for scientific programming? Probably much less than
C fans think, few algorithms used in scientific require pointers. It is well-
known that having unrestricted pointers in a programming language makes it
difficult for the compiler to generate efficient code.

C pointers are characterized by their value and data-type. The value is the
address of the memory location the pointer points to, the type determines how
the pointer will be incremented/decremented in pointer (or subscript)
arithmetic (see below).

Arrays in C and the array equation


----------------------------------
We will use 2D arrays in the following text instead of general N-dimensional
arrays, they can illustrate the subtle points involved with using arrays and
pointers in C, and the arithmetic will be more manageable.

A 2D array in C is treated as a 1D array whose elements are 1D arrays (the


rows).

For example, a 4x3 array of T (where "T" is some data type) may be declared
by: "T mat[4][3]", and described by the following scheme:

+-----+-----+-----+
mat == mat[0] ---> | a00 | a01 | a02 |
+-----+-----+-----+
+-----+-----+-----+
mat[1] ---> | a10 | a11 | a12 |
+-----+-----+-----+
+-----+-----+-----+
mat[2] ---> | a20 | a21 | a22 |
+-----+-----+-----+
+-----+-----+-----+
mat[3] ---> | a30 | a31 | a32 |
+-----+-----+-----+

LESSON 23:

(Pointers and Arrays in more detail) (Contd.)

The array elements are stored in memory row after row, so the array equation
for element "mat[m][n]" of type T is:
address(mat[i][j]) = address(mat[0][0]) + (i * n + j) * size(T)
address(mat[i][j]) = address(mat[0][0]) +
i * n * size(T) +
j * size(T)

address(mat[i][j]) = address(mat[0][0]) +
i * size(row of T) +
j * size(T)

A few remarks:

1) The array equation is important, it is the connection between the abstract


data-type and its implementation. In Fortran (and other languages) it is
"hidden" from the programmer, the compiler automatically "plants" the
necessary code whenever an array reference is made.

2) For higher-dimensional arrays the equation gets more and more


complicated. In some programming languages an arbitrary limit on
the dimension is imposed, e.g. Fortran arrays can be 7D at most.

2) Note that it's more efficient to compute the array equation "iteratively"
- not using the distributive law to eliminate the parentheses (just count the
arithmetical operations in the first two versions of the array equation above).
The K&R method (see below) works iteratively.

It reminds one of Horner's Rule for computing a polynomial iterativly, e.g.

a * x**2 + b * x + c = (a * x + b) * x + c

computing the powers of x is eliminated in this way.

4) The number of rows doesn't enter into the array equation, you don't
need it to compute the address of an element. That is the reason you don't
have to specify the first dimension in a routine that is being passed a
2Darray, just like in Fortran's assumed-size arrays.

LESSON 24:

(Pointers and Arrays in more detail) (Contd.)

The K&R method of reducing arrays to pointers


---------------------------------------------
K&R tried to create a unified treatment of arrays and pointers, one that
would expose rather than hide the array equation in the compiler's code.
They found an elegant solution, albeit a bit complicated. The "ugly" array
equation is replaced in their formulation by four rules:

1) An array of dimension N is a 1D array with


elements that are arrays of dimension N-1.

2) Pointer addition is defined by:

ptr # n = ptr + n * size(type-pointed-into)

"#" denotes here pointer addition to avoid confusion with ordinary


addition. The function "size()" returns object's sizes.

3) The famous "decay convention": an array is treated as a pointer that points


to the first element of the array.

The decay convention shouldn't be applied more than once to the same
object.

4) Taking a subscript with value i is equivalent to the operation: "pointer-add


i and then type-dereference the sum", i.e.

xxx[i] = *(xxx # i)

When rule #4 + rule #3 are applied recursively (this is the case of a multi-
dimensional array), only the data type is dereferenced and not the pointer's
value, except on the last step.

LESSON 25:

(Pointers and Arrays in more detail) (Contd.)

K&R rules imply the array equation


----------------------------------
We will show now that the array equation is a consequence of the above
rules (applied recursively) in the case of a 2D array:

mat[i] = *(mat # i) (rule 4)

mat[i][j] = *(*(mat # i) # j) (rule 4)

"mat" is clearly a "2D array of T" and decays by rule #3 into a "pointer to a
row of T". So we get the first two terms of the array equation.

mat[i][j] = *(*(mat + i * sizeof(row)) # j)


^^^^^^^^^^^^^^^^^^^^^
Pointer to row of T

Dereferencing the type of "(mat # i)" we get a "row of T".

mat[i][j] = *((mat + i * sizeof(row)) # j)


^^^^^^^^^^^^^^^^^^^^^^^
Row of T

We have now one pointer addition left, using again the "decay convention",
the 1D array "row of T" becomes a pointer to its first element, i.e.
"pointer to T". We perform the pointer addition, and get the third term
of the array equation:

mat[i][j] = *(mat + i * sizeof(row) + j * sizeof(T))


^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Pointer to T

address(mat[i][j]) = mat + i * sizeof(row) + j * sizeof(T)


Remember that "mat" actually points to the first element of the array,
so we can write:
address(mat[i][j]) = address(mat[0][0]) +
i * sizeof(row) +
j * sizeof(T)
This is exactly the array equation. QED

LESSON 26:

(Pointers and Arrays in more detail) (Contd.)

Why a double pointer can't be used as a 2D array?


-------------------------------------------------
This is a good example, although the compiler may not complain, it is wrong to
declare: "int **mat" and then use "mat" as a 2D array. These are two very
different data-types and using them you access different locations in memory.
On a good machine (e.g. VAX/VMS) this mistake aborts the program with a
"memory access violation" error.
This mistake is common because it is easy to forget that the decay convention
mustn't be applied recursively (more than once) to the same array, so a 2D
array is NOT equivalent to a double pointer.
A "pointer to pointer of T" can't serve as a "2D array of T". The 2D array is
"equivalent" to a "pointer to row of T", and this is very different from "pointer
to pointer of T".
When a double pointer that points to the first element of an array, is used with
subscript notation "ptr[0][0]", it is fully dereferenced two times (see rule #5).
After two full dereferencings the resulting object will have an address equal to
whatever value was found INSIDE the first element of the array. Since the first
element contains our data, we would have wild memory accesses.

We could take care of the extra dereferencing by having an intermediary


"pointer to T":

type mat[m][n], *ptr1, **ptr2;

ptr2 = &ptr1;
ptr1 = (type *)mat;

but that wouldn't work either, the information on the array "width" (n), is lost,
and we would get right only the first row, then we will have again wild memory
accesses.

A possible way to make a double pointer work with a 2D array notation


is having an auxiliary array of pointers, each of them points to a
row of the original matrix.

type mat[m][n], *aux[m], **ptr2;

ptr2 = (type **)aux;


for (i = 0 ; i < m ; i++)
aux[i] = (type *)mat + i * n;

LESSON 27:

(Pointers and Arrays in more detail) (Contd.)

An example program:

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

main()
{
long mat[5][5], **ptr;

mat[0][0] = 3;
ptr = (long **)mat;

printf(" mat %p \n", mat);


printf(" ptr %p \n", ptr);
printf(" mat[0][0] %d \n", mat[0][0]);
printf(" &mat[0][0] %p \n", &mat[0][0]);
printf(" &ptr[0][0] %p \n", &ptr[0][0]);

return;
}

The output on VAX/VMS is:

mat 7FDF6310
ptr 7FDF6310
mat[0][0] 3
&mat[0][0] 7FDF6310
&ptr[0][0] 3

We can see that "mat[0][0]" and "ptr[0][0]" are different objects


(they have different addresses), although "mat" and "ptr" have
the same value.

LESSON 28:

Functions in C

Almost all programming languages have some equivalent of the function.


You may have met them under the alternative names subroutine or procedure.
Some languages distinguish between functions which return variables and those
which don't. C assumes that every function will return a value. If the programmer
wants a return value, this is achieved using the return statement. If no return value
is required, none should be used when calling the function.
Here is a function which raises a double to the power of an unsigned, and returns
the result.

double power(double val, unsigned pow)


{ double ret_val = 1.0;
unsigned i;

for(i = 0; i < pow; i++)


ret_val *= val;

return(ret_val);
}
The function follows a simple algorithm, multiplying the value by itself
pow times. A for loop is used to control the number of multiplications, and
variable ret_val stores the value to be returned.

Let us examine the details of this function.

double power(double val, unsigned pow)


This line begins the function definition. It tells us the type of the return value, the
name of the function, and a list of arguments used by the function. The arguments
and their types are enclosed in brackets, each pair separated by commas.

The body of the function is bounded by a set of curly brackets. Any variables
declared here will be treated as local unless specifically declared as static or
extern types.

return(ret_val);
On reaching a return statement, control of the program returns to the calling
function. The bracketed value is the value which is returned from the function. If
the final closing curly bracket is reached before any return value, then the
function will return automatically, any return value will then be meaningless.

The example function can be called by a line in another function which looks like
this
result = power(val, pow);

This calls the function power assigning the return value to variable result.

Here is an example of a function which does not return a value.

void error_line(int line)


{
fprintf(stderr, "Error in input data: line %d\n", line);
}

The definition uses type void which is optional. It shows that no return value is
used. Otherwise the function is much the same as the previous example, except
that there is no return statement. Some void type functions might use return, but
only to force an early exit from the function, and not to return any value. This is
rather like using break to jump out of a loop.

LESSON 29:

Scope of Function Variables


Only a limited amount of information is available within each function.
Variables declared within the calling function can't be accessed unless they are
passed to the called function as arguments. The only other contact a function
might have with the outside world is through global variables.
Local variables are declared within a function. They are created anew each time
the function is called, and destroyed on return from the function. Values passed to
the function as arguments can also be treated like local variables.
Static variables are slightly different, they don't die on return from the function.
Instead their last value is retained, and it becomes available when the function is
called again.
Global variables don't die on return from a function. Their value is
retained, and is available to any other function which accesses them.

Modifying Function Arguments


Some functions work by modifying the values of their arguments. This may be
done to pass more than one value back to the calling routine, or because the return
value is already being used in some way. C requires special arrangements for
arguments whose values will be changed.
You can treat the arguments of a function as variables, however direct
manipulation of these arguments won't change the values of the arguments in the
calling function. The value passed to the function is a copy of the calling value.
This value is stored like a local variable, it disappears on return from the function.
There is a way to change the values of variables declared outside the function. It
is done by passing the addresses of variables to the function. These addresses, or
pointers, behave a bit like integer types, except that only a limited number of
arithmetic operators can be applied to them. They are declared differently to
normal types, and we are rarely interested in the value of a pointer. It is what lies
at the address which the pointer references which interests us.
To get back to our original function, we pass it the address of a variable whose
value we wish to change. The function must now be written to use the value at
that address (or at the end of the pointer). On return from the function, the desired
value will have changed. We manipulate the actual value using a copy of the
pointer.

LESSON 30:

Function Prototypes:

A function declaration precedes the function definition and specifies the


name, return type, storage class, and other attributes of a function. To be a
prototype, the function declaration must also establish types and identifiers for the
functions arguments.

The prototype has the same form as the function definition, except that it
is terminated by a semicolon immediately following the closing parenthesis and
therefore has no body. In either case, the return type must agree with the return
type specified in the function definition.

Function prototypes have the following important uses:

i) They establish the return type for functions that return types other than int.
Although functions that return int values do not require prototypes, prototypes
are recommended.

ii) Without complete prototypes, standard conversions are made, but no


attempt is made to check the type or number of arguments with the number of
parameters.

iii) Prototypes are used to initialize pointers to functions before those


functions are defined.

iv) The parameter list is used for checking the correspondence of arguments in
the function call with the parameters in the function definition.

The converted type of each parameter determines the interpretation of the


arguments that the function call places on the stack. A type mismatch between an
argument and a parameter may cause the arguments on the stack to be
misinterpreted. For example, on a 16-bit computer, if a 16-bit pointer is passed as
an argument, then declared as a long parameter, the first 32 bits on the stack are
interpreted as a long parameter. This error creates problems not only with the
long parameter, but with any parameters that follow it. You can detect errors of
this kind by declaring complete function prototypes for all functions.

A prototype establishes the attributes of a function so that calls to the function that
precede its definition (or occur in other source files) can be checked for argument-
type and return-type mismatches.

LESSON 31:

Pointer and Functions


Let us now examine the close relationship between pointers and C's other major
parts. We will start with functions.
When C passes arguments to functions it passes them by value.

There are many cases when we may want to alter a passed argument in the
function and receive the new value back once to function has finished. Other
languages do this (e.g. var parameters in PASCAL). C uses pointers explicitly to
do this. Other languages mask the fact that pointers also underpin the
implementation of this.

The best way to study this is to look at an example where we must be able to
receive changed parameters.

Let us try and write a function to swap variables around?

The usual function call:


swap(a, b) WON'T WORK.

Pointers provide the solution: Pass the address of the variables to the functions
and access address of function.

Thus our function call in our program would look like this:
swap(&a, &b)

The Code to swap is fairly straightforward:

void swap(int *px, int *py)


{ int temp;
temp = *px;
/* contents of pointer */
*px = *py;
*py = temp;
}

LESSON 32:

Common Pointer Pitfalls :

Here we will highlight two common mistakes made with pointers.

Not assigning a pointer to memory address before using it

int *x;
*x = 100;

We need a physical location say: int y;

x = &y;
*x = 100;

This may be hard to spot. NO COMPILER ERROR. Also x could some random
address at initialisation.

Illegal indirection
Suppose we have a function malloc() which tries to allocate memory dynamically
(at run time) and returns a pointer to block of memory requested if successful or a
NULL pointer otherwise.

char *malloc() -- a standard library function (see later).


Let us have a pointer: char *p;
Consider:
*p = (char *) malloc(100); /* request 100 bytes of memory */
*p = `y';
There is mistake above. What is it?

No * in
*p = (char *) malloc(100);
Malloc returns a pointer. Also p does not point to any address.
The correct code should be:
p = (char *) malloc(100);

If code rectified one problem is if no memory is available and p is NULL.


Therefore we can't do:
*p = `y';.

A good C program would check for this:

p = (char *) malloc(100);
if ( p == NULL)

{ printf(``Error: Out of Memory n'');


exit(1);
}
*p = `y';
Exercise 1.

Write a program to find the number of times that a given word(i.e. a short string)
occurs in a sentence (i.e. a long string!).

Read data from standard input. The first line is a single word, which is
followed by general text on the second line. Read both up to a newline
character, and insert a terminating null before processing.

Typical output should be:

The word is "the".


The sentence is "the cat sat on the mat".
The word occurs 2 times.
Exercise 2.

Write a program that takes three variable (a, b, b) in as separate parameters and
rotates the values stored so that value a goes to be, b, to c and c to a.

LESSON 33:

The C Preprocessor
The C preprocessor is a tool which filters your source code before it is compiled. The
preprocessor allows constants to be named using the #define notation. The preprocessor
provides several other facilities which will be described here. It is particularly useful for
selecting machine dependent pieces of code for different computer types, allowing a
single program to be compiled and run on several different computers.
The C preprocessor isn't restricted to use with C programs, and programmers who use
other languages may also find it useful, however it is tuned to recognise features of the C
language like comments and strings, so its use may be restricted in other circu mstances.
The preprocessor is called cpp, however it is called automatically by the compiler so you
will not need to call it while programming in C.

Using #define to Implement Constants


We have already met this facility, in its simplest form it allows us to define textual
substitutions as follows.

#define MAXSIZE 256

This will lead to the value 256 being substituted for each occurrence of the word
MAXSIZE in the file.

Using #define to Create Functional Macros

#define can also be given arguments which are used in its replacement. The definitions
are then called macros. Macros work rather like functions, but with the following minor
differences.

• Since macros are implemented as a textual substitution, there is no effect on


program performance (as with functions).
• Recursive macros are generally not a good idea.
• Macros don't care about the type of their arguments. Hence macros are a good
choice where we might want to operate on reals, integers or a mixture of the two.
Programmers sometimes call such type flexibility polymorphism.
• Macros are generally fairly small.

Macros are full of traps for the unwary programmer. In particular the textual substitution
means that arithmetic expressions are liable to be corrupted by the order of evaluation
rules.
Here is an example of a macro which won't work.

#define DOUBLE(x) x+x


Now if we have a statement

a = DOUBLE(b) * c;
This will be expanded to

a = b+b * c;
And since * has a higher priority than +, the compiler will treat it as.

a = b + (b * c);
The problem can be solved using a more robust definition of DOUBLE

#define DOUBLE(x) (x+x)


Here the brackets around the definition force the expression to be evaluated before any
surrounding operators are applied. This should make the macro more reliable.
In general it is better to write a C function than risk using a macro.

LESSON 34:

Reading in Other Files using #include

The preprocessor directive #include is an instruction to read in the entire contents of


another file at that point. This is generally used to read in header files for library
functions. Header files contain details of functions and types used within the library.
They must be included before the program can make use of the library functions.
Library header file names are enclosed in angle brackets, < >. These tell the preprocessor
to look for the header file in the standard location for library definitions. This is
/usr/include for most UNIX systems.
For example
#include <stdio.h>

Another use for #include for the programmer is where multi-file programs are being
written. Certain information is required at the beginning of each program file. This can be
put into a file called globals.h and included in each program file. Local header file names
are usually enclosed by double quotes, " ". It is conventional to give header files a name
which ends in .h to distinguish them from other types of file.

Conditional selection of code using #ifdef

The preprocessor has a conditional statement similar to C's if else. It can be used to
selectively include statements in a program. This is often used where two different
computer types implement a feature in different ways. It allows the programmer to
produce a program which will run on either type.

The keywords for conditional selection are; #ifdef, #else and #endif.

#ifdef
takes a name as an argument, and returns true if the the name has a current
definition. The name may be defined using a #define, the -d option of the
compiler, or certain names which are automatically defined by the UNIX
environment.
#else
is optional and ends the block beginning with #ifdef. It is used to create a 2 way
optional selection.
#endif
ends the block started by #ifdef or #else.
Where the #ifdef is true, statements between it and a following #else or #endif are
included in the program. Where it is false, and there is a following #else, statements
between the #else and the following #endif are included.

Вам также может понравиться