-1

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';
   }

}
oraz
  • 277
  • 1
  • 6
  • 14
  • 2
    *I thought that any index outside the string would be non-allocated memory. If I increase the length of the input substring by a few characters, the program does fail.* -- Undefined behavior, plain and simple. You access an array beyond the bounds, then anything can happen, including "work". – PaulMcKenzie Apr 17 '16 at 07:18
  • I altered the question in light of that link. – oraz Apr 17 '16 at 07:41
  • 1
    Why not just use `std::string` for this? I know you're going to eventually figure out the "magic code" to get this fixed, as all it does is do stuff with buffers and pointers. But what do you gain out of it, except to fix a bug? Just use `std::string` if you value your time. Heck, you even included ``, but failed to use anything in it. – PaulMcKenzie Apr 17 '16 at 07:46
  • 3
    You can only call `realloc` on space earlier allocated by `malloc` (or null) – M.M Apr 17 '16 at 07:47
  • 1
    ...or `realloc`, or `calloc`. – WhozCraig Apr 17 '16 at 07:50
  • @PaulMcMenzie This is actually being implemented in Arduino, I asked in terms of c++ because It would be an easier platform to test, and I was asking about memory allocation in general. – oraz Apr 17 '16 at 07:50
  • Also, I hope you understand why `in = newspc;` is not viable unless you pass `in` by-reference to the function? Without that, the caller knows nothing about the new value of `in`, and will be left with a dangling pointer. – WhozCraig Apr 17 '16 at 07:53
  • @oraz - If that's the case, let me recommend that you create a String class that properly manages memory, resizes strings, etc. instead of throwing pointers around all over the place in some off-coded function. – PaulMcKenzie Apr 17 '16 at 07:54
  • @PaulMcMenzie Agreed, but this part of another effort to conserve program memory space, so the inputs are all char[]s. – oraz Apr 17 '16 at 07:58
  • @oraz So you're saying that a simple home-made String class that basically does what your code is doing, but is encapsulated and is better organized, takes up precious space? Then you might as well tagged this question as `C` and not `C++`. – PaulMcKenzie Apr 17 '16 at 08:04
  • @PaulMcMenzie In the full program, using String does indeed take up too much space on my device, which is why I am writing the function. changed the tag to C. – oraz Apr 17 '16 at 08:24

1 Answers1

1

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)

Wddysr
  • 86
  • 1
  • 5
  • It seems I had a few things to learn about the -alloc functions, but this helped. The parameter I used was: `(char*& in, ... ` and allocated with `char* p = (char*)calloc(len+sublen, sizeof(char));` – oraz Apr 17 '16 at 14:56