Chapter 5 Notes – Practical C Programming, 3rd Edition By Steve Oualline

Chapter 5 covers arrays and more complex variables. Shorthand operators such as ++ and %= are also described.

A lot of info in this chapter - difficult to summarise - almost totally copied...a few successful progs written up to now for 2 full days of study of this PDF - bed ridden with a bad ankle!! - jeez, things must be bad if I'm learning programming!

progstochap5.png

To archive the source files:

tar -cf progs.tar *.c

To list them after:

 

$ tar -tf progs.tar
2plus2.c
array.c
arrayprob.c
avg.c...

ls -ls progs.tar
40 -rw-r--r-- 1 stevee stevee 40960 Jun 8 06:44 progs.tar

Check text file byte sizes (each char is a byte remember, so idea of total text): ls -als *.c
4 -rw-r--r-- 1 stevee stevee 213 Jun 7 13:14 2plus2.c
4 -rw-r--r-- 1 stevee stevee 572 Jun 6 20:05 array.c
4 -rw-r--r-- 1 stevee stevee 587 Jun 6 19:57 arrayprob.c
4 -rw-r--r-- 1 stevee stevee 425 Jun 6 17:12 avg.c...

or lines/chars: $ wc -lm *.c
9 213 2plus2.c
32 572 array.c
27 587 arrayprob.c
17 425 avg.c...

Check text file disk allocation sizes: du -h *.c

4.0K 2plus2.c
4.0K array.c
4.0K arrayprob.c...

To compile and run them in Mint: $ gcc -o 2plus2 2plus2.c

then $ ./2plus2
The answer is 4

Download them here: progs.tar

Chapter 5 covers arrays and more complex variables. Shorthand operators such as ++ and %= are also described.

/* List of data to be sorted and averaged */

int data_list[3];

The above example declares data_list to be an array of three elements.

data_list[0], data_list[1], and data_list[2] are separate variables.

To reference an element of an array, you use a number called the index—the number

inside the square brackets ([ ]). C is a funny language that likes to start counting at

0.

/*avg.c*/

#include <stdio.h>

float data[5]; /* data [0]-[4] to average and total */

float total; /* the total of the data items */

float average; /* average of the items */

int main()

{

data[0] = 34.0;

data[1] = 27.0;

data[2] = 45.0;

data[3] = 82.0;

data[4] = 22.0;

total = data[0] + data[1] + data[2] + data[3] + data[4];

average = total / 5.0;

printf("Total %.0f Average %.0f\n", total, average); /*no dec places*/

return (0);

}

$ ./avg

Total 210 Average 42

Strings are sequences of characters. C does not have a built-in string type; instead,

strings are created out of character arrays. In fact, strings are just character arrays

with a few restrictions. One of these restrictions is that the special character '\0'

(NUL) is used to indicate the end of a string.

For example:

char name[4];

int main()

{

name[0] = 'S';

name[1] = 'a';

name[2] = 'm';

name[3] = ' \0';

return (0);

}

Note that we had to allocate

one character for the end-of-string marker.

To initialize the variable name to Sam, we would write:

#include <string.h>

char name[4];

int main()

{

strcpy (name, "Sam"); /* Legal */

return (0);

}

C uses variable -length strings. For example, the declaration:

#include <string.h>

char string[50];

int main()

{

strcpy(string,"Sam");

}

creates an array (string ) that can contain up to 50 characters. The size of the array

is 50, but the length of the string is 3. Any string up to 49 characters long can be

stored in string. (One character is reserved for the NUL that indicates

end-o f-string.)

/***str.c******/

#include <string.h>

#include <stdio.h>

char name[30]; /* First name of someone */

int main()

{

strcpy(name, "Sam"); /* Initialize the name */

printf("The name is %s\n", name);

return (0);

}

$ ./str

The name is Sam

/**full.c**/

#include <string.h>

#include <stdio.h>

char first[100]; /* first name */

char last[100]; /* last name */

char full_name[200]; /* full version of first and last name */

int main()

{

strcpy(first, "Steve"); /* Initialize first name */

strcpy(last, "Oualline"); /* Initialize last name */

strcpy(full_name, first); /* full = "Steve" */

/* Note: strcat not strcpy */

strcat(full_name, " ");

strcat(full_name, last);

/* full = "Steve " */

/* full = "Steve Oualline" */

printf("The full name is %s\n", full_name);

return (0);

}

$ ./full

The full name is Steve Oualline

The standard function fgets can be used to read a string from the keyboard. The

general form of an fgets call is:

fgets(name, sizeof(name), stdin);

The arguments are:

name

is the name of a character array. The line (including the end -o f-line character) is read into this array.

sizeof( name )

indicates the maximum number of characters to read (plus one for the end-o f-string character). The sizeof function provides a convenient way of limiting the number of characters read to the maximum numbers that the variable can hold. This function will be discussed in more detail in Chapter 14 .

stdin

is the file to read. In this case, the file is the standard input or keyboard.

Suddenly, a real jump in complexity for what happens re array space re-allocation etc! Not sure in my comments what really happens here…?

/*full1.c*/

#include <stdio.h>

#include <string.h>

char first[100]; /* First name of person we are working with */

char last[100]; /* His last name */

/* First and last name of the person (computed) */

char full[200]; /*sum of both first+last arrays */

int main()

{

printf("Enter first name: "); /*screen prompt 1*/

fgets(first, sizeof(first), stdin); /*takes keys input + EOL string size and places in first array?*/

first[strlen(first)-1] = '\0'; /*sizeof(first) array space becomes first-1= input chars+\n-1 and sets [input chars+\n-1] and [n-1] set to \0 to remove \n newline – see below*/

printf("Enter last name: "); /*screen prompt 2*/

fgets(last, sizeof(last), stdin); /*takes keys input + EOL string size and places in last array?*/

last[strlen(last)-1] = '\0'; /*sizeof(last) array space becomes last-1=input chars+\n-1 and [n-1] set to \0 to remove \n newline – see below*/

strcpy(full, first); /*catcopies “first” array chars to “full” string mem space?*/

strcat(full, " "); /*catcopies “blank” space char to “full” string mem space?*/

strcat(full, last); /*catcopies “last” array chars to “full” string mem space?*/

printf("The name is %s\n", full); /*screen prints contents of final “full” array space*/

return (0);

}

Fully commented:

/*full1.c*/
#include <stdio.h>
#include <string.h>
char first[100]; /* first name array; 0-99 less 2 chars long max*/
char last[100]; /* last name array; 0-99 less 2 chars long max*/*
/* first and last name of the person (computed) and printed to screen*/
char full[200]; /* first + last name array space*/
int main()
{
printf("Enter first name: "); /* user input first name */
fgets(first, sizeof(first), stdin); /* user chars put into first array, chars counted, from keybd */
first[strlen(first)-1] = '\0'; /*sets default penultimate char newline \n to \0 so final result is o/p on one line*/
printf("Enter last name: "); /* user input last name */
fgets(last, sizeof(last), stdin); /* user chars put into first array, chars counted, from keybd */
last[strlen(last)-1] = '\0'; /*sets default newline \n to \0*/
strcpy(full, first); /*copies contents of first array into full array space*/
strcat(full, " "); /*cats a space char into full array space*/
strcat(full, last); /*cats contents of last array into full array space*/
printf("The name is %s\n", full); /*prints contents of full array space*/
return (0);
}

full1.c.png

For example, the name "John" would be stored as:

first[0] = 'J'

first[1] = 'o'

first[2] = 'h'

first[3] = 'n'

first[4] = '\n'

first[5] = '\0'

/* end of string */

By setting first[4] to NUL ('\0'), we can shorten the string by one character and

get rid of the unwanted newline. This change can be done with the statement:

first[4] = '\0';

By setting first[4] to NUL ('\0'), we can shorten the string by one character and

get rid of the unwanted newline. This change can be done with the statement:

first[4] = '\0';

The problem is that this method will work only for four -character names. We need a

general algorit hm to solve this problem. The length of this string is the index of the

end-o f-string null character. The character before it is the one we want to get rid of.

So, to trim the string, we use the statement:

first[strlen(first)-1] = '\0';

$ ./full1

Enter first name: steve

Enter last name: edwards

The name is steve edwards

Arrays can have more than one dimension. The declaration for a two-dimensional array is:

type variable[size1][size2]; /* Comment */

For example:

int matrix[2][4]; /* a typical matrix */

Notice that C does not follow the notation used in other languages of

matrix[10,12].

Question 5-1 : Why does Example 5 -7 print the wrong answer? (Click here for the

answer Section 5.15)

/* Example 5-7. p_array/p_array.c */

#include <stdio.h>

int array[3][2]; /* Array of numbers */

int main()

{

int x,y; /* Loop indicies */

array[0][0] = 0 * 10 + 0;

array[0][1] = 0 * 10 + 1;

array[1][0] = 1 * 10 + 0;

array[1][1] = 1 * 10 + 1;

array[2][0] = 2 * 10 + 0;

array[2][1] = 2 * 10 + 1;

printf("array[%d] ", 0); /*wrong C format! Needs [x] [y]*/

printf("%d ", array[0,0]);

printf("%d ", array[0,1]);

printf("\n");

printf("array[%d] ", 1);

printf("%d ", array[1,0]);

printf("%d ", array[1,1]);

printf("\n");

printf("array[%d] ", 2);

printf("%d ", array[2,0]);

printf("%d ", array[2,1]);

printf("\n");

return (0);

}

$ ./arrayprob

array[0] 6295648 6295656

array[1] 6295648 6295656

array[2] 6295648 6295656

Correct Arrays

/*array.c*/

#include <stdio.h>

int array[3][2];

/* Array of numbers */

int main()

{

int x,y;

/* Loop indicies */

array[0][0] = 0 * 10 + 0;

array[0][1] = 0 * 10 + 1;

array[1][0] = 1 * 10 + 0;

array[1][1] = 1 * 10 + 1;

array[2][0] = 2 * 10 + 0;

array[2][1] = 2 * 10 + 1;

printf("array[%d] ", 0);

printf("%d ", array[0][0]);

printf("%d ", array[0][1]);

printf("\n");

printf("array[%d] ", 1);

printf("%d ", array[1][0]);

printf("%d ", array[1][1]);

printf("\n");

printf("array[%d] ", 2);

printf("%d ", array[2][0]);

printf("%d ", array[2][1]);

printf("\n");

return (0);

}

$ ./array

array[0] 0 1

array[1] 10 11

array[2] 20 21

The function scanf is notorious for its poor end-of-line handling, which makes scanf useless for all but an expert.

However, we've found a simple way to get around the deficiencies of scanf—we don't use it. Instead, we use fgets to read a line of input and sscanf to convert the text into numbers. (The name sscanf stands for "string scanf". sscanf is like scanf, but works on strings instead of the standard input.)

Normally, we use the variable line for lines read from the keyboard:

char line[100]; /* Line of keyboard input */

When we want to process input, we use the statements:

fgets(line, sizeof(line), stdin);

sscanf(line, format, &variable1, &variable2 . . .;

Here fgets reads a line and sscanf processes it. format is a string similar to the

printf format string. Note the ampersand (& ) in front of the variable names. This

symbol is used to indicate that sscanf will change the value of the associated

variables.

If you forget to put & in front of each variable for sscanf, the result could be a "Segmentation violation core dumped" or "Illegal memory access" error. In some cases a random variable or instruction will be changed. On UNIX, damage is limited to the currentprogram;

/* double.c */

#include <stdio.h>

char line[100];

int value; /* input line from console */

int main()

{

printf("Enter a value: "); /* a value to double */

fgets(line, sizeof(line), stdin);

sscanf(line, "%d", &value);

printf("Twice %d is %d\n", value, value * 2);

return (0);

}

Notice that there is no

\n at the end of Enter a value:. This omission is intentional because we do not want the computer to print a newline after the prompt.

$ ./double

Enter a value: 4

Twice 4 is 8

Example 5 -9 computes the area of a triangle, given the triangle's width and height. For some strange reason, the compiler refuses to believe that we declared the variable width. The declaration is right there on line 2, just after the definition of height. Why isn't the compiler seeing it?

#include <stdio.h>

char line[100]; /* line of input data */

int height; /* the height of the triangle

int width; /* the width of the triangle */

int area; /* area of the triangle (computed) */

int main()

{

printf("Enter width height? ");

fgets(line, sizeof(line), stdin); /*stdin takes chars, sizeof counts chars then stores in line array*/

sscanf(line, "%d %d", &width, &height); *sscanf reads/converts line int data and stores in &width, &height – note the space between %d%d is not required to store spaced input – is it because the space between inputs is not an int???*/

area = (width * height) / 2;

printf("The area is %d\n", area);

return (0);

}

$ ./tri

Enter width height? 3 4

The area is 6

C allows variables to be initialized in the declaration statement.

int counter = 0; /* number cases counted so far */

Arrays can also be initialized in this manner. The element list must be enclosed in curly braces ({}). For example:

int product_codes[3] = {10, 972, 45}; /* Product numbers for the parts we are making – remember, array of 3 starts at 0,1,2*/

The previous initialization is equivalent to:

product_codes[0] = 10;

product_codes[1] = 972;

product_codes[2] = 45;

The number of elements in {} does not have to match the array size. If too many

numbers are present, a warning will be issued. If an insufficient amount of n umbers

are present, C will initialize the extra elements to 0.

If no dimension is given, C will determine the dimension from the number of

elements in the initialization list. For example, we could have initialized our variable

product_codes with the state ment:

/* Product numbers for the parts we are making */

int product_codes[] = {10, 972, 45};

Initializing multidimensional arrays is similar to initializing single -dimension arrays.

A set of brackets ([ ]) encloses each dimension. The declaration:

int matrix[2][4]; /* a typical matrix */

can be thought of as a declaration of an array of dimension 2 with elements that are

arrays of dimension 4. This array is initialized as follows:

/* a typical matrix */

int matrix[2][4] =

{

{1, 2, 3, 4},

{10, 20, 30, 40}

};

Strings can be initialized in a similar manner. For example, to initialize the variable

name to the string "Sam", we use the statement:

char name[] = {'S', 'a', 'm', '\0'};

C has a special shorthand for initializing strings: Surround the string with double

quotes ("" ) to simplify initialization. The previous example could have been written:

char name[] = "Sam";

The dimension of name is 4, because C allocates a place for the ' \0' character that

ends the string.

The following declaration:

char string[50] = "Sam";

is equivalent to:

char string[50];

.

.

.

strcpy(string,"Sam");

An array of 50 characters is allocated but the length of the string is 3.

The long qualifier informs C that we wish to allocate extra storage for the integer. If we are going to use small numbers and wish to reduce storage, we use the qualifier short. For example:

short int year; /* Year including the 19xx part */

C guarantees that the size of storage for short <= int <= long. In actual practice, short almost always allocates two bytes, long four bytes, and int two or four bytes.

(See Appendix B, for numeric ranges.)

The type short int usually uses 2 bytes, or 16 bits. 15 bits are used normally for the

number and 1 bit for the sign.

This format gives the type a range of -32768 (-2^15 ) to

32767 (2^15 - 1). An unsigned short int uses all 16 bits for the number, giving it the

range of to 65535 (2^16 ). All int declarations default to signed, so that the declaration:

signed long int answer; /* final result */

is the same as:

long int answer; /* final result */

Character variables use 1 byte. They can also be used for numbers in the range of -128 to 127 ( signed char) or to 255 (unsigned char). Unlike integers, they do not default to signed ; the default is compiler dependent. Very short integers may be printed using the integer conversion (%d ).

You cannot read a very short integer directly. You must read the number into an integer and then use an assignment statement. For example:

#include <stdio.h>

signed char ver_short;

char line[100];

int temp;

/* A very short integer */

/* Input buffer */

/* A temporary number */

int main()

{

/* Read a very short integer */

fgets(line, sizeof(line), stdin);

sscanf(line, "%d", &temp);

very_short = temp;

}

%Conversion Uses

%hd (signed) short int

%d (signed) int

%ld (signed) long int

%hu unsigned short int

%u unsigned int

%lu unsigned long int

long int declarations allow the program to explicitly specify extra precision where it

is needed (at the expense of memory).

short int numbers save space but have a more limited range. The most compact integers have type char. They also have the most limited range.

unsigned numbers provide a way of doubling the positive range at the expense of eliminating negative numbers. They are also useful for things that can never be negative, like counters and indices.

#include <stdio.h>

/* Variable for computation results */

short int answer;

int main()

{

answer = 2 + 2;

printf("The answer is %d\n", answer); /* correct answer needed printf definition "answer" */

return (0);

}

$ ./2plus2

The answer is 4

The float type also comes in various flavors. float denotes normal precision

(usually 4 bytes = 32 bits). double indicates double precision (usually 8 bytes = 64 bits).

Double-precision variables give the programmer many times the range and

precision of single-precision (float) variables.

The qualifier long double denotes extended precision. On some systems, this is the same as double; on others, it offers additional precision. All types of floating -point numbers are always signed.

% Conversion Uses Notes

%f float printf only.

%lf double sscanf only.

%Lf long double Not available on all compilers. (gcc OK, obviously 2106!! hehe…)

/*tri.c*/

#include <stdio.h>

char line[100];/* line of input data */

long double height; /* the height of the triangle*/

long double width; /* the width of the triangle */

long double area; /* area of the triangle (computed) */

int main()

{

printf("Enter integers for: width height ");

fgets(line, sizeof(line), stdin);

sscanf(line, "%Lf%Lf", &width, &height);

area = (width * height) / 2;

printf("The area is %Lf\n", area);

return (0);

}

./triang2

Enter integers for: width height 12345678910 12345678910

The area is 76207893874409394048.000000

Double -precision instructions gain accuracy at the expense of time and storage. In most cases, float is adequate; however, if accuracy is a problem, switch to double.

const float PI = 3.1415927; /* The classic circle constant */

By convention, variable names use only lowercase and constant names use only uppercase. However, the language does not require this case structure.

If we tried to reset the value of PI to 3.0, we would generate an error message:

PI = 3.0; /* Illegal */

Integer numbers are specified as a string of digits, such as 1234, 88, -123, etc.

These strings are decimal (base 10) numbers: 174 or 17410. Computers deal with

binary (base 2) numbers: 10101110. The octal (base 8) system easily converts to

and from binary. Each group of three digits (2 3 = 8) can be transformed into a single

octal digit. Thus, 10101110 can be written as 10 101 110 and changed to the octal

256. Hexadecimal (base 16) numbers have a similar conversion; only 4 bits are

used at a time.

The C language has conventions for representing octal and hexadecimal values.

Leading zeros are used to signal an octal constant. For example, 0123 is 123 (octal)

or 83 (decimal). Starting a number with "0x" indicates a hexadecimal (base 16)

constant. So, 0x15 is 21 (decimal). Table 5 -4 shows several numbers in all three

bases.

Frequently, the programmer wants to increment (increase by 1) a variable. Using a normal assignment statement, this operation would look like:

total_entries = total_entries + 1;

C provides us with a shorthand for performing this common task. The ++ operator is used for incrementing:

++total_entries;

A similar operator, --, can be used for decrementing (decreasing by 1) a variable:

--number_left;

/* is the same as */

number_left = number_left - 1;

But suppose that we want to add 2 instead of 1. Then we can use the following notation:

total_entries += 2;

This notation is equivalent to:

total_entries = total_entries + 2;

Operator Shorthand Equivalent Statement

+= x += 2; x = x + 2;

-= x -= 2; x = x – 2;

*= x *= 2; x = x * 2;

/= x /= 2; x = x / 2;

%= x %= 2; x = x % 2;

Unfortunately, C allows the programmer to use side effects. A side effect is an operation that is performed in addition to the main operation executed by the statement. For example, the following is legal C code:

size = 5;

result = ++size;

The first statement assigns to size the value of 5. The second statement assigns to

result the value of size (main operation) and increments size (side effect).

But in what order are these processes performed? There are four possible answers.

1. result is assigned the value of size (5), and then size is incremented.

result is 5 and size is 6.

2. size is incremented, and then result is assigned the value of size (6).

result is 6 and size is 6.

3. The answer is compile r-dependent and varies from computer to computer.

4. If we don't write code like this, then we don't have to worry about such

questions.

#include <stdio.h>

int main()

{

int size = 5 ;

printf("size is %d\n", size);

int result = ++size ; /*BAD CODE*/

printf("size is %d\n" "result is %d\n", size, result);

return (0);

}

$ ./sidefx

size is 5

size is 6

result is 6

#include <stdio.h>

int main()

{

int size = 5 ;

printf("size is %d\n", size);

++size ; /*BETTER CODE, SAME RESULT – op order is clear*/

int result = size ; /*BETTER CODE, SAME RESULT*/

printf("size is %d\n" "result is %d\n", size, result);

return (0);

}

In order to avoid trouble and keep the program simple, always put ++ and -- on a line by themselves.

5.1: Temp

/*tempconv.c*/

#include <stdio.h>

#include <math.h>

char line[100];/* line of input data */

float F; /* temp in F */

float C; /* temp in C */

float resultF; /* resultF (computed) */

float resultC; /* resultC (computed) */

int main()

{

printf("Enter temp in F: ");

fgets(line, sizeof(line), stdin);

sscanf(line, "%f ", &F );

resultC = ( (F - 32) / (9.0 / 5.0) ) ; /*must have dec point for no zero div*/

printf("The temp in C is %0.2f degC \n ", resultC);

return (0);

}

$ ./tempconv

Enter temp in F: 212

The temp in C is 100.00 degC

5.1 Vsphere

/*vsphere.c*/

#include <stdio.h>

#include <math.h>

char line[100];/* line of input data */

/*float F;*/ /* temp in F */

float r; /* radius sphere */

/*float resultF; */ /* resultF (computed) */

const float PI = 3.142; /* PI made unchangeable value*/

float result; /* volume result (computed) */

int main()

{

printf("Enter sphere radius: ");

fgets(line, sizeof(line), stdin);

sscanf(line, "%f ", &r );

/*resultC = ( (F - 32) / (9.0 / 5.0) ) ;*/ /*must have dec point for no zero div*/

result = 4.0/3.0 * PI * (r*r*r);

printf("The volume is %0.2f \n ", result);

return (0);

}

$ ./vsphere

Enter sphere radius: 1

The volume is 4.19

stevee@AMDA8 ~/hello $ ./vsphere

Enter sphere radius: 2

The volume is 33.51

stevee@AMDA8 ~/hello $ ./vsphere

Enter sphere radius: 3

The volume is 113.11

stevee@AMDA8 ~/hello $ ./vsphere

Enter sphere radius: 4

The volume is 268.12

Exercise 5-3 : Write a program that prints the perimeter of a rectangle given its

height and width. perimeter = 2 * (width + height)

Exercise 5 -4 : Write a program that converts kilometers per hour to miles per hour.

miles = (kilometer · 0.6213712)

Exercise 5-5 : Write a program that takes hours and minutes as input, and then

outputs the total number of minutes. (1 hour 30 minutes = 90 minutes).

Exercise 5 -6 : Write a program that takes an integer as the number of minutes, and

outputs the total hours and minutes (90 minutes = 1 hour 30 minutes).

Comments are closed.

Post Navigation