alt.126
alt.126

Reputation: 1107

Freeing a C pointer after altering its value

Can I free a pointer such as:

unsigned char *string1=NULL;
string1=malloc(4096);

After altering its value like:

*string1+=2;

Can free(string1) recognize the corresponding memory block to free after incrementing it (for example to point to a portion of a string), or do I need to keep the original pointer value for freeing purposes?

For example, for an implementation of the Visual Basic 6 function LTrim in C, I need to pass **string as a parameter, but in the end I will return *string+=string_offset_pointer to start beyond any blank spaces/tabs.

I think that here I am altering the pointer so if I do this in this way I will need to keep a copy of the original pointer to free it. It will probably be better to overwrite the non-blank contents into the string itself and then terminate it with 0 to avoid requiring an additional copy of the pointer just to free the memory:

void LTrim(unsigned char **string)
{
 unsigned long string_length;
 unsigned long string_offset_pointer=0;


 if(*string==NULL)return;

 string_length=strlen(*string);
 if(string_length==0)return;


 while(string_offset_pointer<string_length)
 {
  if(
     *(*string+string_offset_pointer)!=' ' &&
     *(*string+string_offset_pointer)!='\t'
    )
  {
   break;
  }

  string_offset_pointer++;
 }


 *string+=string_offset_pointer;
}

It would probably be best to make the function to overwrite the string with a substring of it but without altering the actual value of the pointer to avoid requiring two copies of it:

void LTrim(unsigned char **string)
{
 unsigned long string_length;
 unsigned long string_offset_pointer=0;
 unsigned long string_offset_rebase=0;


 if(*string==NULL)return;

 string_length=strlen(*string);
 if(string_length==0)return;


 //Detect the first leftmost non-blank
 //character:
 ///
  while(string_offset_pointer<string_length)
  {
   if(
      *(*string+string_offset_pointer)!=' ' &&
      *(*string+string_offset_pointer)!='\t'
     )
   {
    break;
   }

   string_offset_pointer++;
  }



 //Copy the non-blank spaces over the
 //originally blank spaces at the beginning
 //of the string, from the first non-blank
 //character up to the string length:
 ///
  while(string_offset_pointer<string_length)
  {
   *(*string+string_offset_rebase)=
   *(*string+string_offset_pointer);

   string_offset_rebase++;
   string_offset_pointer++;
  }



 //Terminate the newly-copied substring
 //with a null byte for an ASCIIZ string.
 //If the string was fully blank we will
 //just get an empty string:
 ///
  *(*string+string_offset_rebase)=0;


 //Free the now unused part of the
 //string. It assumes that realloc()
 //will keep the current contents of our
 //memory buffers and will just truncate them,
 //like in this case where we are requesting
 //to shrink the buffer:
 ///
  realloc(*string,strlen(*string)+1);
}

Upvotes: 1

Views: 81

Answers (2)

Barmar
Barmar

Reputation: 780818

The value passed to free() must be a pointer returned by malloc(), calloc(), or realloc(). Any other value results in undefined behavior.

So you have to save the original pointer if you modify it. In your code you don't actually modify the pointer, you just increment the contents of the location that it points to, so you don't have that problem.

Why is the language specified this way, you might ask? It allows for very efficient implementations. A common design is to store the size allocation in the memory locations just before the data. So the implementation of free() simply reads the memory before that address to determine how much to reclaim. If you give some other address, there's no way for it to know that this is in the middle of an allocation and it needs to scan back to the beginning to find the information.

A more complicated design would keep a list of all the allocations, and then determine which one the address points into. But this would use more memory and would be much less efficient, since it would have to search for the containing allocation.

Upvotes: 6

Peter
Peter

Reputation: 36597

Since you're actually doing

unsigned char *string1=NULL;
string1=malloc(4096);
*string1+=2;
free(string1);

free(string1) IS being passed the result of a malloc() call.

The *string1 += 2 will - regardless of the call of free() - have undefined behaviour if string1[0] is uninitialised. (i.e. If there is some operation that initialises string1[0] between the second and third lines above, the behaviour is perfectly well defined).

If the asterisk is removed from *string1 += 2 to form a statement string1 += 2 then the call of free() will have undefined behaviour. It is necessary for free() to be passed a value that was returned by malloc() (or calloc() or realloc()) that has not otherwise been deallocated.

Upvotes: 7

Related Questions