How are pointer addresses assigned in malloc

C programming: pointer

Up to now, a variable was always addressed directly by name. For example, to add two numbers, the value was assigned to a variable name:

A variable is always addressed internally in the computer via an address (unless the variable is already in a processor register). All memory cells within the main memory are given a unique address. Whenever the processor reads or writes a value from the RAM, it sends this to the main memory via the system bus.

A variable can also be addressed indirectly in C via the address. The (start) address of a variable or more generally - a memory area - is supplied by the & operator (also known as the address operator). You already know this address operator from the statement:

scanf ("% i", & a);

An instruction can be used to find out where this variable was stored:

printf ("% p \ n", (void *) & a);

The value can differ depending on the operating system, platform, and even from call to call. The placeholder stands for the word pointer (English: pointer).

A pointer variable is used to address an object (e.g. a variable) via its address. A pointer variable behaves exactly like a "normal" variable, but its value is interpreted as an address. As a result, each pointer variable also has an address.

Example [edit]

The pointer variable a is defined in the following program:

#include intmain () {int * a, b; b = 17; a = & b; printf ("Contents of the variable b:% i \ n", b); printf ("Contents of the memory of the address to which a points:% i \ n ", * a); printf (" Address of the variable b:% p \ n ", (void *) & b); printf (" Address to which the pointer variable a refers:% p \ n ", (void *) a); / * But * / printf (" Address of the pointer variable a:% p \ n ", & a); return0;}
Fig. 1 - The (simplified) scheme shows how the sample program works. The pointer points to the variable. The memory location of the pointer only has the address of (in the example 1462). Note: The addresses for the memory cells are invented and are only used for better illustration.

In line 5 the pointer variable is defined and a variable b of type int.

After the definition, the pointer variable has an undefined content. The instruction on line 8 therefore assigns a new value, namely the address of. The variable now points to the variable. The statement prints the value of the variable pointed to by the pointer. Since it was assigned the address of, the number 17 is displayed.

Whether you are accessing the content of the address referenced by the pointer variable or the address referenced by the pointer variable depends on the operator (the content or dereferencing operator): accesses the content of the pointer variable. But if you want to have the address of the pointer variable itself, you have to choose the operator, that is.

A pointer may only refer to a variable that has the same or a compatible data type. A pointer of the type cannot refer to a variable with the type. You will get to know the reason for this in the next chapter. Just so much in advance: The variable type has nothing to do with the width of the address. Depending on the system, this is always the same. With a 16 bit CPU the address is 2 bytes, with a 32 bit CPU 4 bytes and with a 64 bit CPU 8 bytes - regardless of whether the pointer variable was declared as char, int, float or double.

Pointer arithmetic [edit]

It is possible to increase the pointer and thus to address another memory area, e.g. B .:

#include intmain () {intx = 5; int * i = & x; printf ("Memory address% p contains% i \ n", (void *) i, * i); i ++; // next address read, equivalent to: i = (void *) i + sizeof (* i); printf ("Memory address% p contains% i \ n", (void *) i, * i); return0;}

increases here Not the content, but those address of the pointer. The output also makes it easy to see how big an int is on the system on which the program was compiled. The following is a 32-bit system (difference between the two memory addresses 4 bytes = 32 bits):

Memory address 134524936 contains 5 Memory address 134524940 contains 0

Now to increase the value in the memory, not the pointer, will be of no use. This is because the dereferencing operator has lower priority than the post-increment (). To achieve the intended effect, one writes, or else. When in doubt and also to increase readability and maintainability, brackets are a good choice.

Pointer to functions [edit]

Pointers can refer not only to variables, but also to functions, since functions are nothing more than code in memory. A pointer to a function therefore receives the address of the code.

The following expression defines a pointer to a function:

This notation seems a bit unusual at first. On closer inspection, however, there is only one difference between a normal function definition and the pointer notation: The pointer appears instead of the name of the function. The variable type is the return type and the parameter passed to the function. The bracket around the pointer must not be removed because the bracket operator has a higher priority than the dereferencing operator.

As with a pointer variable, a pointer to a function can only hold one address. So we still have to assign an address to the pointer:

int (* f) (float); intfunc (float); f = func;

This notation is the same because the address of the function is in the function name. For the sake of readability, you should generally not do without the address operator.

We can now call the function using the pointer as usual:

or

Here is a complete sample program:

#include intzfunc () {printf ("zfunc executed! \ n"); return0;} intmain () {int (* f) (); f = & zfunc; printf ("Call f, the pointer zfunc, on: \ n "); f (); return0;}

void pointer [edit]

The pointer is compatible with every other data pointer (attention, different than in C ++), i.e. in C programs no Cast in the target pointer type is required, in C ++ it is mandatory. One speaks here of an untyped or generic pointer. This goes so far that you can convert a void pointer to any other pointer and back without the pointer's representation losing any properties. Such a pointer is used, for example, with the library function malloc. This function is used to provide a certain amount of memory; the start address of the allocated area is returned. Then the programmer can write and read data of any type there. Therefore pointer typing is irrelevant. The prototype of malloc is the following:

void * malloc (size_tsize); int * ptrint = malloc (100); / * the cast in "ptrint = (int *) malloc (100);" is NOT necessary * /

The return type is necessary here because it is not known which pointer type (etc.) should be returned.

The only difference to a typed ("normal") pointer is that the pointer arithmetic is difficult to master because the compiler does not know the memory consumption per variable (we will come to this in the next chapter) and in this case you has to take care of the fact that the pointer comes to rest at the correct address. For example with the help of the operator.

int * intP; void * voidP; voidP = intP; / * both now point to the same element * / intP ++; / * now points to the next element * / voidP + = sizeof (int); / * error! non-standard, void * pointers do not allow arithmetic * /

Call by Value [edit]

A function is used to perform a specific task. Variables can be passed to it for this purpose or it can return a value. The compiler does not pass this variable directly to the function, but makes a copy of it. This way of passing variables is called Call by Value designated.

Since only a copy is made, the transferred values ​​are only valid within the function itself. As soon as the function is exited, all these values ​​are lost. The following example illustrates this:

#include voidfunc (intvalue) {value + = 5; printf ("% i \ n", value);} intmain () {intvalue = 10; printf ("% i \ n", value); func (number); printf ("% i \ n", value); return0;}

After compilation, the program produces the following output on the screen:

10 15 10

This is because the function only receives a copy of the variable. The function then adds the number 5 to this copy. After exiting the function, the content of the variable is lost. The last statement in therefore outputs the number 10 again.

One solution has already been addressed in the functions chapter: The return via the instruction. However, this has the disadvantage that only one value can be returned at a time.

A good example of this is the function. Its purpose is to swap two variables. The function should look something like this:

voidswap (intx, inty) {inttmp; tmp = x; x = y; y = tmp;}

The function is basically correct, but cannot return the result to the main function, since it only works with copies of the variables and.

The problem can be solved by not passing the variable directly, but - as you probably guessed it - a pointer to the variable is passed to the function. The correct program then looks like this:

#include voidswap (int * x, int * y) {inttmp; tmp = * x; * x = * y; * y = tmp;} intmain () {intx = 2, y = 5; printf ("Variable x:% i, Variable y:% i \ n", x, y); swap (& x, & y); printf ("Variable x:% i, Variable y:% i \ n", x, y ); return0;}

In this case the result is correct:

Variable x: 2, variable y: 5, variable x: 5, variable y: 2

The program is now correct because the function does not work with the copies of the variables x and y, but with the originals. In many books, such a call is also called Call By Reference designated. However, this designation is not without its problems. In fact, there is also one here Call by Value before, but not the value of the variable but its address is transferred. C ++ and some other languages ​​also support a real one Call By Reference, C however not.

Use [edit]

The question you may be asking yourself is what use is made of pointers. It seems that apart from calling a function with Call by Reference, we have so far got along quite well without pointers. Other programming languages ​​even seem to be able to do without pointers entirely. But this is a fallacy: often pointers are only well hidden so that it is not immediately apparent that they are being used. For example, in the case of strings, the computer works internally with pointers, as we shall see later. It is also not possible to copy, search or change data fields without a pointer. With type-safe programming languages, there are usually no pointers that can be used at will.

There are areas of application that cannot do without pointers at all: An example of this are data structures such as linked lists, which we will get to know briefly later. In the case of linked lists, the data is stored in a so-called node. These nodes are connected to each other with pointers. This has the advantage that the number of nodes and thus the number of elements to be saved can grow dynamically. If a new element is to be added to the list, a new node is simply created and connected to the rest of the linked list with a pointer. It would be possible to implement a pointerless variant for linked lists, but this would lose a lot of flexibility. Even with many other data structures and algorithms, you cannot do without pointers. Some algorithms can also be implemented more efficiently using pointers, so that their execution time is faster than implementing the same algorithm without a pointer.

Errors can occur with pointers, for example:

#include intmain () {intstart_value = 0; int * pointa = & start_value; for (inti = 0; i <5; ++ i) {printf ("pointa points to% p with the value% d \ n ", pointa, * pointa); intnew_value = 1; new_value + = * pointa; pointa = & new_value;} return0;}

In this program, a pointer points to a variable that is out of range. The value to which the pointer points is then not defined. For example, if the program was compiled with gcc without optimization, the output looks like this:

pointa points to 0x7fffdfe01d44 with the value 0 pointa points to 0x7fffdfe01d48 with the value 1 pointa points to 0x7fffdfe01d48 with the value 2 pointa points to 0x7fffdfe01d48 with the value 2 pointa points to 0x7fffdfe01d48 with the value 2

The address is different when the program is executed again.
If the program has been compiled for debugging optimization (-Og parameter), the value is 1 instead of 2.
If the program was compiled with a different optimization, the value (apart from the start value) is also different when the program is executed again, for example:

pointa points to 0x7ffd880e3c60 with the value 0 pointa points to 0x7ffd880e3c64 with the value 21848 pointa points to 0x7ffd880e3c64 with the value 21848 pointa points to 0x7ffd880e3c64 with the value 21848 pointa points to 0x7ffd880e3c64 with the value 2184848