What happens after incrementing a NULL pointer

Pointers are very special variables. Its content is actually irrelevant. What is much more interesting is that they can point to other variables. You are not looking at the pointer variable itself, but rather using the pointer variable to look at the content of another variable. This indirection is not easy to understand at the beginning, and it is certainly cause for confusion later on.

The modern parking garage

Imagine driving your car to work every day and parking in a state-of-the-art parking garage. To park, drive the car into an elevator, get out, and press a button. The elevator door then closes and an automatic system transports your car to a free parking space. As soon as that happens, you will be given a magnetic card. When you pick up your car in the evening, insert the magnetic card and after a while the elevator opens and your car is inside. You can hop on and drive home.

Magnetic card as a pointer

The magnetic card corresponds to a pointer variable. You can use the magnetic card to access your car, which is parked somewhere in the large parking garage. You do not know where the car is and what is really on the map. You don't really care if you get your car back later. On the other hand, if you lose the card, it's almost like losing your car. It occupies the parking lot and is still physically present. But you can't get to the car.

Info terminal

Because there are people who are less open to technical innovations than you are, the operator has set up an information terminal. There you can insert the magnetic card and receive the data that was obtained about your car when parking. So the car was weighed in the elevator so that someone would not overtax the statics with a tank. Height, width and length were determined by light barriers so that the parking bays can be optimally used. And finally the license plate was scanned in. If you insert the magnetic card into the terminal, you will see this information about your car: weight, height, length, width and license plate number. These data are not the data on the map, but belong to the car to which the map is pointing. In addition, the car park operator has set up a new service. The owners who lost their card can get their car back by typing in the license plate.

Variables and pointers

Your variables behave like the cars. They are located in the main memory, which is comparable to the parking garage. You usually don't care where your variables actually are in memory. They usually address you via the variable name, just as you can reach the car via the license plate. But you can also define a pointer variable that corresponds to a magnetic card. If you assign the address of a variable to the pointer variable, you can access this variable via the pointer variable. This is the same process as when writing on the magnetic card. The car park computer will encode where the car is parked on the magnetic card. You are just as uninterested in what is actually written on the magnetic card as it is in the content of the pointer variables. Instead of looking at its direct content, use it to look at the variable it refers to. If you lose the content of the pointer variable, the variable content remains inaccessible unless you still have the variable name.

Pointer variable

A pointer is a variable whose content contains the position of another variable in main memory. A pointer variable is used to refer to and access memory contents indirectly.

definition

To define a pointer variable, the type that the pointer can access is first specified. It is followed by an asterisk and then the name of the variable. char * charZeiger;

The variable is therefore a pointer to a variable of the type. In other words, the position of the memory location in which a variable is located can be stored in the variable.

address

A memory location is called an address. The memory locations in the computer are numbered so that there is simply a number behind the address. To determine the address of a variable, it is preceded by an ampersand. This character is called an ampersand in English and is also usually called that in programming circles. The following example shows how the address of the variable is assigned to the pointer. char letter = 'A'; charPeiger = & letter;

Accessed by the pointer

As I said, we are actually not interested in the content of the pointer variable, but rather in the content of the variable to which the pointer points. After the pointer variable has been filled with the address of the variable, you can access it by prefixing the pointer variable with an asterisk: cout << * charPeiger; With this instruction, the 'A' in the variable would be output on the screen. You can even smuggle new content into the variable using the pointer variable: * charZeiger = 'B'; cout << letter;

The asterisk in front of the pointer variable makes it clear that we are not accessing the content of the pointer variable, but rather the memory location to which the pointer points. Since it still contains the address of the variable, its content is now changed. When the variable is output, a 'B' will now appear on the screen.


Illustration

The illustration shows the situation after assigning the 'B' via the pointer. The variable is at the top right. It is simply assumed here that it was created in memory location 17543. The pointer variable contains exactly this number, which in turn is located in the memory at position 23164. The size ratio is also quite appropriate. While a variable of the type usually occupies one byte, a pointer variable on a normal PC requires four bytes.

What for?

You can surely imagine that pointers allow very flexible access to the variables. In fact, pointers are used quite often. Here are some typical uses for pointers that you will find later in the book:
  • You can write a piece of program that accesses a variable via a pointer. As soon as you "bend" the pointer to the address of another variable, the same part of the program works with another variable. You will get to know this in connection with function parameters.
  • You can request new memory during the program. As you will see later, this is what the command is used for. So that the program can access the new memory, it supplies a pointer to it.
  • You can use pointers to model complex data constructs. To do this, you create variable groups from data and pointers. If the pointer of one group of variables points to the next, chains can be formed from them, which are known as linear lists. If you add several pointers to the data network, you can create a tree-like structure.

Self protection

It is a good idea to set a pointer to 0 that has not yet been assigned a specific target address. There can definitely be no variable in memory location 0 and for this reason accidental access via a null pointer would always lead to an immediate crash. It is better to end with horror than to end with horror. The endless horror occurs when the pointer points to a random value and the program inadvertently continues to work with it without noticing the error. char * charZeiger; // define charZeiger charZeiger = 0; // save as null pointer

If the variable is to be initialized with 0, you can simply append it as with an integer variable.

In C programs you will often find a constant called NULL in this context. This is a 0 that is explicitly declared for use with pointers.

In C ++, NULL is neither required nor useful. However, there was also a desire for a special zero for pointers. That is why C ++ 11 introduced a special 0 for pointers.

char * charPointer = 0;

Indirect access

In order to be able to access the content of another variable via a pointer variable, the variable name is preceded by an asterisk. As a result, the pointer variable itself is not accessed, but the memory location whose address contains the pointer variable. The commands shown in the previous section have been put together again for repetition: char * charZeiger; // Definition of the pointer variable charZeiger = 0; // Save as a null pointer char letter = 'A'; // Variable should be the target later charPeiger = & letter; // assign address of letter * charZeiger = 'B'; // letter now contains 'B'

Indirection operator

This star is the same symbol that is used in multiplication. However, the compiler recognizes from the context how the asterisk is to be interpreted. When accessed via a pointer variable, it is at the position where you would otherwise find a sign. Since a sign can only be a plus or a minus, the compiler recognizes the asterisk at this point as an operator for indirect access. The star is therefore also called the indirection operator. The following gadgets are designed to give you a sense of what you can do with pointers. The comments describe what each statement does. int main () {int * intPointer = 0; int intVar = 5; intZeiger = & intVar; // the pointer gets the address of intVar // then intZeiger points to intVar * intZeiger = 1; // the variable pointed to by intPointer is assigned // the value 1. This means that the // content of intVar is now 1. intVar = * intZeiger + 1; // intVar is calculated from 1 and the value to which // intPointer points. But that is intVar itself. // That is why intVar is then 2.}

In the example, the pointer variable was already set to 0 when it was defined, as recommended. If the pointer variable is local, it can contain any value without initialization. This means that it points to a random position in main memory. If this pointer is accidentally accessed before it has been correctly assigned, the program will access the memory somewhere. In this way, nonsensical data is determined or even data is changed without an error occurring immediately. The program continues to run with defective data and will only deliver an error much later, the origin of which is then difficult to identify. If, on the other hand, the pointer was set to 0, the first access via it will cause the program to crash. With the help of a debugger, you can then easily determine where this crash occurs. You can also immediately see that the pointer is at 0 and you will surely quickly recognize where you have forgotten to set the pointer correctly.

Arrays and pointers

relationship

In C and C ++, arrays and pointers are miraculously related. You can assign an array directly to a pointer variable. The result is that the pointer points to the first element of the array. int numbers [4]; int * number pointer = 0; Numberpointer = numbers; It is particularly interesting that you can also give a pointer variable the square array brackets. Lo and behold: the pointer variable behaves as if it had been born as an array. In the following example the pointer variable is used as an array variable: numberpointer = numbers; Numberpointer [3] = 4;

This situation is shown in the figure (abbarrayptr). It is not without reason that the array tag is also shown as a pointer.


Figure (abbarrayptr).

The following constructions may make you sweat on your forehead; It won't even seem strange to your compiler. The initial situation initially seems similar:

int numbers [4]; int * number pointer = 0; Numberpointer = numbers; Numberpointer [3] = 5; Numberpointer = & numbers [2]; Numberpointer [1] = 3; // ends up in numbers [3]!

The line in which the pointer is set to the address of the third array element is particularly original:

Numberpointer = & numbers [2]; Since it is nothing more than an ordinary integer variable, the pointer can of course also be set to its address. When you use this pointer in square brackets, it behaves like the array numbers, but shifted two elements to the right. You can see the situation in the picture below.


Figure (abbarrayptr2).

The shift becomes identical to. The result is that is the last element of the array. So if the element were processed, a memory area would be accessed that is outside the reserved space. The results are unpredictable.

The key difference: When an array is defined, the memory is reserved for the array elements. When defining a pointer, only memory is created for the pointer itself.

Pointer arithmetic

You can increment and decrement pointers. This advances the pointer by as many bytes as the size of the type it points to. So if you have a pointer that points to the beginning of an array, incrementing it will move it to the next position in the array. This is why a pointer is often used to traverse an array.

example

If you want to set an array to 0, you can do this using an index operator: int value [MAX]; for (i = 0; i Alternatively, you can use a pointer and run it through the array:

int value [MAX]; int * run = value; for (i = 0; i Efficiency Of the two loops, the second is faster. That makes sense very quickly, because when using the pointer it has to be increased once in each run. To do this, the processor adds the size of the type to the pointer. In the first loop, on the other hand, the type size must first be multiplied by the index and then added to the array address in order to get access to the element. Then the index has to be increased, but only by 1.

Figure (figurarith)

for loop

Strings can be processed very elegantly with the loop. In the brackets after the keyword, the start instruction, the condition under which the loop runs, and then the instruction that is carried out at the end of the loop body appear one after another. The following example assumes that the variable source contains a string and prints it letter by letter on the screen. char source [MAX]; for (char * p = source; * p; p ++) {cout << * p; } The pointer variable p is defined in the start statement and with the array variable source initialized. The next statement is the condition under which the loop continues. The brevity is of course irritating. returns the character to which the pointer p is currently pointing. While this is not a Boolean value, remember that C ++ interprets null values ​​as false and all others as true. This means here that the loop will continue until it encounters the terminating zero of the string. The expression only becomes false with the terminating zero of the character string. The final statement finally ensures that after each run through the body of the loop, the variable p advances to the next character.

Addition and subtraction

Pointers cannot just be incremented or decremented. It is also possible to add up numbers. This adding is consistent with the incrementing in that the added value is treated as a unit for the type size. The following expression refers to two elements after the element pointed to by the pointer: * (pointer + 2) = 4; Pointer [2] = 4; Exactly the same thing is done in the second statement as in the first. The second element after the start element is accessed. The bracket on the first line is required because the indirection operator has a higher priority than the plus sign.

Constant pointer

You already know the keyword from the declaration of constants. It can also be used in conjunction with pointers. It can be in two different places and then each has a slightly different meaning. const int * constant target = & target variable;

Constant goal

This pointer is defined in such a way that the keyword comes directly before the type of the target. The target variable must not be changed using this pointer. The pointer itself can certainly be incremented. For example, you can use it to iterate over and print an array. int * const constant pointer = & target variable;

Constant pointer

The pointer constant pointer must not be changed with regard to its aim. The keyword is immediately before the name of the pointer and indicates that it must not move. That is why the pointer must be finally initialized when it is defined. The variable to which it points can, however, be changed as desired. const int * const completeKonstant = & target variable;

Everything constant

At the pointer completely constant Neither the referenced variable nor the pointer itself may be changed. Constant pointers are most often used when describing function parameters.

Anonymous pointers

Pointers are always the same size, regardless of what data they point to. Ultimately, they always contain a memory address, and this is the same for all types. The size depends on the machine architecture. On today's 32-bit systems, for example, a pointer is 32 bits, i.e. 4 bytes. From the point of view of the computer, the type to which a pointer points is completely the same. However, the compiler ensures that a pointer to a variable is not suddenly used to access a variable of the type. In certain situations it can be useful to store pointers whose destination is unknown. Such pointers are defined as pointers to the data type. void * ptr;

Compatible

There is no such thing as a variable of the type. Defining such a variable would therefore lead to an error. However, it is entirely permissible to define a pointer to. You can assign any pointer to a variable defined as a pointer to a pointer without the compiler complaining about it. Otherwise, C ++ refuses to assign pointer variables of different types. Usually a pointer to is used as a transport vehicle for a pointer, the target type of which is only revealed in the course of the program.

one way street

As I said, you can assign each pointer to a pointer. Conversely, if you want to assign a pointer to a pointer, this is only possible if you cast the pointer to the target type. void * voidPtr; int * intPtr; voidPtr = intPtr; // works fine intPtr = (int *) voidPtr; // explicit casting required