0

Ihave hard times on learning dynamic memory allocation in C. I have this in main.

int *a = malloc(sizeof(int));
printf("%d %d %d\n", *a, *(a + 1), *(a + 2));
*a = 4;
printf("%d %d %d\n", *a, *(a + 1), *(a + 2));
a = realloc(a, sizeof(int) * 2);
printf("%d %d %d\n", *a, *(a + 1), *(a + 2));
*(a + 1) = 8;
printf("%d %d %d\n", *a, *(a + 1), *(a + 2));
*(a + 2) = 16;
printf("%d %d %d\n", *a, *(a + 1), *(a + 2));

It printed out..

7417176 7405760 706735178
4 7405760 958328410
4 7405760 958328410
4 8 958328410
4 8 16

As you can see I reallocated a to a new size of ~8 bytes. But I initialised *(a + 2) to 16 while as you can see I only allocated ~8 bytes not more (it needs at least 12 bytes). How is this possible?

Dousea
  • 15
  • 6
  • 3
    Welcome in C, come here, we need to talk about memory management. You have a UB on your first call to `printf()`, `*(a + 1)` because it's out of bound. And you have UB on the second call to `printf()` because you didn't init `*(a + 1)`. You should read something about C before try to code in C. – Stargateur Dec 20 '16 at 06:13
  • 2
    `*(a + 1)` : 1st : reading value of out of bounds. 2nd : reading uninitialized value. – BLUEPIXY Dec 20 '16 at 06:20
  • @Stargateur Yeah, I didn't init *(a + 1) on 'purpose'. When we allocate a new memory, it didn't init the value, right? So how do I know that this memory is mine so that I can set the value? – Dousea Dec 20 '16 at 06:21
  • 1
    Since the return value of `memalloc` is discarded, it is not always possible to set a value even after `memalloc`. – BLUEPIXY Dec 20 '16 at 06:25
  • @BLUEPIXY Read the returned value of `memalloc`: `return (ptr != NULL ? (ptr = tmp) : tmp);`. I discarded it since my `a` was already set to `tmp` inside the function. – Dousea Dec 20 '16 at 06:28
  • Changing an argument in a function does not affect the caller's variable. – BLUEPIXY Dec 20 '16 at 06:30
  • What do you mean by "this memory is mine"? All memory that you allocate is yours. "how do I know?", this is why in C, we talk about memory management. The C "trust" the programmer, it's your job to know when you must set a value, allocate/free memory, etc. Like @BLUEPIXY say you don't assign the return value of `memalloc` on the second call, `a = memalloc(a, sizeof(int) * 2);`. You need to init `*(a + 1)` because you ask to `memalloc()` to increase your "array". The value of `a` had change. – Stargateur Dec 20 '16 at 06:30
  • People do "stupid" things all the time with C, and hence we have memory leaks, memory corruptions and seg faults. C is very good at letting the user shoot him/herself to the foot. The answer to your question is really experience. – 599644 Dec 20 '16 at 06:38
  • Already edited it to a more appropriate question of mine. – Dousea Dec 20 '16 at 06:43
  • 2
    They are undefined behavior. – BLUEPIXY Dec 20 '16 at 06:46
  • You might be interested in this SO documentation: http://stackoverflow.com/documentation/c/364/undefined-behavior#t=201612200719426018157 – Kami Kaze Dec 20 '16 at 07:21

1 Answers1

1

Your program has undefined behavior as you access memory that hasn't been allocated to your program.

Looking at some details:

int *a = malloc(sizeof(int));

// This part is fine. It will try to allocate the amount of memory
// needed for storing exactly 1 int. It will assign the pointer a with
// with a value that points to the allocated memory.

However, the malloc may fail (i.e. be unable to allocate the amount of memory needed) so you must check whether it failed. If malloc fails it will return NULL. So you need to add:

if (a == NULL)
{
    // malloc failed...
    // print error
    exit(1);
}

Next line is:

printf("%d %d %d\n", *a, *(a + 1), *(a + 2));

// This is undefined behavior:
//     *a will read the value of the memory just malloc'ed as an int
//     In principle that is OK but since you didn't initialize the
//     memory, it is still undefined behavior
//
//     *(a+1) and *(a+2) will read memory after the memory malloc'ed
//     So you access memory not allocated to you. That is undefined
//     behavior

So what you should do is to 1) initialize the memory first and 2) only access memory allocated to you. Like:

*a = 42;
printf("%d\n", *a);

The next line:

a = realloc(a, sizeof(int) * 2);

// In principle this is valid. However, it is not good as realloc may
// fail just like malloc.

So you should do:

int* temp = realloc(a, sizeof(int) * 2);
if (temp == NULL)
{
    // Error handling or just exit...
    free(a);
    exit(1);
 }
 a = temp;

For the rest of the code you have problems similar to the ones mentioned above.

But I initialised *(a + 2) to 16 while as you can see I only allocated ~8 bytes not more (it needs at least 12 bytes). How is this possible?

The C language doesn't check that everything you do is valid. In other words - C let you do what you ask for. It is your own responsibility to make sure that you only do valid things.

At run time the system may detect that the program is doing illegal things. If so a crash (core dump) will happen.

However, there are many situations where the system can't detect that your program is doing something illegal. So the program will just carry on. That can result in all kinds of strange errors but it may also look as if the program is correct the first 100 times you execute it and then fail the next time. That is what undefined behavior is.... undefined - you'll never know what may happen.

So coding in C puts a lot of burden on you. It is very easy to do illegal things in C. It is your responsibility to make sure that you only do legal things.

Today a number of tools are available for analyzing C and finding wrong code, e.g. valgrind and coverity.

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63