1

I am a beginner with C. I am wondering, how's malloc working. Here is a sample code, I wrote on while trying to understand it's working.

CODE:

#include<stdio.h>
#include<stdlib.h>
int main() {
  int i;
  int *array = malloc(sizeof *array);
  for (i = 0; i < 5; i++) {
    array[i] = i+1;
  }
  printf("\nArray is: \n");
  for (i = 0; i < 5; i++) {
    printf("%d ", array[i]);
  }
  free(array);
  return 0;
}

OUTPUT:

Array is: 
1 2 3 4 5

In the program above, I have only allocated space for 1 element, but the array now holds 5 elements. So as the programs runs smoothly without any error, what is the purpose of realloc().

Could anybody explain why?

Thanks in advance.

bharadhwaj
  • 2,059
  • 22
  • 35
  • 4
    C doesn't mandate any bounds checking on array access - you've written data past the end of the array. The behavior on doing so is *undefined*, and literally any result is possible, including working as expected. You've written data to memory you don't own, but haven't clobbered anything important, so your program appears to work correctly. – John Bode Jul 08 '17 at 11:35
  • @JohnBode Thanks for the info! As I mentioned, I am beginner with C, so was in confusion. Thanks for clearing. :) – bharadhwaj Jul 08 '17 at 11:39
  • What array? I don't see any array? – ThingyWotsit Jul 08 '17 at 11:52
  • @ThingyWotsit: I guess OP thinks of `array` which is indeed a pointer, not an array. – Basile Starynkevitch Jul 08 '17 at 11:53
  • @bharadhwaj: possible reasons to close your question is that: 1. it has been asked many times in some form. 2. You are confusing arrays and pointers, and you don't yet understand what "arrays decay into pointers" mean, and that is *really* an essential part of C. – Basile Starynkevitch Jul 08 '17 at 13:04

3 Answers3

8

The fact that the program runs smoothly does not mean it is correct! Try to increase the 5 in the for loop to some extent (500000, for instance, should suffices). At some point, it will stop working giving you a SEGFAULT.

This is called Undefined Behaviour.

valgrind would also warn you about the issue with something like the following.

==16812== Invalid write of size 4
==16812==    at 0x40065E: main (test.cpp:27)
Davide Spataro
  • 7,319
  • 1
  • 24
  • 36
3

This is typical undefined behavior (UB).

You are not allowed to code like that. As a beginner, think it is a mistake, a fault, a sin, something very dirty etc.

Could anybody explain why?

If you need to understand what is really happening (and the details are complex) you need to dive into your implementation details (and you don't want to). For example, on Linux, you could study the source code of your C standard library, of the kernel, of the compiler, etc. And you need to understand the machine code generated by the compiler (so with GCC compile with gcc -S -O1 -fverbose-asm to get an .s assembler file).

See also this (which has more references).

Read as soon as possible Lattner's blog on What Every C programmer should know about undefined behavior. Every one should have read it!

The worst thing about UB is that sadly, sometimes, it appears to "work" like you want it to (but in fact it does not).

So learn as quickly as possible to avoid UB systematically.

BTW, enabling all warnings in the compiler might help (but perhaps not in your particular case). Take the habit to compile with gcc -Wall -Wextra -g if using GCC.

Notice that your program don't have any arrays. The array variable is a pointer (not an array) so is very badly named. You need to read more about pointers and C dynamic memory allocation.


 int *array = malloc(sizeof *array); //WRONG

is very wrong. The name array is very poorly chosen (it is a pointer, not an array; you should spend days in reading what is the difference - and what do "arrays decay into pointers" mean). You allocate for a sizeof(*array) which is exactly the same as sizeof(int) (and generally 4 bytes, at least on my machine). So you allocate space for only one int element. Any access beyond that (i.e. with any even small positive index, e.g. array[1] or array[i] with some positive i) is undefined behavior. And you don't even test against failure of malloc (which can happen).

If you want to allocate memory space for (let's say) 8 int-s, you should use:

int* ptr = malloc(sizeof(int) * 8);

and of course you should check against failure, at least:

if (!ptr) { perror("malloc"); exit(EXIT_FAILURE); };

and you need to initialize that array (the memory you've got contain unpredictable junk), e.g.

for (int i=0; i<8; i++) ptr[i] = 0;

or you could clear all bits (with the same result on all machines I know of) using

memset(ptr, 0, sizeof(int)*8);

Notice that even after a successful such malloc (or a failed one) you always have sizeof(ptr) be the same (on my Linux/x86-64 box, it is 8 bytes), since it is the size of a pointer (even if you malloc-ed a memory zone for a million int-s).

In practice, when you use C dynamic memory allocation you need to know conventionally the allocated size of that pointer. In the code above, I used 8 in several places, which is poor style. It would have been better to at least

  #define MY_ARRAY_LENGTH 8

and use MY_ARRAY_LENGTH everywhere instead of 8, starting with

 int* ptr = malloc(MY_ARRAY_LENGTH*sizeof(int));

In practice, allocated memory has often a runtime defined size, and you would keep somewhere (in a variable, a parameter, etc...) that size.

Study the source code of some existing free software project (e.g. on github), you'll learn very useful things.

Read also (perhaps in a week or two) about flexible array members. Sometimes they are very useful.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • I believe that beginners should be at least aware that UB exist and that they should work hard to avoid it. – Basile Starynkevitch Jul 08 '17 at 13:02
  • @PeterJ , if possible could you please explain what's wrong with **int *array = malloc(sizeof *array)**? As I mentioned I am a beginner and can learn from the mistake here. – bharadhwaj Jul 08 '17 at 16:08
  • 1
    do printf of sizeof *array and you'll get the answer – 0___________ Jul 08 '17 at 16:26
  • @bharadhwaj: I improved my answer to explain why that is wrong. – Basile Starynkevitch Jul 08 '17 at 16:26
  • This is the reason why I opted **sizeof *array** rather than **sizeof(int)**. https://stackoverflow.com/questions/17258647/why-is-it-safer-to-use-sizeofpointer-in-malloc and https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc – bharadhwaj Jul 08 '17 at 16:33
  • 1
    but you allocate only memory for one element and as I see you want more. You try to get into some details but you do not understand the basics. And what is safer depends on circumstances which I will nod discuss with you. – 0___________ Jul 08 '17 at 16:43
  • and my question was that. I only allocated for one memory, but still I was able to use more. Many people pointed me it's UB. That was my question and got answer for it anyway. Thanks! – bharadhwaj Jul 08 '17 at 16:59
  • 1
    Actually, you are not *allowed* to use more. The fact that on some particular occasion you have *apparently* been able to use more is bad luck (and that is why UB is dirty & sick). – Basile Starynkevitch Jul 08 '17 at 17:10
1

So as the programs runs smoothly without any error

That's just because you were lucky. Keep running this program and you might segfault soon. You were relying on undefined behaviour (UB), which is always A Bad Thing™.

What is the purpose of realloc()?

From the man pages:

void *realloc(void *ptr, size_t size);

The realloc() function changes the size of the memory block pointed to by ptr to size bytes. The contents will be unchanged in the range from the start of the region up to the minimum of the old and new sizes. If the new size is larger than the old size, the added memory will not be initialized. If ptr is NULL, then the call is equivalent to malloc(size), for all values of size; if size is equal to zero, and ptr is not NULL, then the call is equivalent to free(ptr). Unless ptr is NULL, it must have been returned by an earlier call to malloc(), calloc() or realloc(). If the area pointed to was moved, a free(ptr) is done.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
babon
  • 3,615
  • 2
  • 20
  • 20