10 Beginner's Lesson 4A: Arrays, Qualifiers, and Reading Numbers

Greetings,

C allows variables to be initialized in the declaration statement.

This can make our program more compact, but please make sure that
the program remains easy to read and understand! We are not only
trying to learn the nuances of the C programming language, but we
are also trying to learn good programming practices while we are
at it. Easy to read and understand code is better than cryptic and
terse source code, especially if the code we are writing has to
be maintained. I have been told that only a small amount of time
is actually spent writing NEW source code -- way more time is spent
MAINTAINING source code that was written by someone else.

Declare an integer variable `counter' and initialize it to zero:

int counter = 0; /* comment */

Arrays can also be initialized when declared:

int codes[3] = {10, 99, 21};

The number of elements in {} does not have to match the array size.
If too many elements are present, a warning will be issued. If there
aren't enough, not all the elements are initialized. If no length
is given for the array, C determines the the length from the number
of elements in the initialization list.

int mem[100] = {0}; /* initialize all elements to zero */

Strings can also be initialized in a similar manner:

char name[] = {'A', 'n', 'd', 'y', '\0'};

C also allows a string to be initialized like this:

char name[] = "Andy";

The length of name[] is five, because C allocates a place for the
NUL ('\0') character that ends the string. The following declaration:

char string[50];

is equivalent to:

char string[50];
...
...
...
strcpy(string, "Andy");

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

The C programming language has a wealth of variable types which
allow the C programmer to get very close to the actual hardware
of the machine. There are type specifiers such as `int' and
also type qualifiers: short, long, signed, and unsigned. Different
machines will store integers differently. The programmer also
needs to use the proper conversion string for printf() and sscanf().

Integer printf/sscanf conversions
=================================
%Conversions | Uses
-------------+-------------------
%h | (signed) short int
%d | (signed) int
%ld | (signed) long int
%H | unsigned short int
%u | unsigned int
%lu | unsigned long int
---------------------------------

Use the `sizeof' operator to find out how much space is allocated for
these on your machine. Here is an example. (Can anyone take this and
modify it to show all the above integer types with qualifiers?)

#include <stdio.h>
int main(void)
{
printf("(signed) short int %h\n", sizeof(signed short int));
return 0;
}

The range of numbers that can be used with these depends on whether
the integer is signed, or unsigned. For example, on some machines,
a `signed int' has the range -32768 (2^15) to 32767 (2^15 -1). On
the same machine, an `unsigned int' has the range of 0 to 65535 (2^16).

All `int' declarations default to 'signed', so the declaration:

signed long int var_name;

is the same as:

long int var_name;

If you need a *very* short integer. It is the type `char'.
Character variables take up 1 byte (8 bits). They can be
used for numbers in the range of -128 to 127 or 0 to 255.
Unlike integers, they do not default to signed; the dafault
is compiler dependent. Questions: What is the default for
`char' on a GNU/Linux i386 PC? How did you find out?

Very short integers may be printed using the integer conversion (%d).
There is no way to read a very short ingteger directly. You must
read the number into an integer and then use an assignment statement.

`long int' types 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. `unsigned' numbers
provide a way of doubling the range at the expense of eliminating
negative mumbers. As a C programmer, YOU are in charge of which
type is used, depending on the machine's storage requirements.

Floating point types come in various flavors as well. `float' is
for normal precision (usually 4 bytes -- 32 bits), and `double'
means double precision (usually 8 bytes -- 64 bits). `double'
gives the programmer twice the range and precision of single-
precision variables (float).

Float printf/sscanf conversions
=================================
%Conversions | Uses
-------------+-------------------
%f | float
%lf | double
%L | long double
---------------------------------

On most machines single-precision, floating-point instructions
execute faster (but less accurately) than double precision.
Double precision gains accuracy at the expense of time and storage.
Remember that integer arithmetic is the fastest of all, but is
least precise with the smallest range.

What does a program using `sizeof' output for the above conversion
specifiers? Anyone? Show us your (hopefully `easy-to-read') source.

I recently posted an ASCII character chart to the list. I hope
everyone got one! Check the list archives if you didn't get one.
In that chart were some decimal, octal, and hexadecimal numbers.

Decimal numbers are base 10 numbers: 0, 1, 2, 3, ..., 9
This is what we use to count with (ten digits/fingers)
Binary (base 2) numbers are what computers use: 0, 1
Octal numbers are base 8 numbers: 0, 1, 2, 3, ..., 7
Each group of three digits (2^3 = 8) can be transformed into a
single octal digit. Thus binary 10101110 can be written as binary
10 101 110 and changed to the octal 256 (base 8).
Hexadecimal (base 16) numbers have a similar conversion, only 4 bits
at a time. 1, 2, 3, ..., 9, A, B, C, D, E, F

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).
The ASCII chart shows some numbers in all three bases.

C also has a large number of special purpose operators.

The increment operator (++) increments the value in a variable by one.
The decrement operator (--) decrements the value in a variable by one.
It can also make a differnce whether the operators are prefixed to the
variable, or postfixed to the variable, for instance, ++x and x++.

Can anyone write an easy-to-read program that would show the difference?
(I'm trying to get YOU actively involved in this "course"... here's
a hint: declare your variable and initialize it, then printf() the
value. increment the variable using prefixed `++', then printf()
the variable again... do the same for postfix and for decrement).
Show us your source! (Use the source Luke, use the source!)
May the source be with you!

Here is a table of some `shorthand' operators you'll probably see.

Operator | Shorthand | Equivalent Statement
---------+------------+-----------------------
++ | ++x or x++ | x = x + 1;
-- | --x or x-- | x = x - 1;
+= | x += 2; | x = x + 2;
-= | x -= 3; | x = x - 3;
*= | x *= 4; | x = x * 4;
/= | x /= 5; | x = x / 5;
%= | x %= 6; | x = x % 6;
--------------------------------------------

Watch out for "side effects"!!! A side effect is an operation
that is performed in addition to the main operation executed by
the statement. C allows the programmer to use side effects.
The reason to watch out for side effects is that it makes your
program more difficult to read and understand. For example,
size and result have been declared as type integer:

size = 5;
result = size++;

The first statement assigns the value 5 to size. The 2nd statement
assigns to result the value of size (main operation) and increments
size (side effect). But in what order? Four possibilties:

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

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

3. The answer is compiler dependent and varies on the computer.

4. We don't write code like this, we don't have to worry about these
sorts of questions. 8^D

The correct answer is number 1; the assignment occurs before the
increment. However, 4 is a much more practical answer. The main
effects of C are confusing enough without having to worry about
side effects.

Here's a mind bender (legal C code): o = --o - o--;

The problem with the above mind bender is that a programmer can't
read it -- they have to decode it.

Well, it looks like we've more or less finished Chapter Four!
Hooray! We now have quite a few tools and materials to create
our own programs with. Try these exercises on for size:

1. Write a program that converts Centigrade to Fahrenheit.
Formula: Fahrenheit equals nine divided by five times Centigrade
plus thirty-two.

2. Write a program to calculate the volume of a sphere.
Formula: four divided by three times radius(cubed).

3. Write a program to print out the perimeter of a rectangle
given its height and width.
Formula: perimeter equals two times (width + height).

4. Write a program that converts miles per hour to kilometers per hour
Formula: miles equals kilometers times 0.6213712

5. Write a program that takes hours and minutes as input and outputs
the total number of minutes (1 hour 30 minutes = 90 minutes).

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)

7. Write an exercise for the other students on the list that they
can solve, using the tools we have learned in Lessons One through
4B. Here is a chance to be creative! If the subject requires
knowing a formula of some kind, please include that with your
exercise.

All mistakes are mine. I have tried to proof-read this as best I can,
but certainly some mistakes and typos have crept in. If you find a
mistake, all of us will be thankful if you point it out to us.

Happy Programming!
--
K