views:

114

answers:

4

Hi there, I'm having some trouble with a program that is intended to be a String buffer, specifically this function is intended to reset the buffer with the string cstr. If cstr is null then the content needs to be reset to an empty char '\0'. It always hangs at the second set of realloc where it's resizing buf->contents I have no clue why that is. Any help would be awesome.

The struct:

typedef struct strbuf {
     char   *contents;
     size_t  length;  
} StringBuffer;

It is called from

strbuf_reset(sb, NULL)

Here is the strbuf_reset function that is having the problem.

StringBuffer *strbuf_reset(StringBuffer *buf, const char *cstr)
{
if(buf == NULL)
    return NULL;

StringBuffer *tempBuf = NULL ;

if(cstr == NULL)
    tempBuf = (StringBuffer*)realloc(buf,sizeof(StringBuffer) + sizeof(char));
else
    tempBuf = (StringBuffer*)realloc(buf,sizeof(buf) + strlen(cstr)*sizeof(char));

if(tempBuf == NULL)
    return NULL;

if(cstr == NULL)
    tempBuf->contents = (char*)realloc(buf->contents,sizeof(char));
else
    tempBuf->contents = (char*)realloc(buf->contents,(sizeof(buf->contents) + strlen(cstr)*sizeof(char) + 1));

if(tempBuf->contents == NULL){
    free(tempBuf);
    return NULL;
}
buf = tempBuf;

if(cstr == NULL)
   buf->contents = '\0';
else
   strcat(buf->contents,cstr);

buf->length = strlen(buf->contents);    

return buf;
 }

With what I believe to be the suggested changes...

StringBuffer *strbuf_reset(StringBuffer *buf, const char *cstr)
{
if(buf == NULL)
    return NULL;

StringBuffer *tempBuf = NULL ;

if(cstr == NULL)
    tempBuf = (StringBuffer*)realloc(buf,sizeof(StringBuffer) + sizeof(char) + 10);
else
    tempBuf = (StringBuffer*)realloc(buf,sizeof(buf) + strlen(cstr)*sizeof(char)+ 1);

if(tempBuf != NULL)
    buf = tempBuf;
else
    return NULL;    

if(cstr == NULL)
    tempBuf->contents = (StringBuffer*)realloc(buf->contents,sizeof(StringBuffer) + sizeof(char) + 10);
else
    tempBuf->contents = (StringBuffer*)realloc(buf->contents,sizeof(buf) + strlen(cstr)*sizeof(char)+ 1);

if(tempBuf != NULL)
    buf->contents = tempBuf->contents;
else
    return NULL;

if(cstr == NULL)
   buf->contents = '\0';
else
   strcat(buf->contents,cstr);

buf->length = strlen(buf->contents);    

return buf;
 }
+7  A: 

You don't seem to understand what realloc does.

The way you should be thinking about it (at least as far as enlarging goes), is that it allocates a new buffer, copies the old data into it, and then frees the old buffer.

The old pointer is then invalid and it should not be suprising if you get crashes when you try and use it again later.

You should be assigning the returned value back to the old pointer immediately, so it still points at valid data.

Anon.
I believed that returning the value into a temporary pointer to ensure that the operation completed successfully and then copying the temporary pointer value to the original was enough. While the modified code does not work still is that what you meant?
Dean
+1 With small note: First check if return value != NULL (and errno != ENOMEM), then assign to old pointer. Otherwise you'll have memory leak if realloc fails to allocate larger buffer.
Tomek Szpakowicz
@tomekszpakowicz (I think that's spelt right...): Thanks for that addition. @Dean: Your improved version is better on that front. But at this point, your memory allocations are looking a bit schizophrenic. How do you initially allocate memory for buf->contents?
Anon.
+1  A: 

You are allocating extra space for the string in the StringBuffer. I just assumed buf->contents is supposed to point into that extra allocated space. If this is not true then why is the extra space allocated in the StringBuffer?

If buf->contents already points into memory that was allocated for StringBuffer, attempting to realloc it will throw the memory system into a crash/hang/corrupt heap situation, because you would be realloc'ing a pointer that was never allocated.

Instead of attempting to realloc buf->contents, I think the structure should look like:

struct StringBuffer {
    size_t length;
    char contents[1];
};

Then instead of reallocing buf->contents you just copy the string into there and the realloc for StringBuffer takes care of all the memory.

Zan Lynx
If i just copy the string in its giving me a seg fault.
Dean
+1  A: 

Since you are going to overwrite StringBuffer's contents, using realloc makes no sense. It will not save you any memory allocation but instead will copy old data you intent to overwrite anyway. Use normal malloc and free.

With your original structure

typedef struct strbuf {
  char   *contents;
  size_t  length;  
} StringBuffer;

strbuf_reset sets buf to cstr. On success returns buf, on failure NULL.

StringBuffer *strbuf_reset(StringBuffer *buf, const char *cstr)
{
  if (!buf)  return NULL;      
  if (!cstr)  cstr = "";

  size_t len = strlen(cstr);
  if (len > buf->length) {
    char *new_contents = malloc(len + 1);
    if (!new_contents)  return NULL;
    free(buf->contents);
    buf->contents = new_contents;
  }
  memcpy(buf->contents, cstr, len + 1);
  buf->length = len;

  return buf;
}
Tomek Szpakowicz
This deffinitely makes the most sense.
Dean
@Dean: No it doesn't. Gosh! Now I'm asking myself, why I'd given answer like that. Full solution. Instead of just answering your question: "No, there is no problem with realloc. Program crashes because..." ;-P
Tomek Szpakowicz
A: 

Time to use malloc and free instead of all this realloc business. Everything is being overwritten anyway. Thanks everybody. Deffinitely better with realloc anyway now!

Dean