Search code examples
c++cstringmemory-managementmemmove

Making memmove() safe using realloc()


In my function to replace a substring. If the input substring is longer than the original one, it moves part of the input string out to make room for the input substring.

I understand this results in undefined behavior. I thought I should be able to allocate the needed space by using realloc() but have not been successful.

I tried adding this before memmove():

char *newspc = (char*)realloc(in,len+sublen);
in = newspc;

Is this a reasonable strategy? What is the right way to make space for this operation?

Here is the program without using realloc():

#include <iostream>
#include <string>
#include <string.h>

void replc(char* in, char* subin);

int main()
{
  char stmt[] = "replacing this $string ok";
  std::cout << stmt << "\n";
  replc(stmt, "longerstring");  //<<<4 characters longer breaks the program
  std::cout << stmt << "\n";

}

void replc(char* in, char* subin){
  uint8_t len = strlen(in);
  uint8_t aftok = strchr(strchr(in, '$'), ' ')-in;
  uint8_t dollar = strchr(in, '$')-in;
  uint8_t tklen = aftok - dollar;
  uint8_t sublen = strlen(subin);

   if(sublen <= tklen){
    //enough room for substring
    memmove(in+aftok-(tklen-sublen), in+aftok, (tklen-sublen)+1);
    memcpy(in+dollar, subin, sublen);
    in[len-(tklen-sublen)] = '\0';
   }
   else{
   //not enough room for substring
   // memory allocation should take place here?
    memmove(in+aftok+(sublen-tklen), in+aftok, (sublen-tklen)+1);
    memcpy(in+dollar, subin, sublen);
    in[len+(sublen-tklen)] = '\0';
   }

}

Solution

  • First, if you want to use realloc, you don't have to use memmove, since realloc will take care of copying data.

    From man:

    The realloc() function changes the size of the memory block pointed to by ptr to size bytes. The contents will be unchanged in the range from the start of the region up to the minimum of the old and new sizes.

    Also, you can only use realloc on pointer previously returned by malloc, realloc, or calloc

    Unless ptr is NULL, it must have been returned by an earlier call to malloc(), calloc() or realloc().

    So you need to use malloc in your main

    char *stmt = malloc(strlen("replacing this $string ok") + 1);
    if (stmt)
        stmt = "replacing this $string ok";
    

    Second, if you want to change the value of the pointer in the caller function you need to use a pointer on that pointer (C style) or a reference (C++ style), else the pointer in the caller will point to the old address.

    C-style example for prototype:

    void replc(char** in, char* subin);
    

    Allocation (with NewSize as integer):

    *in = realloc(*in, NewSize);
    

    (Keep in mind that malloc and realloc can return NULL if the allocation fail)