Understanding of pointers with malloc and free

2019-02-15 05:19发布

问题:

Pointers are a really tricky thing in C. For a lot of people is hard to understand it, so for a good understanding I wrote following code:

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    int *p; // pointer -> will be dynamic allocated
    int *a; // array -> will be dynamic allocated

    // print before allocate memory (1)
    printf("&p: %p\tp: %p\t*p: %d\n", &p, p, *p);
    printf("&a: %p\ta: %p\t*a: %d\n", &a, a, *a);
    printf("\n");

    // allocate memory (2)
    p = (int *)malloc(sizeof(int));
    a = (int *)malloc(sizeof(int) * 10);

    // print after allocate, but before give a value to poinetrs (3)
    printf("&p: %p\tp: %p\t*p: %d\n", &p, p, *p);
    printf("&a: %p\ta: %p\t*a: %d\n", &a, a, *a);
    printf("\n");

    // give a value to poinetrs (4)
    *p = 1;
    for (int i = 0; i < 10; i++) { a[i] = i; }

    // print after we gave a value to pointers (5)
    printf("&p: %p\tp: %p\t*p: %d\n", &p, p, *p);
    printf("&a: %p\ta: %p\t*a: ", &a, a);
    // because a is an array we must use a loop for print 
    for (int i = 0; i < 10; i++) { printf("%d ", a[i]); }
    printf("\n");
    printf("\n");

    // free pointers (6)
    free(p);
    free(a);

    // print pointers after free (7)
    printf("&p: %p\tp: %p\t*p: %d\n", &p, p, *p);
    printf("&a: %p\ta: %p\t*a: ", &a, a);
    // because a is an array we must use a loop for print 
    for (int i = 0; i < 10; i++) { printf("%d ", a[i]); }
    printf("\n");
    printf("\n");

    // try to change values after free (8)
    *p = 12;
    for (int i = 0; i < 10; i++) { a[i] = 3; }

    // print after (8)
    printf("&p: %p\tp: %p\t*p: %d\n", &p, p, *p);
    printf("&a: %p\ta: %p\t*a: ", &a, a);
    // because a is an array we must use a loop for print 
    for (int i = 0; i < 10; i++) { printf("%d ", a[i]); }
    printf("\n");
    printf("\n");

    return 0;
}

Output:

&p: 0xbfe5db64  p: 0xbfe5dc24   *p: -1075452506
&a: 0xbfe5db68  a: 0xbfe5dc2c   *a: -1075452502

&p: 0xbfe5db64  p: 0x8716008    *p: 0
&a: 0xbfe5db68  a: 0x8716018    *a: 0

&p: 0xbfe5db64  p: 0x8716008    *p: 1
&a: 0xbfe5db68  a: 0x8716018    *a: 0 1 2 3 4 5 6 7 8 9 

&p: 0xbfe5db64  p: 0x8716008    *p: 0
&a: 0xbfe5db68  a: 0x8716018    *a: 0 1 2 3 4 5 6 7 8 9 

&p: 0xbfe5db64  p: 0x8716008    *p: 12
&a: 0xbfe5db68  a: 0x8716018    *a: 3 3 3 3 3 3 3 3 3 3 

Now, questions and observations:

  1. When I print pointers before give memory for it, why pointer have a random value and a random address to point to it and why it isn't NULL?

  2. After we use malloc, we can see the address where pointer points to changed and its value is NULL, so what malloc really does?

  3. After we give a value to it and print it, we free it and print it again, but values and address are same as behind for array, but not for the integer, why? So what free really does?

  4. After we freed space, we can continue to change values of array and integer, why is this possible after free space? We don't need to reuse malloc?

回答1:

  1. Because the language specification says so. The value of the pointer (i.e. the address it points to) is indeterminate. It can point anywhere, just like an int could hold any value. Reading those values, (as you do with *p and *a in the first printfs) is actually undefined behaviour.

  2. If you mean the data it points to is 0, that is by chance. The memory allocated does not have to be zeroed out. For example, it could be part of a block previously allocated with malloc and then freed (free doesn't zero out the memory, see point 3. below.)

  3. That is also by chance. When you free the memory, it is not zeroed out, and it does not have to be used immediately. It can keep storing old values until it is used for something else (for instance, by another allocation)

  4. That is also undefined behaviour. You are writing to memory you no longer own. Anything can happen. The program could have crashed. By chance, it seems like you can successfully write to the array, probable because the memory is still not used by anything else that would cause a more apparent run time error.



回答2:

What are pointers

Pointer declarations look much like other declarations: but don't be misled. When pointers are declared, the keyword at the beginning declares the type of variable that the pointer will point to. The pointer itself is not of that type, it is of type pointer to that type. A given pointer only points to one particular type, not to all possible types. In practice, all pointers are treated as integers, but the compiler will most likely complain about an assignment between incompatible types.

int *p;

The pointer p hasn't been assigned an address, so it still contains whatever random value was in the memory it occupies (whatever value was there before it was used for p). So, provided that a pointer holds the address of something, the notation *p is equivalent to giving the name of the something directly. What benefit do we get from all this? Well, straight away it gets round the call-by-value restriction of functions. Imagine a function that has to return, say, two integers representing a month and a day within that month.

Summary

  • Arrays always index from zero—end of story.

  • There are no multidimensional arrays; you use arrays of arrays instead.

  • Pointers point to things; pointers to different types are themselves different types.

  • They have nothing in common with each other or any other types in C; there are no automatic conversions between pointers and other types.

  • Pointers can be used to simulate ‘call by reference’ to functions, but it takes a little work to do it.

  • Incrementing or adding something to a pointer can be used to step along arrays. To facilitate array access by incrementing pointers, the Standard guarantees that in an n element array, although element n does not exist, use of its address is not an error.

Pointer arithmetic

Not only can you add an integral value to a pointer, but you can also compare or subtract two pointers of the same type. They must both point into the same array, or the result is undefined. The difference between two pointers is defined to be the number of array elements separating them; the type of this difference is implementation defined and will be one of short, int, or long. This next example shows how the difference can be calculated and used, but before you read it, you need to know an important point.

In an expression the name of an array is converted to a pointer to the first element of the array. The only places where that is not true are when an array name is used in conjunction with sizeof, when a string is used to initialize an array or when the array name is the subject of the address-of operator (unary &). We haven't seen any of those cases yet, they will be discussed later. Here's the example.

#include <stdio.h>
#include <stdlib.h>
#define ARSZ 10

main(){
  float fa[ARSZ], *fp1, *fp2;

  fp1 = fp2 = fa; /* address of first element */
  while(fp2 != &fa[ARSZ]){
          printf("Difference: %d\n", (int)(fp2-fp1));
          fp2++;
  }
  exit(EXIT_SUCCESS);
}

freed Pointers

Calling free() on a pointer doesn't change it, only marks memory as free. Your pointer will still point to the same location which will contain the same value, but that vluae can now get overwritten at any time, so you should never use a pointer after it is freed. To ensure that it is a good idea to always set the pointer to NULL after free'ing it.



回答3:

1.When I print pointers before give memory for it, why pointer have a random value and a random address to point to it and why it isn't NULL?

You didn't make the pointer NULL. you are just declaring it. After declaring the pointer, it may have any value.

To make NULL-

int *p = NULL; 
int *a = NULL; 

2.After we use malloc, we can see the address where pointer points to changed and its value is NULL, so what malloc really does?

Man Page says-

void *malloc(size_t size);

The malloc() function allocates size bytes and returns a pointer to the allocated memory. The memory is not initialized. If size is 0, then malloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().

If your allocated memory have 0 means that is by a chance only! The memory allocated by malloc doesn't freed out. But calloc does!

3.After we give a value to it and print it, we free it and print it again, but values and address are same as behind for array, but not for the integer, why? So what free really does?

free does not mean that it will actually delete the memory! It will inform to the OS that I don't want this memory any more, use it for some other process!

You can certainly continue to use memory a after calling free(...) and nothing will stop you. However the results will be completely undefined and unpredictable. It works by luck only. This is a common programming error called "use after free" which works in many programs for literally years without "problems" -- until it causes a problem.

4.After we freed space, we can continue to change values of array and integer, why is this possible after free space? We don't need to reuse malloc?

This is totally Undefined behavior! After freeing the memory also the pointer still points to the same memory location. It is called Dangling Pointer.

To avoid dangling pointer, Make pointer to null after free!

But after freeing the memory, you need to use memory means use malloc to allocate memory and use it!