1

I've read the other questions of this type, but I still can't figure out why this error is occurring in my code.

Here is my code:

#include <stdio.h>
#include <stdlib.h>

typedef struct {                 
    int len;
    int str[0];
} bit_str;

bit_str * input(void);
void print_bitstr(bit_str * s);

int main(void)
{
    bit_str * s1 = input();
    print_bitstr(s1);

    return 0;
}

bit_str * input(void)
{
    bit_str *s = (bit_str *) malloc(sizeof(bit_str)); 
    int count = 0, bit;

    while ((bit = getchar()) != 'k'){
        s = (bit_str *) realloc(s, sizeof(*s) + sizeof(int));           
        s->str[count++] = bit - 48;
    }

    s->len = count;
    return s;
}

void print_bitstr(bit_str * s)
{
    for (int i = 0; i < s->len; i++)
        printf("%d ", s->str[i]);
    printf("\n");

    return;
}

This code is part of a program I was trying to add two bit strings together (as an exercise from a Data Structures book).

I've created a struct that will store the bits string and it's length. The struct is initialized using a malloc in the input function. It is reallocated every time a new bit is added. The bits are read using a getchar, and the end of a bitstring is demarcated by the letter 'k'.

When I compile the code, it works perfectly for exactly 6 bits. But if I try to input 7 bits, it crashes with following error:

> realloc(): invalid next size  

I've read the other posts with similar error, but I couldn't figure out why this error is occurring in my code here. I've made sure that when I reallocate, I use sizeof operator to get the size, instead of absolute values.

Can someone please help me figure out what I'm doing wrong here?

user2736738
  • 30,591
  • 5
  • 42
  • 56
Nathu
  • 449
  • 1
  • 4
  • 11
  • 2
    `int str[0];` is not allowed in Standard C – M.M Jan 01 '18 at 07:56
  • 1
    I think you should not do this unless you have a lot of experience with C. Use classic allocation method not FMA. – Stargateur Jan 01 '18 at 08:09
  • Indeed, you must use `int str[];`. `int str[0]` is a GCC extension but so would the standard `int str[]` work there - it seems from the error message, that you're using glibc + gcc. – Antti Haapala -- Слава Україні Jan 01 '18 at 08:49
  • Also, do not cast the return value of `malloc`. – Antti Haapala -- Слава Україні Jan 01 '18 at 08:51
  • The allocation should be`something like `s = (bit_str *) realloc(s, sizeof(*s) + (count + 1) * sizeof(int));`. For the explanation, just read Jonathon'a answer. – Joël Hecht Jan 01 '18 at 09:19
  • The error message means you've been trampling outside the bounds of the memory that was allocated to you. There's a bug somewhere in your code; you need to fix it. The [Valgrind](http://valgrind.org/) program is excellent for helping with this — use it if possible. The details of what's wrong with your code are covered in the answer. – Jonathan Leffler Jan 01 '18 at 09:49
  • 1
    Possible duplicate of [realloc(): invalid next size when reallocating to make space for strcat on char \*](https://stackoverflow.com/questions/8436898/realloc-invalid-next-size-when-reallocating-to-make-space-for-strcat-on-char) – Jean-Baptiste Yunès Jan 01 '18 at 11:31
  • when calling any of the heap allocation functions: (malloc, calloc, realloc) 1) always check (!=NULL) the returned value to assure the operation was successful. 2) the returned type is `void*` which can be assigned to any other pointer, Casting just clutters the code, making it more difficult to understand, debug, etc. 3) when calling `realloc()` assign the returned value to a temporary variable, check that variable, then if the function was successful, then assign to the target variable. Otherwise, when `realloc()` fails, the original pointer is destroyed, resulting in a memory leak. – user3629249 Jan 01 '18 at 16:57

1 Answers1

5

sizeof(*s) is evaluated at compile-time; it has no knowledge of dynamic allocations such as malloc or realloc.

So even though you're attempting to dynamically add sizeof(int) bytes to the current allocation:

s = (bit_str *) realloc(s, sizeof(*s) + sizeof(int)); 

This always causes s to point to sizeof(bit_str) + sizeof(int) bytes.

Of course, after that no-op realloc, you proceed to write more bytes to the buffer, overrunning the buffer and resulting in undefined behavior.

Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
  • 1
    But before I write any bytes to it, I realloc it and increase it's size by one integer. This is done each time I write to it. – Nathu Jan 01 '18 at 07:44
  • 1
    You only ever add one `int` -- you don't keep track of its size. ` sizeof(*s)` doesn't know how big your last allocation was. – Jonathon Reinhart Jan 01 '18 at 07:45
  • @JonathonReinhart: I think you need to explain that `sizeof` works off of static type information, not current dynamic size of the block... – Ben Voigt Jan 01 '18 at 07:46
  • I think I do, since I use sizeof(*s) + sizeof(int). So reallocate it to be it's own size + the size of one int. Atleast that's what I intend to with my code. – Nathu Jan 01 '18 at 07:47
  • @Nathu `sizeof` is evaluated at compile time -- it knows nothing about runtime constructs such as `malloc` or `realloc`. Someone owes me a downvote back. – Jonathon Reinhart Jan 01 '18 at 07:50
  • 1
    @JonathonReinhart `sizeof` may be evaluated at runtime, e.g. `int x[rand() % 6 + 1]; printf("%d\n", (int)sizeof x);`. In this question `s` has type `bit_str *`, so `*s` has type `bit_str` and we are seeing the size of that type – M.M Jan 01 '18 at 07:57
  • @M.M I avoided your C99-ism by specifying `sizeof(*s)` :-) – Jonathon Reinhart Jan 01 '18 at 08:10