tags:

views:

109

answers:

5

I'm trying to create a pointer to char pointer to which I can easily add new elements (strings). I use malloc to create the first 2 dimensions and realloc when I want to add new items. I wrote code with and without realloc and I'm getting the same results. Is this an expected/normal behavior?


With realloc:

    char **p; // create pointer to char pointer
    p = malloc(sizeof(char*) * 2); // allocate 2 dimensions
    p[0] = "ab";
    p[1] = "cd";

    void* resizedP = (void*)realloc(p, sizeof(char*) * 4); // resize array
    p = (char**)resizedP;
    p[2] = "ef";
    p[3] = "gh";

    printf("%s \n", p[0]); // prints ab
    printf("%s \n", p[1]); // prints cd
    printf("%s \n", p[2]); // prints ef
    printf("%s \n", p[3]); // prints gh

    free(p);


Without realloc:

    char **p;
    p = malloc(sizeof(char*) * 2);

    p[0] = "ab";
    p[1] = "cd";
    p[2] = "ef";
    p[3] = "gh";

    printf("%s \n", p[0]); // prints ab
    printf("%s \n", p[1]); // prints cd
    printf("%s \n", p[2]); // prints ef
    printf("%s \n", p[3]); // prints gh

    free(p);
+8  A: 

It is absolutely necessary to call realloc() to resize a memory block obtained by malloc(). You are invoking undefined behavior by accessing beyond the bounds of the memory block that has been allocated to your program.

There might be some important data right after your allocated memory block that your second code snippet will run over. And if the program even attempts to access memory blocks in another process because of an out-of-bounds error, the operating system will complain loudly (resulting in segmentation faults).

There is no guarantee that your second code snippet (without realloc()) won't crash or otherwise fail in the future. It just happened to work this time.

In silico
Thanks a lot for your answer!
jdecuyper
Technically it can't and won't run over another process' memory on any system that would SIGSEGV a process. More like it can reference a segment that's not marked for write access or a page that doesn't have a frame mapped to it.
Novikov
@Novikov: Yes, the operating system should stop a memory access to another program before it gets run over. I've edited my post to make that a little bit clearer.
In silico
+2  A: 

Suppose your p is a bus with space for 20 people.

And you try to put 30 people inside (without enlarging it). Some times it works, if they're not fat ...

In respect to C, that's Undefined Behaviour. Anything can happen. You were unlucky just now and it "worked".

In short: Don't do that!

pmg
+4  A: 

In your case, it may work by coincidence. For example, your C library's malloc() may decide to pad your allocation size by some number which happens to be >= 2*sizeof(void*).

However, in theory, there is nothing to stop a subsequent malloc from returning the bytes that happen to lie at p[2]. There is also nothing to stop malloc from returning a value whereby after the size you requested, the OS hasn't mapped any page into the address space. Therefore you should not rely on this behavior. Don't write to p[2] without allocating the right amount, because that could lead to any of the following:

  • You overwrite a value that some other function has written.
  • Some other function may overwrite you later.
  • You may crash trying to access it.
asveikau
+3  A: 

Try running your second option through valgrind or with the -fstack-protector compiler switch. You might get more information about why In silico's answer is right.

Peter K.
Thanks, I read the documentation explaining what valgrind does and how it works. I will definitively try it but on another box with Linux installed.
jdecuyper
+1  A: 

Whoa don't do that man! There's a reason it works, and it's nothing but trouble.

Firstly, p isn't an array, it's a pointer, you can see this by checking sizeof(p) it will always return either 4 or 8, depending on whether you are running a 32 or 64 bit system.

A pointer refers to a position in memory so if you try to print a pointer, you'll get a memory address, such as 0x0B32D2F1 on a 32-bit machine, When you call one of the *alloc functions, the processor finds a free block of memory of the size you request, and allocates it to your program.

What you doing is accessing and editing memory that hasn't been allocated to you, while being easily possible, nothing stops you from doing it, but it's bad because that memory might be allocated to a different program, and then the user will hate you when you crash his other programs. In short, ALWAYS ALLOCATE THE MEMORY YOU WILL BE USING! ALWAYSS!

Oh yeah, then there's the possibility the processor will allocate that unallocated memory you're using to another program, which will mess with yours.

Andrew Dunn
Thanks for pointing out my confusion between array and pointer. I edited my question's title.
jdecuyper