[go: up one dir, main page]

0% found this document useful (0 votes)
296 views14 pages

Module 2 - CP - CS100 - Notes - KtuQbank

This document provides examples and explanations of arrays, strings, and two-dimensional arrays in C. It discusses declaring and initializing arrays, accessing array elements, passing arrays to functions, and important rules regarding arrays. It also covers initializing and manipulating strings, NULL termination, and common string functions from the string.h library. Two-dimensional arrays are demonstrated with examples of declaration and element access.

Uploaded by

Binesh RB
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
296 views14 pages

Module 2 - CP - CS100 - Notes - KtuQbank

This document provides examples and explanations of arrays, strings, and two-dimensional arrays in C. It discusses declaring and initializing arrays, accessing array elements, passing arrays to functions, and important rules regarding arrays. It also covers initializing and manipulating strings, NULL termination, and common string functions from the string.h library. Two-dimensional arrays are demonstrated with examples of declaration and element access.

Uploaded by

Binesh RB
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 14

KTUQBANK.

COM

Module II
Arrays and strings- example programs. Two dimensional arrays - matrix operations.
Structure, union and enumerated data type.

2.1 Arrays and strings


An array is a group of similar variables which are stored together. In C, we can declare
arrays of any variable type like so:

int a[24]; /* An array of 24 integers */


float b[6]; /* An array of 6 floats */
char c[2]; /* An array of 2 characters */

a[0]= 3;
a[1]= 5;
b[0]= 2.1;
b[1]= 2.4e6;
c[0]= 'H';
c[1]= 'i';
printf ("a[0] is %d, b[1] is %f, c[1] is %c\n",a[0], b[1], c[1]);

We must specify the size of the array before we start – so we can't have code which works out what
size an array should be and then sets it up to be that size. (Yes, this can be annoying).

IMPORTANT RULE: int a[100]; initialises an array of 100 integers. They can be referred
to by the array index inside the square brackets. For example a[n] refers to the nth element of the
array. You can use an array element with an index anywhere inside a function where you could use
a normal integer.

CAUTION: It inevitably confuses new C programmers that, in C, array offsets start from 0. If we
declare an array of ints as int a[100]; then its first element is a[0] and its last element is
a[99] attempts to access element a[100] are an error.

We can pass arrays to functions like so:

void set_array(int [], int); /* Prototype for function to set


array to zero */

void set_array(int array[], int length)


/* Sets an array of "length" integers all to zero */
{
int i;
for (i= 0; i < length; i++)
array[i]= 0;
}

This code fragment shows another important thing about arrays.

IMPORTANT RULE: The values of array elements can be changed within a function. [This is
because an array is really a special type of pointer – but don't worry about that for now].

Note also that you can't have a function which returns an array. Since a function can change array
values you will never need to do this anyway.
KTUQBANK.COM
We can also initialise arrays like so:

int a[5]= {1,5,10,15,20};

[But note that you can't set arrays all in one like that in the middle of a function – so it is an error to
write.]

int a[5];
a= {1,5,10,15,20}; /*This line should not compile */

Arrays in our sieve

Our table of potential prime numbers is surely best stored as an array. We might also consider what
we want to store. We could store the number in the appropriate place in the array – but this is not
really necessary – after all, we only really need to know if the number at a particular place in the
array should be crossed out or not.

Because of this, we could make it an array of char to save memory or an array of int to keep
things simple (char takes up less memory than int) – we've gone for the int solution this time
since we're not using that much memory here – if we wanted a larger sieve then we might be forced
to reconsider this. We'll also take some advice from the former section about magic numbers and
create some enum statements to make things clearer. So, we might then have a starting bit of code
which looks like this:

const int UN_CROSSED= 0;


const int CROSSED= 1;
const int MAX_NUM = 100;

int main()
{
int i;
int sieve[MAX_NUM + 1]; /* Array to hold sieve */

/* Everything should start out uncrossed out */


for (i= 0; i < MAX_NUM + 1; i++)
sieve[i]= UN_CROSSED;
sieve[0]= CROSSED;
sieve[1]= CROSSED;
.
.
.
return 0;
}

Note that the array has a size of MAX_NUM + 1. Remember that an array of n elements in C has
elements numbered from 0 to n-1. We could decide to use an array of MAX_NUM elements with
the table entry for 1 stored in 0 and 2 stored in 1 – but this would get awfully confusing awfully
quickly. Instead of this, we've elected to include element 0 in our table – but cross it out as a special
case – as we have with element 1.

Look for obvious loops

One early task when you're looking at any algorithm is to consider what loops to use. In this case,
the most obvious loop is the one between steps 2 and 4 which changes the value of k. In all cases it
is arbitrary whether a for or a while loop is chosen – the two are always interchangeable:
KTUQBANK.COM

for (initialiser ; condition ; increment) {


code;
}

is exactly equivalent to:

initialiser;
while (condition) {
code;
increment;
}

Generally speaking, if your for statement looks too "busy" then try rewriting it as a while loop
which might make it clearer. In this case, we can put it into a relatively tidy for loop like so:

for (k= 2; k <= sqrt(MAX_NUM); k= next_k(sieve,k))

Another obvious loop is in the cross_k function. Again a for loop seems the best solution:

int i;
/* Cross out all multiples of k but the first */
for (i= k*2; i < MAX_NUM + 1; i+= k)
sieve[i]= CROSSED;

Note that i is starting at twice k – obviously we don’t want to cross out k itself.

Strings in C
Arrays of type char are known as strings in C. Actually, without knowing it, you've already seen
an array of char. When we typed:

printf ("Hello world!\n");

the phrase Hello world! was really an array of characters. We could equally well have
written:

char hi_there[100]= "Hello world!\n";


printf (hi_there);

or even:

char hi_there[100]= "Hello world!\n";


printf ("%s",hi_there);

%s in a printf string is similar to %d but instead of an integer fits in a string to the print statement.

Note that as with arrays, we can only initialise a string with this type of construct. It is illegal to
write:

char hi_there[100];
hi_there= "Hello World!\n"; /* This line should not compile */
KTUQBANK.COM
We can write to a string using the sprintf function in the same way that we can write to the
screen using printf:

char answer[100];
int ans= 42;
sprintf (answer,"The answer is %d\n",ans);

sprintf is another useful function which is part of the stdio.h library.

IMPORTANT RULE: Strings in C should be NULL terminated. That is to say, the last element of
the character array must be '\0' or the char which has value 0.

When we write:

char a[14]="Hello World!\n";

we are saying that a is an array of 14 characters of which the elements are:

a[0]= 'H';
a[1]= 'e';
a[2]= 'l';
.
.
.
a[10]= 'd';
a[11]= '!';
a[12]= '\n';
a[13]= '\0';

The reason for having the '\0' convention is so that we know when to stop printing. If we have an
array of 100 characters the first few of which are Hello World! if there were no '\0'
character then the print statement would simply continue to print until it came to a '\0' needless
to say, most of what it would print would be rubbish. Fortunately, the C language most of the time
takes care of the '\0' for you. However, if you accidentally overwrite it then you can be in
trouble. For example if we later alter the string:

char hi[12]= "Hello World"; /*Initialise a string which is


correctly
terminated with \0 */
printf ("String is (%s)\n",hi); /* Prints the string fine */
hi[11]= '!'; /* Add an exclamation mark at the end
of
the string */
printf ("String is now (%s)\n",hi); /* Oh oh! We wrote over the \0

this may print nonsense */

Usually you will find this could print something like this:

Hello world
Hello world!a$?!#
d1%

I.e. it prints the second Hello world correctly but then continues to print a lot of random nonsense
KTUQBANK.COM
after it. This is because the print statement will simply continue to print until it finds the '\0'
even if this is a long way after where you thought the string should have ended. [Sometimes you
get lucky and by coincidence the next character is '\0' – code which relies on you being lucky is
NOT good code].

If we write a function which takes a char [] variable as an argument then we can also send it
strings in double quotes. So, for example if we write:

void print_quoted_string (char []); /* Prototype for function*/

void print_quoted_string (char str[])


/* Prints a string enclosed in quotes */
{
printf ("\"%s\"\n",str);
}

we can call it in two ways:

char hi [100]= "Hello World!\n";


print_quoted_string (hi); /* One way */
print_quoted_string ("Hello World!\n"); /* Another way*/

Both of these should work exactly the same.

The string.h library contains many useful functions for strings which we will look at in the next
worksheet. Some of the more common ones are:

strcat (adds two strings together)


strcpy (copies one string to another)
strlen (returns the length of the string)
strcmp (compares two strings to see if they are the same)

It is also worth pointing out that if we initialise a string then we do not have to give it a length, it
will automatically take the minimum length to hold the data given:

char test[]="1234567890\n";

Is the same as if we’d declared the string to be 12 characters (10 numbers, one for the return and
one for the NULL termination).

2.2 Two Dimensional Arrays


We can declare arrays with more than one dimension in C. For example:

int array [3][6];


int array2[3][6] = {
{4,5,6,7,8,9},
{1,5,6,8,2,4},
{0,4,4,3,1,1}
};

Such arrays are accessed like so:

array[1][4]= -2;
if (array[2][1] > 0) {
KTUQBANK.COM
printf ("Element [2][1] is %d", array[2][1]);
}

Remember that, like ordinary arrays, multi-dimensional arrays are numbered from 0. Therefore, the
array above has elements from array[0][0] to array[2][5].

CAUTION: A common beginner mistake is to attempt to access array elements using the syntax
array[1,4]= -2;

When we pass multi-dimensional arrays to functions or use a prototye, we must include the size of
the array in the prototype. E.g.

void process_array (int [3][6]);

void process_array (int array[3][6])


{
.
.
.
}

CAUTION: It's easy to become confused here. The above function body defines a function which
takes as an argument a 3 by 6 array of int. However, if we call it with:

process_array (array[3][6]);

Then we will cause a problem as this will not pass the array – this will attempt to pass the element
[3][6] of the array – which is out of range anyway if the array is [3][6].

Multi-dimensional arrays are actually quite rare in C – an array of pointers is more common and
more useful.

It is worth mentioning that char [3][10]; declares 3 strings 10 characters long not 10 strings
3 characters long.

2.3 Matrix Operations


A matrix is a rectangular array of numbers or symbols arranged in rows and columns. The
following section contains a list of C programs which perform the operations of Addition,
Subtraction and Multiplication on the 2 matrices. The section also deals with evaluating the
transpose of a given matrix. The transpose of a matrix is the interchange of rows and columns.

Matrix addition
The matrix addition is done as follows :
KTUQBANK.COM
The code segment for matrix addition is as follows:
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
{
ADD[i][j]=A[i][j]+B[i][j];
}
}

Matrix Multiplication

Matrix Transpose

Algorithm to find transpose of a matrix


Let A be the input matrix of size M x N
and T be the transpose matrix of A(T =
AT).
• To find transpose of a matrix, we
have to interchange the row and
column index of every element.
For example, an element of matrix
A at position [i][j] will become an
element of transpose matrix T at
position[j][i](T[j][i] = A[i][j])
• Traverse matrix A row wise(first
all elements of a row from left to
right, then jump to next row) using
two loops(check line number 23
and 24 of below program).
• For any element A[i][j], copy it's
value to T[j][i](swap row and
column indexes).
KTUQBANK.COM
2.4 Structure

Adding new types to C typedef and struct statements

You might be wondering about that peculiar FILE thing that seems to be usable like an int or a
float in functions. FILE is an example of an important part of the C language known as a
structure. A structure is a built up part of the C language which behaves like a built in type. We can
declare a group of associated variables which are associated as a structure.

IMPORTANT RULE: A struct statement declares a structure which can hold information of
existing types (or indeed other structures). A struct should be declared at the top of the code or
in a header file.

For example, if we are writing a program for a bank, we might decide that an account is a
fundamental data type in such a program. Therefore we declare a structure which deals with each
account:

struct account {
char acct_holder[80]; /* Name of holder */
char address[100]; /* Address of holder */
double balance; /* Balance in pounds */
int acct_type; /* type of account 1= savings 2= current
*/
};

We can declare variables to have this type. And we can access elements using the dot notation
shown below:

struct account new_acct;


strcpy (new_acct.acct_holder, "S. Holmes");
strcpy (new_acct.address, "222B Baker St., London");
new_acct.balance= 23.50;
new_acct.type= 1;

We can use any of these variables within a struct wherever we could use a variable of the same
type. So, for example, we could add this:

float interest;
float rate= 1.75;
interest= new_acct.balance * rate / 100.0;
printf ("Adding interest of %f\n",interest);
new_acct.balance+= interest;

We can make our struct look even more like a built in type such as int or float by using a
typedef statement. For example:

typedef struct imaginary_number {


double real_part;
double imag_part;
} IMAG_NUM;

we can then use the new type IMAG_NUM pretty much wherever we can use an int. For
example:
KTUQBANK.COM
IMAG_NUM x,y;
double a= 2.0;
x.real_part= 3.0;
x.imag_part= a;

We can also use these typedef types in functions for example:

IMAG_NUM mult_imag (IMAG_NUM, IMAG_NUM);


/* Function to multiply imaginary numbers */

IMAG_NUM mult_imag (IMAG_NUM x, IMAG_NUM y)


{
IMAG_NUM ans;
ans.real_part= x.real_part*y.real_part –
x.imag_part*y.imag_part;
ans.imag_part= x.real_part*y.imag_part +
y.real_part*x.imag_part;
return ans;
}

IMPORTANT RULE: typedef can be used to associate a label with a structure. By


convention, we put our typedef names in ALL_CAPS or at least InitialLetterCaps in order to
distinguish them from built in types and variable names. Like a struct statement, typedef
should be at the start of the code or in a header file.

We can even have arrays of typedef variables. For example:

IMAG_NUM points[2];
points[0].real_part= 3.0;
points[0].imag_part= 1.0;
points[1].real_part= -3.5;
points[1].imag_part= -2.0;

You can also use typedef to create another name for a built in type. For example you could
write:

typedef int Length;


the only common use for this is in defining things which are a pain in the neck to type. For
example, if your program uses a lot of unsigned char values (recall that such a variable can
store a number from 0 to 255) then you might want to:

typedef unsigned char uchar;

simply to save typing. (Programmers are notoriously lazy).

IMPORTANT RULE: We can build structs up from other structs. For example, we might
want to define a rectangle in the imaginary plane by defining two of its corners. We could do so as
follows (assuming the previous definition of IMAG_NUM has already been defined earlier in the
program):

typedef struct imag_rect {


IMAG_NUM corner1;
IMAG_NUM corner2;
} IMAG_RECT;
KTUQBANK.COM

We can access the bits of the rectangle as follows:

IMAG_RECT rect1;
rect1.corner1.imag_part= 3.1;
rect1.corner1.real_part= 1.2;
rect1.corner2.imag_part= -2.3;
rect2.corner2.real_part= 1.4;

You can also use typedef to create another name for a built in type. For example you could
write:

typedef double Length;

This would allow you to use Length wherever you could have used double. E.g.
Length room_size=10.4;
Length room_width=12.3;

This isn't particularly useful, however. The only common use for this is in defining things which
are a pain in the neck to type. For example, if your program uses a lot of unsigned char
values (recall that such a variable can store a number from 0 to 255) then you might want to:

typedef unsigned char uchar;

Arrays of structures

Remember from previous lectures and worksheets that structures extend the C language by, for
example, providing imaginary numbers:

typedef struct imaginary_number {


float real_part;
float imag_part;
} IMAG_NUM;

We can even have arrays of typedef variables. For example:

IMAG_NUM points[2];
points[0].real_part= 3.0;
points[0].imag_part= 1.0;
points[1].real_part= -3.5;
points[1].imag_part= -2.0;

This was seen in the deck of cards example in a previous worksheet.

Even more complex structures within structures

We can build structs up from other structs. For example, we might want to define a rectangle
in the imaginary plane by defining two of its corners. We could do so as follows (assuming the
previous definition of IMAG_NUM has already been defined earlier in the program):

typedef struct imag_rect {


IMAG_NUM corner1;
IMAG_NUM corner2;
} IMAG_RECT;
KTUQBANK.COM
We can access the bits of the rectangle as follows:

IMAG_RECT rect1;
rect1.corner1.imag_part= 3.1;
rect1.corner1.real_part= 1.2;
rect1.corner2.imag_part= -2.3;
rect2.corner2.real_part= 1.4;

Pointers to struct

if we have a struct as follows:

typedef struct great_mathematician {


char name[80];
int year_of_birth;
int year_of_death;
char nationality[80];
} MATHEMATICIAN;

We can access elements using the . notation – for example:

MATHEMATICIAN fermat;
fermat.year_of_birth= 1601; /* This margin is too small to
contain*/
fermat.year_of_death= 1665; /* more information about
Fermat*/
strcpy (fermat.name,"Pierre de Fermat");
strcpy (fermat.nationality, "French");
If, instead of using a structure, we had used a pointer to a structure, it would work like this:
MATHEMATICIAN *cantor;
cantor= (MATHEMATICIAN *)malloc (sizeof (MATHEMATICIAN));
(*cantor).year_of_birth= 1845;
(*cantor).year_of_death= 1918;
/* Remember to close comments with a diagonal slash */
strcpy ((*cantor).name= "Georg Cantor");
strcpy ((*cantor).nationality= "German");
.
free(cantor);

All that (*cantor). looks rather confusing. Recall that a * in front of a pointer means
dereference or "the value of whatever it is that my pointer is pointing to". Therefore, to get at the
year of birth of Cantor we must first dereference his pointer with the * to get at the stuff inside the
pointer and then use the dot to get at that part of the struct.

[*cantor.year_of_birth without the brackets would be incorrect. This would be the same
as *(cantor.year_of_birth) and would be used to refer to a pointer to int called
year_of_birth which would be part of the struct called cantor – as opposed to the int
called year_of_birth which actually is part of the pointer to struct called cantor. This is
a subtle but important distinction, the upshot of which is that, without the brackets, your program
will not compile because the compiler can't find a pointer to int in the struct].

There's a handy shorter way of saying (*cantor).year_of_birth since this type of


expression comes up a lot in C.
KTUQBANK.COM
MATHEMATICIAN *cauchy;
cauchy= (MATHEMATICIAN *)malloc (sizeof (MATHEMATICIAN));
/* Dynamic memory allocation- will be discussed later.
The sizeof Cauchy was quite large */
cauchy->year_of_birth= 1789;
cauchy->year_of_death= 1857;
strcpy (cauchy->name= "Augustin Louis Cauchy");
strcpy (cauchy->nationality= "French");
.
.
.
free(cauchy);

IMPORTANT RULE: If we have a pointer to a structure, we can access elements within the
structure using the -> operator. For example: fptr->name would give us access to the element
name of the structure pointed to by the pointer fptr.

Pointers to struct are starting to look a bit complex – so why would anyone use them. There are
two obvious reasons (there is a third, even more compelling reason which we will discuss later):

1) We might want to alter the contents of the structure within a function – recall then that we must
use pass by reference and write our function to accept a pointer argument. For example:

void set_up_turing (MATHEMATICIAN *turing)


{
turing->year_of_birth= 1912;
turing->year_of_death= 1954; /* In tragic circumstances */
strcpy (turing->name,"Alan Mathison Turing");
strcpy (turing->nationality,"British");
}

which we could call with:

MATHEMATICIAN alan;
setup_turing (&alan);

2) If our structure is large then it is extremely inefficient to use pass by value. For example, if we
have a huge structure:

typedef struct my_huge_struct {


int nums[10000];
float more_nums [10000];
char and_some_chars [10000];
} HUGE;

then we might want to write a function which does something with one of these structs:

/* This is not a good way to go about things */


void print_some_floats_in_struct (HUGE);
.
. Some code here
.

void print_some_floats_in_struct (HUGE huge_struct)


{
KTUQBANK.COM
printf ("The third float is %f\n",huge_struct.more_nums[2]);
printf ("The twentieth is %f\n",huge_struct.more_nums[19]);
}

This may seem to be entirely reasonable – and indeed it will work but there's a catch. Because
we're not passing a pointer here we are passing by value. Remember that when we pass by value,
we pass a local copy of the variable which is used locally (and destroyed when the function is over).
In this case, our program will be making a copy of all those 10000 element arrays. It passes that
copy over to the function – which doesn't do a whole lot with it. That's a whole lot of work – and
just to print out a couple of floats. In this case it would be much better to pass the struct by
reference – i.e pass a pointer: When we pass a pointer, the only thing that is sent to the function is a
single pointer – much more efficient than sending a copy of the whole structure:

void efficiently_print_floats (HUGE *);


.
. Some code here
.
void efficiently_print_floats (HUGE *huge_struct)
{
printf ("The third float is %f\n",huge_struct->more_nums[2]);
printf ("The twentieth is %f\n",huge_struct->more_nums[19]);
}
2.5 Union
Definition : A data structure that overlays components in memory, allowing one chunk of
memory to be interpreted in multiple ways.
C provides a data structure called union to deal with situations in which one needs a data
object that can be interpreted in a variety of ways.

The following is an example of union :


union hair_t{
int wears_wig;
char color[20];
};

union hair_t hair data;

This variable hair_data does not contain both wears_wig and color components. Rather it has
either wears_wig component referenced by hair_data. When memory is allocated for hair_data, the
amount of memory is determined by the largest component of the union.

In most cases, it is useful to interpret a chunk of memory in more than one way only if it is
possible to determine which way is currently the valid interpretation. For this reason, unions are
most often used as types of portions of a larger structure, and the larger structure typically contains
a component whose value indicates which interpretation of the union is correct at the present time.
KTUQBANK.COM

2.6Enumerated Data Type.

Definition : it is a datatype whose list of values is specified by the programmer in a type


declaration.

An enumeration is a user-defined data type that consists of integral constants. Enums are lists of
constants . When you need a predefined list of values which do not represent some kind of numeric
or textual data, you should use an enum. To define an enumeration, keyword enum is used.

enum flag { const1, const2, ..., constN };


Here, name of the enumeration is flag.
And, const1, const2,...., constN are values of type flag.
By default, const1 is 0, const2 is 1 and so on. You can change default values of enum elements
during declaration (if necessary).

Declaration

When you create an enumerated type, only blueprint for the variable is created. Here's how you can
create variables of enum type.
enum boolean { false, true };
enum boolean check;

Example: Enumeration Type


#include <stdio.h>

enum week { sunday, monday, tuesday, wednesday, thursday, friday, saturday };

int main()
{
enum week today;
today = wednesday;
printf("Day %d",today+1);
return 0;
}

Output
Day 4

You might also like