15. The left overs

(1) Variable modifiers

A variable modifier adjusts the variable type.

A const variable (const is short for constant) is one which cannot be changed by your program. For instance if you had a line in a program which read

const int a_number=10;

and somewhere else you had the line

a_number++;

the compiler would spit this out and complain violently!

consts can also be used in function prototypes

void some_fn(const char *p);

this sets the pointer parameter to be such that it cannot be changed in the function, only accessed (it's very similar to making a floppy disc read only!)

A variable type preceeded by volatile tells the compiler that the variable can be altered by the program and also by other means (for instance, the variable may contain the results of a signal sent by a device through the serial port).

volatile is important in this respect. When you compile your C application, the compiler will try and optimise the code in such a way as to make the final application run as quickly as possible. If the volatile variable type modifer (which both volatile and const are really) has not been used, the program may not examine the contents of the variable each time it is accessed - upshot, you get something which would not look out of place in some "off the shelf" PC software.

An example of using volatile would be something like this

volatile unsigned var;
some_interupt_address(&var);
for (;;)
 printf("%d",u);

In this example, if var had not been declared volatile, the compiler would optimise to the printf, so that var did not always get checked. However, it was declared volatile and so is checked whenever it is referenced.

At the time of writing, the compiler I was using did not have a bool as per the C99 standard. The latest versions of both Acorn C and GCC for RISC OS do

BOOL (short for BOOLEAN - a form of mathmatics), is simple. It's either true or false. Most commonly, BOOL is used as a function return type e.g.

BOOL some_function(params)
{
 /* routine code */
}

this would test whatever is inside the braces and return either a

TRUE or FALSE value.

(2) Storage Class Specifiers

There are four of these, one of which is never used : auto ( never used), extern, register and static.

While what we have done so far has been very small scale (well, the tutorial code has been, the tasks may not have been!), if you looked at the source code for something like !Impression (or any large application), you would see that the source would be huge and split into a large number of files. Why should this be though? Surely it would be simpler to have one large file!

Wrong. Try finding a bug in a 30,000 line source file when it's all in one file! Using small files actually helps in developing software - it makes debugging simpler (there is less to plough though for a start), cuts down on compiling time and makes expansion easier.

When using multiple files, it is usual to set up something known as Global Variables - these are variables which are used throughout the application. These are defined once in one file. To access the global variable from another file, the extern keyword is used. Globals are handy in that all functions can access them, however, their use should be restricted as the compiler will not be able to optimise them correctly. Consider this :

FILE 1
#include <stdio.h>

int counter; // global var

void func_1(void);

int main(void)
{
 int loop;
 func_1();
 for (loop=0;loop<counter;loop++)
  printf("%d",loop);
 return 0;
}
FILE 2
#include <stdlib.h>

void func_1(void)
{
 counter=rand();
}

File 1 will compile no problems. File 2 won't compile as counter has not been initialised.

What's the answer? Add the line int counter; after the #include line? Well no. Adding the int counter; will just make the linker complain that you've defined counter twice. This is where you would use extern.

If the second file has the line

extern int counter;

added after the #include statement, the compiler will compile and link the code happily.

REGISTER NOW!

If you define a variable as a register variable, you're telling the compiler that you want this variable to be accessed quicker than a normal variable. The fastest way to do this is to store it in a CPU register (which is what happens with int and char variable types - the other types are held in such as way as to reduce the access time - say at the top of the heap).

Like any method of storage though, there are only a certain number of registers to go around - when they're all used up, they're gone. It is therefore a good idea only to use them for frequently used variables.

An important thing to remember though - as a register variable may be held in a register of the CPU, it may not have a memory address and so the & cannot be used

It is also really up to the compiler as to if the variable should be held in a register, so at best, using register really only is a suggestion!

Finally, we come to the static modifer. These are initialised once, but have the advantage of preserving the contents of the local variable between function calls. The following example show this nicely....

#include <stdio.h>v

void func(void);

int main(void)
{
 int loop;
 for (loop=0;loop<10;loop++)
 func();
 return 0;
}

void func(void)
{
 static int counter=0;
 counter++;
 printf("Counter = %d",counter);
}

this will display

count = 1
count = 2
:
count = 10
count has kept it's value between the function calls.

static can also be used on global variables. When this happens, it causes the variable to be known to and accessed by only the functions in the same file it was declared in. Any other files can't access this variable and so won't know of it's existance - the upshot is that file #1 can have a variable called fred and file #2 can also have a variable called fred, only in the case of #2, fred was declared using the static modifier.

(3) sprintf()

The world has really been spoiled by BASIC. I've not come across a language yet with such a super method of dynamic allocation of space for a string or for string handling in particular.

Take for instance

a$=mid$(var$,start%,len%)

There is nothing like that in C - the nearest you'd get would be something like

strncpy(string_1,string_2,no_of_bytes)

which copies n bytes from string 2 to string 1, but even this doesn't do the same. I've included in the downloads possible ways of doing left$, right$ and mid$, but none are that good!

Face it, what other language can you just say

a$=""
IF MID$(r$,3,LEN b$)="fred" a$=MID$(r$,3,LEN b$) ELSE a$="Billy Bob"

a$ started out life being 0 characters long and could end up being 9 characters long (or longer).

Another nice one in BASIC is a line such as

a$="I am "+STR$ age%+ years old"

but here C has response - sprintf(). This allows you create a variable such as the one above.

Format

sprintf(newname,"%delimiters",delimited_variables_list)

newname has to be defined as a pointer array, capable to of holding the contents of the delimited variable list. %delimiters are the likes of %d %c and %s. The delimited variables list are variables associated with the %delimiters.

For example

char *newname=malloc(40*sizeof(char));
char text[][20]={"The answer is "," and PI = "};
int number1=4,number2=2;
float pi=3.141593;
sprintf(newname,"%s%d%d%s%f",text[0],number1,number2,text[1],pi);
printf("%s",newname);

will output

The answer is 42 and PI = 3.141593

As you can see, sprintf() is a very powerful function.