I'm very confused about malloc() and calloc() on C

81,590

Solution 1

You can't assign memory to an array. An array has a fixed size, for the whole of its lifespan. An array can never be null. An array is not a pointer.

malloc returns the address to a memory block that is reserved for the program. You can't "assign" that (being the memory block) to an array, but you can store the address of this memory block in a pointer: luckily, array subscription is defined through pointers - so you can "use pointers like arrays", e.g.

int *ptr = malloc(5 * sizeof *ptr);
ptr[2] = 5; // access the third element "of ptr"
free(ptr); // always free at the end

When you declare an array without a size (i.e. array[]), it simply means the size of the array is determined from the initializer list. That is

int array[] = {1, 2, 3, 4, 5}; // is equal to
int array[5] = {1, 2, 3, 4, 5};

Trying to declare an array without a size and without an initializer is an error.


The code pthread_t tid[MAX_OPS]; declares an array named tid of type pthread_t and of size MAX_OPS.

If the array has automatic storage (i.e. declaration is inside a function and not static, not global), then each of the arrays elements has indeterminate value (and it would cause undefined behavior trying to read such value). Luckily, all that the function call does is that it takes the address of the first element of the array as the first parameter, and probably initializes it (the element) inside the function.


The difference of calloc and malloc is that the memory block that calloc returns is initialized to zero. That is;

int *ptr = calloc(5, sizeof *ptr);
// is somewhat equal to
int *ptr = malloc(5 * sizeof *ptr);
memset(ptr, 0, 5 * sizeof *ptr);

The difference between

int *ptr = malloc(5 * sizeof *ptr);
// and
int array[5];

is that array has automatic storage, (is stored on stack), and is "released" after it goes out of scope. ptr, however, (is stored on heap), is dynamically allocated and must be freed by the programmer.

Solution 2

You are missing three very basic and tighten (and misleading!) C topics:

  • the difference between array and pointers
  • the difference between static and dynamic allocation
  • the difference from declaring variables on the stack or on the heap

If you write int array[] = malloc(3*sizeof(int)); you would get a compilation error (something like 'identifier' : array initialization needs curly braces).

This means that declaring an array allows only static initialization:

  • int array[] = {1,2,3}; that reserves 3 contiguous integers on the stack;
  • int array[3] = {1,2,3}; which is the same as the previous one;
  • int array[3]; that still reserves 3 contiguous integers on the stack, but does not initialize them (the content will be random garbage)
  • int array[4] = {1,2,3}; when the initializer list doesn't initialize all the elements, the rest are set to 0 (C99 §6.7.8/19): in this case you'll get 1,2,3,0

Note that in all these cases you are not allocating new memory, you are just using the memory already committed to the stack. You would run in a problem only if the stack is full (guess it, it would be a stack overflow). For this reason declaring int array[]; would be wrong and meaningless.

To use malloc you have to declare a pointer: int* array.

When you write int* array = malloc(3*sizeof(int)); you are actually doing three operations:

  1. int* array tells the compiler to reserve a pointer on the stack (an integer variable that contains a memory address)
  2. malloc(3*sizeof(int)) allocates on the heap 3 contiguous integers and returns the address of the first one
  3. = assigns copies that return value (the address of the first integer you have allocated) to your pointer variable

So, to come back to your question:

pthread_t tid[MAX_OPS];

is an array on the stack, so it doesn't need to be allocated (if MAX_OPS is, say, 16 then on the stack will be reserved the number of contiguous bytes needed to fit 16 pthread_t). The content of this memory will be garbage (stack variables are not initialized to zero), but pthread_create returns a value in its first parameter (a pointer to a pthread_t variable) and disregards any previous content, so the code is just fine.

Solution 3

C offers static memory allocation as well as dynamic- you can allocate arrays off the stack or in executable memory (managed by the compiler). This is just the same as how in Java, you can allocate an int on the stack or an Integer on the heap. Arrays in C are just like any other stack variable- they go out of scope, etc. In C99 they can also have a variable size, although they cannot be resized.

The main difference between {} and malloc/calloc is that {} arrays are statically allocated (don't need freeing) and automatically initialized for you, whereas malloc/calloc arrays must be freed explicitly and you have to initialize them explicitly. But of course, malloc/calloc arrays don't go out of scope and you can (sometimes) realloc() them.

Share:
81,590
bluehallu
Author by

bluehallu

Full stack polyglot software engineer

Updated on January 07, 2023

Comments

  • bluehallu
    bluehallu over 1 year

    I've always programmed in Java, which is probably why I'm so confused about this:

    In Java I declare a pointer:

    int[] array
    

    and initialize it or assign it some memory:

    int[] array = {0,1,0}
    int[] array = new int[3]
    

    Now, in C, it's all so confusing. At first I thought it was as easy as declaring it:

    int array[]
    

    and initializing it or assigning it some memory:

    int array[] = {0,1,0}
    int array[] = malloc(3*sizeof(int))
    int array[] = calloc(3,sizeof(int))
    

    Unless I'm wrong, all of the above is equivalent Java-C, right?

    Then, today I met a code in which I found the following:

    pthread_t tid[MAX_OPS];
    

    and some lines below, without any kind of initialization...

    pthread_create(&tid[0],NULL,mou_usuari,(void *) 0);
    

    Surprisingly (at least to me), the code works! At least in Java, that would return a nice "NullPointerException"!

    So, in order:

    1. Am I correct with all of the Java-C "translations"?

    2. Why does that code work?

    3. Is there any difference between using malloc(n*sizeof(int)) and calloc(n,sizeof(int))?

    Thanks in advance