06/10

An Introduction to C

Part XI, 20150610

Some time has passed since I last did anything with my C tutorial. This is largely due to work and having several different projects going at any one time (learning Chinese, developing a hobby OS, a gaming community, etc...). However, I felt that I ought to cover pointers in a bit more detail. Quite a few people have asked me why one would ever use a pointer, so here's my attempt to explain that.

DEFINITION

A pointer is a variable that contains the address of some other variable.

EXAMPLE

Let's create a variable and print it:

int foo = 0; printf("%d",foo);

Not too complicated at all really. Yet, we can refer to this variable using the address of the variable in memory. This will simply be another integer variable.

int *bar;

To set this to the address of foo, we would do something like:

bar = &foo;

The & infront of a variable means "the address of" and any time you see an asterisk in front of a variable is means "the stuff at the address of".

So, if want to copy foo using bar we can do:

int foo2 = *bar;

If we want to copy the pointer we can do:

int bar2 = bar;

We can also create a pointer to a pointer:

int *barbar = &bar;

See, that's just it. Showing someone the above tells him/her none of what he/she really needs to know to make effective use of pointers.

ARRAYS

C doesn't actually have array variables. C does have syntax for dealing with arrays, but that syntax is actually just a special pointer syntax.

int randomArray[10];
randomArray[3] = 25;

So, above we create a 10 unit array (0-9). We then assign the number 25 to the fourth array segment. This is really just a pointer assignment, as that line is identical in function to:

*(randomArray+3) = 25;

That is, it's assigning 25 to the location three places right of randomarry[0] which is the pointer.

Perhaps more importantly for arrays, pointers allow us to create arrays of arbitrary lengths:

int *randomArray;
int raLength=20;
randomArray=(int*)malloc(raLength*sizeof(int));

This will create an integer array the length of "raLength". Note that because this memory was manually allocated with malloc(), we will need to release it with free(randomArray) when the program has completed its usage of this memory space.

We can use the array feature to create strings as well. These are just character arrays that are terminated with a null character.

char *randomString;
randomString="Random Text";

Or, treating this like an array of characters:

char randomString[12]={'R','a','n','d','o','m',' ','T','e','x','t','\0'};

So, Chris McClelland pointed out on G+ that it's kind of dishonest of me to imply that these are the same. They both do create the requested string, however:

char *randomString = "Random Text";

This creates a pointer to the string literal.

char randomString[] = "Random Text";

Allocates space on stack (as opposed to heap which the malloc example below will do). So, as noted the first should be char *const randomString. The const means that randomString is not to be modified, yet what it points to can be; the pointer is constant- the data to which it points is not necessarily.

const char *randomString;

This means that randomString is now a normal pointer, but the thing at which it points must not be modified.

Back to arrays and such. We could do this manually:

char *randomString;
randomString=(char*)malloc(12*sizeof(char));
randomString[0]='R';
randomString[1]='a';
randomString[2]='n';
randomString[3]='d';
randomString[4]='o';
randomString[5]='m';
randomString[6]=' ';
randomString[7]='T';
randomString[8]='e';
randomString[9]='x';
randomString[10]='t';
randomString[11]='\0';

There are some down sides to working with strings in this manner. A string cannot contain a null value, as this would signify the end of the string. You're not going to have a good time with UTF8 or UTF16. The concatenation or copying of strings will require the writing of routines that work with each character individually. Remember the length! If you do not terminate the string you can get buffer overruns!

WRITABLE FUNCTION PARAMETERS

When a method/function/subroutine is called in C, a copy of its parameters are passed to the function. That is, the variable passed will be local in scope by default.

As one might guess, if you pass a pointer then the pointer gets copied. But, that's fine because you can still access the value at the address:

int randomInt=1;
randomSubroutine(&randomInt);
void randomSubroutine(int *randomParameter) {
  *randomParameter=*randomParameter+1;
}

That will increase the value of randomInt by 1.

STRUCTS

Up to this point, I haven't really introduced structures before. Structures (structs) are C's really crude implementation of objects. A struct is a collection of variables. So, you could create a struct foobar that consists of an array and an integer.

struct randomDef {
  char *randomString;
  int randomInt;
}; // this defines the type
struct randomDef randomStruct; // this instantiates the definition as randomStruct

This will be familiar to people who use object oriented C-like languages. We access the array and integer as follows:

randomStruct.randomString = "Random Text";
randomStruct.randomInt = 123;

As one might expect, there is a special syntax for this. It may be familiar to people who have used objects in other languages.

struct randomDef *randomPointer;
randomPointer=&randomStruct;

You can now refer to the piece of the structure as follows:

randomPointer->randomString="Random Text";
randomPointer->randomInt="123";

The above is functionally identical to:

(*randomPointer).randomString="Random Text";
(*randomPointer).randomInt="123";

For this example, the pointer to the struct isn't all that useful. However, if the structure were very large the use of the pointer would prevent consuming more memory just to pass the structure to a subroutine. The use of the pointer also cleans up the syntax a little bit should the structure itself contain pointers.

CONCLUSION

I realize that this was all very hasty. Obviously, this may require re-reading, but I hope that you now have a better idea about what pointers are, when they are used without your having to declare them, and when you should explicitly use them. In general, pointers are used to provide features in C that were not necessarily written into C. Why were the features not written in? C commands are directly translated to ASM for a given platform. We can assume that part of the reason was programmer time required for the creation of C. Another reason is to allow more flexibility to the programmer. This is evident when you look at languages implemented in C, and just how different those languages can be (Python, PHP, Ruby).