0

I have a matrix dinamically allocated of char or something else like this:

char **ptr;
ptr = (char**)malloc(sizeof(char*) * 3);
*ptr = (char*)malloc(4);
*(ptr + 1) = (char*)malloc(4);
*(ptr + 2) = 0;

If I will free the lines of matrix without free the memory of pointers like

int i;
for(i = 0; i < 2; i++)
    free(*(ptr + i));

will be free all lines beside the last one before the null pointer, in this case will be free only the *ptr line. To free and the last one we need to add one more instruction

free(ptr);

The question is what it works like this and why we can't to free only the pointers memory or only the string memory of that matrix?

Xtx
  • 83
  • 10
  • https://ericlippert.com/2014/03/21/find-a-simpler-problem/ First change to the simpler concept of using an array, size 3, of poitners to char. Get used to mallocing and freeing those three pointers. When that works, change the array to a malloced piece of memory - if it is really necessary for your goal. You might by the way want to explain a bit more about the context of the code. It helps with understanding your question and thereby with helping you. – Yunnosch May 04 '18 at 05:57
  • If i will free the last pointer, ptr + 2, it will work fine without instruction free(ptr); ? – Xtx May 04 '18 at 06:00
  • @Yunnosch, i did it for 2 hours before to put the question on stack, i couldn't find by myself an explanation – Xtx May 04 '18 at 06:03
  • Is that an answer to my proposal? Your and my comment seem so unrelated... – Yunnosch May 04 '18 at 06:07
  • I said that about your link, i know very well the concept inside of that, context of code doesn't exist, it's something generally which i want to know how it's works and why it works like that. – Xtx May 04 '18 at 07:10
  • The text is more than `try simpler;` it is more of `while(!works) try_even_simpler; while(works) increase_minimally;`. – Yunnosch May 04 '18 at 07:13
  • I did it my friend, and i resolve the problem, but i want to understand why is that way – Xtx May 04 '18 at 07:21
  • `*(ptr + 2)` - is never allocated. You allocate for `3-pointers` and allocate `4-chars` assigned to the first 2 pointers. The 3rd pointer is not `NULL` it is *uninitialized*. You only need to `free` what you allocate. See: [Do I cast the result of malloc?](http://stackoverflow.com/q/605845/995714) – David C. Rankin May 04 '18 at 07:32
  • The *(ptr+2) don't enter in discution, my code if it will be run, without the last instruction free(ptr); after the for, will free only the first line *ptr and *(ptr+1) will stay allocated. – Xtx May 04 '18 at 07:36
  • Sorry, my point was not to say you should allocate it, my point was it was never allocated and thus does not need a separate call to `free`. I apologize for any confusion. – David C. Rankin May 04 '18 at 07:41
  • I don't free it. – Xtx May 04 '18 at 07:46

2 Answers2

1

I believe there are two issues with the way you may be thinking of memory allocation.

Firstly, you appear to be thinking of the matrix as being allocated inside the memory as a visual matrix. But you must remember that the compiler does not have a concept of a matrix. It also does not know that the memory you allocate as a starting point, in a row of a matrix, points to the row.

Secondly, you must explicitly tell the compiler what you want to happen, rather than relying on the compiler to guess, which may also cause undefined behaviour.

I'll use 8-bit addresses to illustrate this.

Take ptr = (char**)malloc(sizeof(char*) * 3), it may allocate addresses at 0x30 0x31 0x32, which will store an 8-bit type each. Suppose that you do *(ptr + 0) = malloc(4). The malloc call will return a pointer to four consecutive locations where chars can be stored. Suppose it returns 0x40 this means that addresses 0x40 0x41 0x42 0x43 are all available to use.

However, the value at address 0x30 is assigned 0x40, now remember that a pointer is not a type, but rather a binary representation of a number, that happens to represent an address. Therefore, pointers do not have the luxury of a deallocator (e.g. a destructor in C++). The compiler does not know that this value is used to access an allocated address of memory but rather stores that value for you. Calling free(ptr) will only free the addresses 0x30 0x31 0x32 for this reason.

Simply put, suppose you have four integers stored in an int* as 1 2 3 4, if the compiler attempted to free the four integers as addresses, you will run into issues as you are literally telling the compiler to free addresses 0x01 0x02 0x03 0x4 which on an old system would have dire consequences, and on modern systems, you're application would be terminated.

This may seem inconvenient initially, however, this implies some flexibility in using these pointers. For example, you may wish to use ptr again, instead of making a call to free and another call to malloc, to point at another matrix.

If freeing all rows of a matrix also freed the pointers to the rows, you may have a situation where you run into segmentation faults because you were unaware of a pointer pointing to this address.

In saying this, it is your responsibility to keep track of this and make sure that the heap does not accumulate data that has no references pointing to it.

  • You explained it very well, I knew that before I posted the question, I had an undefined behavior of my code and I have been thinking that's how it works. – Xtx May 07 '18 at 08:47
0

You should always do the exact same number of frees as mallocs.

You could rewrite your code as:

char **ptr;
ptr = malloc(sizeof(char*) * 3); //A
*(ptr + 0) = malloc(4); //B
*(ptr + 1) = malloc(4); //B
*(ptr + 2) = 0; //C

You malloc 3 times:

  • A. You malloc an array to hold 3 pointers to char arrays.
  • B. You malloc an array of 4 chars and assign it to one of the pointer values you created in A.
  • C. You set the last element of A. to 0. This is the same as setting it to NULL. But, david notes, setting it to NULL is better. This is a special value, called the null pointer.

Freeing ptr first, won't work, as you then loose the values of *(ptr+0) etc. Therefor after that, you won't be able to free them. So you must first free *(ptr+0) and *(ptr+1) as you malloc'ed them. But you won't have to free *(ptr+2), as you didn't malloc it.

You can however safely call free on *(ptr+2) as you have previously set it to NULL, and you can always safely free null. Just to be safe, I always set a pointer to NULL after freeing.

*(ptr + 0) and *(ptr + 1) are just pointers to what you allocated with (char*)malloc(4);. Freeing ptr will free those pointers themselves, not the memory they are pointing to. Free isn't recursive. So you must free the individual pointers before you can free the array of pointers.

Lanting
  • 3,060
  • 12
  • 28
  • Sorry, was my bad, I edited and now you can look i reefer to something else. – Xtx May 04 '18 at 07:07
  • You have to be careful saying you can just set the pointer to `0` for `NULL` (it is allowed), but technically it is `(void *)0`, `NULL` is cleaner. (and use the disappearing paragraph stuff -- very sparingly, it is more of a distraction to some than a benefit) – David C. Rankin May 04 '18 at 07:38
  • From my knowledge NULL is a define of (void*)0 so is same thing, sorry if i'm mistaken – Xtx May 04 '18 at 07:45