Part 5 - Pointing & Functions

Answer to last times problem.

#include <stdio.h>
#include <string.h>

int main(void)
{
 int no1,no2;
 char command[9];
 do
 {
  printf("Please select : \n\n");
  printf("Add\nSubtract\nMultiply\nDivide\n");
  printf("Modulus\nQUIT    ");
  gets(command);
  if (!strcmp(command,"QUIT")) break;
  printf("Please enter your first number : ");
  scanf("%d",&no1);
  printf("Please enter your second number : ");
  scanf("%d",&no2);
  printf("\n");
  if ((!strcmp(command,"Add")) || (!strcmp(command,"add")))
  {
   printf("%d + %d = %d\n",no1,no2,no1+no2);
  }
  if ((!strcmp(command,"Subtract")) || (!strcmp(command,"subtract")))
  {
   printf("%d - %d = %d\n",no1,no2,no1-no2);
  }
  if ((!strcmp(command,"Multiply")) || (!strcmp(command,"multiply")))
  {
   printf("%d * %d = %d\n",no1,no2,no1*no2);
  }
  if ((!strcmp(command,"Divide")) || (!strcmp(command,"divide")))
  {
   no2!=0 ? printf("%d / %d = %d\n",no1,no2,no1/no2) :
            printf("Your second number is a zero\n");
  }
  if ((!strcmp(command,"Modulus")) || (!strcmp(command,"modulus")))
  {
   no2=0 ? printf("Your second number is a zero\n") :
            printf("%d %% %d = %d\n",no1,no2,no1%no2);
  }
 }
 while (strcmp(command,"QUIT"));
 return 0;
}
Functions

A program is made up from a series of functions, this much we already know. What you didn't know is that besides the standard int main(void) function, you can have other functions and function types.

There are lots function types, which follow the different variable types, with one exception, void. We have seen what the int function does, but what is a void function and can arguments be passed over to them?

A void function is normally one which doesn't really do very much - it may just output a line of text to say that the function being used hasn't been completed yet. That's not to say a void function is useless, far from it.

Due to the fact that arguments can be passed over to a void function, it makes them useful. For instance, say I had a program which needed some work performed on a set of variables, I could enter the same piece of code a fair number of times, but as long as the maths is the same each time, a void function can be used to save time in an data processing situation.

Usage

void get_a_line(void); // predefines the function
char line[40]; // global var

int main(void)
{
 get_a_line(); // calls the function
 printf("%s",line);
 return 0;
}

void get_a_line(void)
{
 printf("Enter something : ");
 gets(line);
}

You will notice that the function is also declared before the int main(void) line. This is called the prototype for the function. This declares the function before it is used, the return type, the name and parameter list.

The parameter list does not need to be empty; it can contain any number of arguments, but the variable type has to be maintained between the protocol and the actual function. For instance

PROTOTYPE : void rpc(int mem, char name); FUNCTION : void rpc(int memory, char title)

the variable types have been maintained, but not the variable names - these don't matter (remember in BASIC, if you had lines such as

PROCrpc(a%,a$)
DEFPROCrpc(memory%,title$)

this is fine. The first argument may be called a% in the called, but will be processed as memory% in the proc.

One important thing to remember though, a void function returns no values.

You can have other function types which return other variable types :

  • double fn_name(...) returns a double value
  • char fn_name (...) returns a char

you get the idea!

I will return to functions in a later piece to cover arguments in the int main(...) function and functions across files.

Pointers & pointers to pointers

When defining a variable, a set amount of memory is reservered for each variable type. If this is used in (say) a scanf("%d",&var); line the contents of var are written to this address as well as being stored in var. What a pointer does is point to the start of the address where var is kept. It has to be handled slightly differently though.

Normally, you would have

int var;

for the pointer, you would need

int *var;

the * means that it is a pointer.

If the int *var is declared, no space is allocated to var, it's simply a starting point for an address. The int defines the maximum space (number of bytes) it will occupy.

Where pointers come into their own is when passing over a variable to a function.

The normal practice is to pass a single value or character string. However, while in C the single value is simple enough, the string is held in an array, so it is the entire array which has to be passed over.

In the examples up to now, the biggest array has been 80 characters - this is not going to slow even a A3000 down by much, however, say I had a 3 dimensional array of floating point variables 300 x 300 x 300 - a gross size of 270,00000 (or around 26Mb of numbers). This is not impossible, but the amount of time taken to pass 26Mb of data around a program would be terrible.

The way around this would be to call the function using a pointer.

The array would be set up as

float *array[300][300][300];

and the function called would be

do_something=funct(array);

and the prototype would be

double funct(double *array);

A simple method to understand the pointer is this program (type this in and run it)

#include <stdio.h>

int main(void)
{
 int var1, *pointer;
 var1 = 1024
 pointer = &var1;
 printf ("%d",*pointer);
 return 0;
}

this declares two variables; var1 and pointer. var1 is set to be 1024. pointer is then set to be the start address of var1, but not the contents of that place. The value of the memory location pointed to by pointer is then printed.

This can also be swapped around thus

int main(void)
{
 int var1, *pointer;
 pointer=&var1;
 *pointer=1024
 printf("var1 = %d",var1);
 return 0;
}

here pointer has been set at the address reserved for var1. Next 1024 has been written to this address via the *pointer line (BASIC equivalent, !address). var1 is then written to the screen (again, it will display 1024). It has displayed 1024 as var1's memory location contains 1024 instead of 0.

The pointer to a pointer is denoted by a double asterix (**).

Usage

char **dbl_pointer; This type of pointer points to the memory location of a normal pointer which inturn points to a variable :
char **dbl_point, *point, var;
point = &var;
dbl_point = &point;
**dbl_point='x';

Here, pointer gets the address of var while dbl_point gets the address of pointer. As with the second example, var is then given the character 'x' (note the type of quotes used) though dbl_point -> point -> var.

Alright, this may not seem very useful, but just wait.....

Next on my list for you is arrays and more on the printf command.

Until then, try this one out.

Time for a task

Re-write either version of the simple calculator using multiple functions, however, this time you must add the following functions

  • power, average and sum.

These three should provide you with some entertainment. Remember, a power is simply the number multiplied by itself however many times (a for loop would be helpful here), the average can be performed using 2 variables, and sum is basically the same as average with the exception that it is not divided by the number of entries. If you felt really clever, you could use the same function for both average and sum. At least one of these functions must be a pointer function.

Remember to declare the prototypes before the int main(void) statement.

An interesting point to note though. If you compare the compiled filesizes of the original program (which contained 5 types of processing) and the new version (which contains 8), you will see that there is roughly 500 bytes difference. For this 500 bytes, you've made the program far more expandable and far simpler to debug!