1

The following code seemingly runs fine:

char **func()
{
    char **s = malloc(sizeof(char) * 5);
    
    s[0] = "1101";
    s[1] = "1001";
    s[2] = "0001";
    // s[3] = "1100";
    return s;
}

int main()
{
    char **s;
    s = func();
    printf("Hello World: %s", s[0]);
    free(s);

    return 0;
}

But if I uncomment the line s[3] = "1100"; there is an error:

Error in `./a.out': free(): invalid next size (fast): 0x000000000259f010

Why is it so, even when the array has enough size?

anastaciu
  • 23,467
  • 7
  • 28
  • 53
Developer
  • 425
  • 3
  • 15
  • 7
    `sizeof(char)` should be `sizeof(char *)` – Barmar Nov 04 '20 at 21:00
  • 4
    `sizeof(char) * 5` is the wrong size. You could avoid this problem by using the recommended style `p = malloc(N * sizeof *p);` . https://stackoverflow.com/questions/605845/ – M.M Nov 04 '20 at 21:00
  • 5
    If the argument to `sizeof` in `malloc` is a type, it should always have one less `*` than the type you're assigning to. – Barmar Nov 04 '20 at 21:02

4 Answers4

2

Here you are allocating enough memory for 5 chars.

char **s = malloc(sizeof(char) * 5);

not however for 5 pointers to char, which are bigger.
Lets assume pointers are four bytes (could be 8...). Chars are definitly 1 byte.

Here you are writing into that memory, something of 4 bytes (because of the size of a pointer, not because of the four characters).
Not a problem yet. s[0] = "1101";

Here you are writing into memory which is on a 4 byte higher address.
That adress itself is not yet a problem, it is still within the 5 allocated bytes.
However, writing something larger than one byte there (which you do, size 4) is already incorrect. s[1] = "1001";

Even worse here, 8 bytes behind the address you got, 3 bytes behind what is alled and a total of 4 bytes. Very incorrect. s[2] = "0001";

You could already get problems for the second write.
Not encountering problems for second and third access is pure luck. Good or bad luck, depending on your philosophy on when you prefer to find errors.

To solve, allocate memory for the size of what you are writing. Quoting from Barmar comments:

sizeof(char) should be sizeof(char *)
If the argument to sizeof in malloc is a type, it should always have one less * than the type you're assigning to.

From Deduplicator (I actually prefer this, too):

I would much prefer to see char** s = malloc(5 * sizeof *s);. Not having to write the type makes it less error-prone.

The part of not having to type the type includes the potentially wrong part of the number of pointers/asterisks. I.e. this best practice would have prevented your problem.

Yunnosch
  • 26,130
  • 9
  • 42
  • 54
  • I would much prefer to see `char** s = malloc(5 * sizeof *s);`. Not having to write the type makes it less error-prone. – Deduplicator Nov 05 '20 at 01:41
  • @Deduplicator True. I thought I might seem like a nitpicker to change that detail, but with your support, I am happy to go to that level of detail. Thanks. – Yunnosch Nov 05 '20 at 06:20
1

sizeof( char ) is always equal to 1. So in this call of malloc

char **s = malloc(sizeof(char) * 5);

you allocated a memory for 5 bytes.

But you are using this memory extent to store pointers that usually can be equal either to 4 or to 8 bytes depending on the used system.

So you are accessing a memory beyond the allocated extent. And as a result the program has undefined behavior.

What you need is to write

char **s = malloc(sizeof(char *) * 5);

or you could write

char **s = malloc(sizeof( *s ) * 5);
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

A pointer, in a 64 bit system, which I will guess is your case, is usually 8 bytes in size, your memory allocation only reserves space for 5 chars which is only 5 bytes, that's not even enough for one pointer, let alone four.

For me the surprise is how it does not break in the 1st or the 2nd assignment.

The largely consensual way to do this is to use the dereferenced name of the pointer to which the memory is assigned as an argument of sizeof, precisely to avoid problems like the one you are experiencing:

char **s = malloc(sizeof *s * 4); //you have 4 pointers

Not only it avoids errors, but it also makes the code more maintainable.

anastaciu
  • 23,467
  • 7
  • 28
  • 53
1

Why is it so, even when the array has enough size?

Add some code to check what is going on.

char **func(){
    char **s = malloc(sizeof(char) * 5);
    
//    s[0] = "1101";
//    s[1] = "1001";
//    s[2] = "0001";
//    s[3] = "1100";

    printf("allocated : %zu bytes\n", sizeof(char) * 5);
    printf("used : %zu bytes\n", sizeof(s[0]) + sizeof(s[1]) + sizeof(s[2]) + sizeof(s[3]));
    printf("Is the allocated memory big enough?\n");
    return s;
}

https://godbolt.org/z/9aqYKb

allocated : 5 bytes
used : 32 bytes
Is the allocated memory big enough?

Does it answer your question?

Makyen
  • 31,849
  • 12
  • 86
  • 121
0___________
  • 60,014
  • 4
  • 34
  • 74