The point about pointers
Introduction
Until now all examples were done with variables, but for the upcoming next talks we need also something different and that is to talk and use pointers. So what's a pointer in comparison towards a variable? Reduced to one simple first definition: You don't save concrete values within a pointer, you save addresses within memory and point onto them as there is the content saved.
Before getting to the definition of pointers and more into the notation and arithmetic, it is better to understand what happens when we write the following code:
And that is leading towards the question: What exactly are pointers?
A block of memory is reserved by the compiler to hold the value for a variable. The name of this block is digit and the value stored in this block is 42. Now, to remember the block, it is assigned with an address or a location number.
The value of the location number is not important as it is a random value. But, it is possible to access this address using the ampersand or address of operator. The conclusion therefore: A variable has an address. A pointer can be assigned to this address, even changing it within the code! Just how to get the address of the variable defined above? Here is the example to get this more clear for the start:
#include <stdio.h>
int main() {
int digit = 42;
/* the memory-address where the variable digit is saved */
printf("The memory-address of digit = %d.\n", &digit);
/* getting the content itself again throughout pointer */
printf("The value of digit = %d.\n", *(&digit));
return 0;
}
How to define a pointer?
A pointer can be defined several ways:
int* pointer1; /* can save an address, where a value with type Integer is saved */
int *pointer2; /* you can use space before or after asterisk */
int * pointer3; /* also possible as notation, depending on the code-style defined */
int *pointer4, *pointer5; /* definition of two pointers */
int *pointer6, number; /* definition of one pointer and a variable with type Integer */
A defined pointer that has not yet been initialized shows a random address. When accessing this address, there may be a program crash or overwriting of the value stored at this address. Basic ruleset: Pointer should be initialized. When you want to check if a pointer is not initialized, you can use the keyword NULL within a comparison (look therefore at the paragraph Nullpointer). Followup examples for allocations and operators:
Allocation
int variable = 0;
int array[10];
int *pointer;
int *pointer2;
pointer = &variable; /* with address-operator */
pointer = array; /* with array, done without ampersand as not needed and handled internally from C */
pointer = pointer2; /* with another pointer */
pointer = NULL; /* with NULL (predefined keyword in syntax) */
Address-operator
int variable = 0; /* definition of a variable with type Integer, including initialization */
int *pointer = &variable; /* definition of a variable for a pointer with type Integer, including initialization */
printf("%p", (void*)&variable); /* output of the adress for the variable, depends on implementation for example this would result in hexadecimal output */
Content-operator
int variable = 3;
int *pointer = &variable; /* this is NOT the content-operator */
printf("%d", *&variable); /* output value "3" */
printf("%d", *pointer); /* output, value "3" */
* pointer = 5;
printf("%d", *pointer); /* output, value "5" */
* pointer = *pointer + 1;
printf("%d", *pointer); /* output, value "6" */
Nullpointer
If a pointer should not show up to no object, you can assign the value NULL. The pointer is invalid with it and can not be assigned until this is first done correctly, a dereference and usage of such pointer-definition usually leads to a runtime-error along with program abort. On the other hand the comparison of (data)object-pointers with NULL, which is also the main application for the macro (more about that later).
int *pointer;
pointer = NULL;
* pointer = 0; /* error */
pointer = malloc( sizeof(*pointer) );
if ( pointer == NULL )
puts("error while reserving memory");
NULL is a macro and is defined in multiple header-files (at least in STDDEF.h). The definition is specified by the standard implementation-dependent and implemented by the compiler manufacturer, for example:
#define NULL 0
#define NULL 0L
#define NULL (void *) 0
Pointer arithmetic
Using type char for example:
char character;
char *pointer = &character;
printf("%p\n", pointer); /* output is for example the address 0019FF01 */
pointer = pointer + 1;
printf("%p\n", pointer); /* output would be therefore 0019FF02 */
Another example, this time with Integer:
int number;
int *pointer = &number;
printf("%p\n", pointer); /* output would be some memory-address, for example 0019FF01 */
pointer = pointer + 2;
printf("%p\n", pointer); /* corresponding with the mentioned above: output would therefore here 0019FF09, meaning 0019FF01 plus 4 multiplied with 2 */
Addition and subtraction can be written as common as in C generally shortened here.
pointer += 5; /* pointer = pointer + 5 */
pointer++; /* pointer = pointer + 1 */
Always remember: A pointer is for the allocation and addressing of memory. All variable-types have therefore also a concrete, predefined size within memory to be reserved. So another example based upon that:
char *string = "hello";
char *pointer = &string;
char *pointer2 = pointer + 2;
ptrdiff_t difference;
difference = pointer2 - pointer;
printf("%d\n", difference); /* result is here the difference in bytes, here "2" */
Management of dynamic memory
Dynamic memory in C as used for opposing automatic or static memory use in situations in which the required size is only known at runtime or the desired size exceeds the limits of automatic / static memory in the process. To request dynamic memory for the program during runtime the function malloc() is used in C (or the similar functions of calloc, realloc). The function reserves the required memory and returns a pointer to this. Likewise the release of this memory must be manually done with the function free() after use.
int *pointer = malloc(sizeof(int)); /* pointer at the start of a reserved memory-area at runtime, memory for one value of type Integer */
... usage of pointer ...
pointer[0] = 1;
printf("%d", pointer[0]);
* pointer = 2;
printf("%d", *pointer);
free(pointer); /* get memory free */
... after free() the usage of the pointer will be no longer stable and foreseeable, undefined results incoming when doing that ...
Usage of pointer as function-parameter
void function(int *pointer) {
*pointer = 1; /* the value for concurrent memory is overwritten with "1" */
}
int main() {
int number = 0;
function(&number); /* using variable as parameter */
printf("%d\n", number); /* output will be "1" */
}
Conclusion
Pointers are there to help with especially dynamic constructions. But as much freedom C is giving here as much responsibility you have also: Not handling pointer-declarations correctly can result in far more big problems. Those are known in common as memoryleaks. You should check everytime: Is everything cleared at runtime correctly when you leave the point of development going forward towards development-tests?
Human being in favor with clear principles and so also for freedom in soft- and hardware!
Certainly anyone who has the power to make you believe absurdities has the power to make you commit injustices: For a life of every being full with peace and kindness, including diversity and freedom. Capitalism is destroying our minds, the planet itself and the universe in the end!