Is malloc() initializing allocated array to zero?
Asked Answered
N

9

22

Here is the code I'm using:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *arr;
    int sz = 100000;
    arr = (int *)malloc(sz * sizeof(int));

    int i;
    for (i = 0; i < sz; ++i) {
        if (arr[i] != 0) {
            printf("OK\n");
            break;
        }
    }

    free(arr);
    return 0;
}

The program doesn't print OK. malloc isn't supposed to initialize the allocated memory to zero. Why is this happening?

Nerland answered 26/7, 2017 at 10:12 Comment(18)
The contents of the memory is indeterminate. It might be seemingly random. Or it might be all zeroes. You simply do not know beforehand.Nonstop
"The program doesn't print OK. malloc isn't supposed to initialize the allocated memory to zero. " - It's also not supposed to guarantee it isn't all zero. Either way, by reading indeterminate values, your program has undefined behavior. You can't expect anything.Joub
Also, debug-builds might actually cause memory you allocate, or even local variables, to be initialized. To make memory and pointer problems easier to detect.Nonstop
In Visual C++ for example, the memory region allocated is always set to 0xCDCDCDCD in Debug mode and is random in Release mode. It is an implementation defined behavior and every compiler does what it wants with the allocated memory region.Paleoclimatology
@StoryTeller "by reading indeterminate values, your program has undefined behavior" Not in C, only in C++ is this unconditional. In C it might be UB depending on if it's a "trap value" IIRC.Nonstop
@Someprogrammerdude - Whether or not a type has trap values isn't defined by the C standard. And if a trap value is read the behavior is explicitly undefined. So I do believe it's UB all around.Joub
@StoryTeller You probably got a point there.Nonstop
@Someprogrammerdude but irrespective of whether it has a trap representatio or not, reading uninitialized variables is UB in C, right?Reiners
@StoryTeller but irrespective of whether the type has a trap representation or not, reading uninitialized value is UB in C, right?Reiners
@AjayBrahmakshatriya - UB in the language lawyer sense or just the "don't do this if you want predictability" sense? In the first sense it depends. If the standard guarantees that variable is merely unspecified, then that's not UB, only unpredictable. Your program still has to behave a certain way. If the standard says it's indeterminate, than that's entirely UB, the compiler can do whatever.Joub
@StoryTeller okay, so you mean the standard guarantees that if I do 2 reads on the uninitialized value it will read the same both the times? I think this should also not be guaranteed since a compiler could optimize the first read out.Reiners
@AjayBrahmakshatriya - I'm afraid I presented my argument poorly. For a plain local variable, it's always declared as indeterminate if uninitialized, IIRC. But for a block of memory you get from malloc or such, semantics may vary. For instance, various sources of true entropy give you unspecified values, not indeterminate ones. So your program stays well-formed and its behavior defined.Joub
@StoryTeller Oh! So you mean the program int a; int b = a; int c = a; if (b==c) printf("yes");else printf("no"); is guaranteed to print "yes" by the standards?Reiners
@AjayBrahmakshatriya - Eh, no. a is uninitialized. Its value indeterminate. The behavior is UB. If you did something like int *a = source_of_entropy(); int b = *a; ... then it would not be UB, if source_of_entropy was well behaved with regard to the contents of the memory it returns.Joub
@AjayBrahmakshatriya; but irrespective of whether the type has a trap representation or not, reading uninitialized value is UB in C, right? No. Reading uninitialized variable is not always UB. It can be UB if the type has trap representation.Engle
@StoryTeller Ah! Now I get you. But malloc is not well behaved with regards to the content of the memory it returns, right? (purely by standards not implementation). So it should be UB?Reiners
@AjayBrahmakshatriya - You got it. It's explicitly indeterminate.Joub
@StoryTeller Great! Thanks for clarifying the difference between unspecified and indeterminate values and bearing with the doubts!Reiners
M
16

The man page of malloc says:

The malloc() function allocates size bytes and returns a pointer to the allocated memory. The memory is not initialized. If size is 0, then malloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().

So malloc() returns uninitialized memory, the contents of which is indeterminate.

 if (arr[i] != 0)

In your program, You have tried to access the content of a memory block, which is invoked undefined behavior.

Maebashi answered 26/7, 2017 at 10:15 Comment(1)
The last paragraph is not correct. Merely using an indeterminate value does not necessarily lead to undefined behaviour. It can be very erratic though. It does become undefined behaviour if you pass it to a library function.Glassful
B
24

malloc isn't supposed to initialize the allocated memory to zero. Why is this happening?

This is how it was designed more than 40 years ago.

But, at the same time, the calloc() function was created that initializes the allocated memory to zero and it's the recommended way to allocate memory for arrays.

The line:

arr = (int *)malloc(sz * sizeof(int));

Should read:

arr = calloc(sz, sizeof(int));

If you are learning C from an old book it teaches you to always cast the value returned by malloc() or calloc() (a void *) to the type of the variable you assign the value to (int * in your case). This is obsolete, if the value returned by malloc() or calloc() is directly assigned to a variable, the modern versions of C do not need that cast any more.

Borgia answered 26/7, 2017 at 10:20 Comment(3)
GCC documentation: the cast is necessary in contexts other than assignment operators.Paleoclimatology
@RyanB. - GCC isn't being C standard compliant here. void* is implicitly convertible to all object types. No cast required.Joub
@StoryTeller-UnslanderMonica this is obviously really late, but I assume that Ryan B. is referencing pointer arithmetic, which would require you to cast to a type. (unless you're using the void* arithmetic GNU extension which isn't standard and I don't particularly like)Clench
M
16

The man page of malloc says:

The malloc() function allocates size bytes and returns a pointer to the allocated memory. The memory is not initialized. If size is 0, then malloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().

So malloc() returns uninitialized memory, the contents of which is indeterminate.

 if (arr[i] != 0)

In your program, You have tried to access the content of a memory block, which is invoked undefined behavior.

Maebashi answered 26/7, 2017 at 10:15 Comment(1)
The last paragraph is not correct. Merely using an indeterminate value does not necessarily lead to undefined behaviour. It can be very erratic though. It does become undefined behaviour if you pass it to a library function.Glassful
E
11

malloc isn't supposed to initialize the allocated memory to zero.

Memory allocated by malloc is uninitialised. Value at these locations are indeterminate. In this case accessing that memory can result in an undefined behavior if the value at that location is to be trap representation for the type.

n1570-§6.2.6.1 (p5):

Certain object representations need not represent a value of the object type. If the stored value of an object has such a representation and is read by an lvalue expression that does not have character type, the behavior is undefined. [...]

and footnote says:

Thus, an automatic variable can be initialized to a trap representation without causing undefined behavior, but the value of the variable cannot be used until a proper value is stored in it.

Nothing good can be expected if the behavior is undefined. You may or may not get expected result.

Engle answered 26/7, 2017 at 10:14 Comment(0)
P
10

From the C Standard 7.22.3.4:

Synopsis

#include <stdlib.h>
void *malloc(size_t size);

Description

The malloc function allocates space for an object whose size is specified by size and whose value is indeterminate.

The value is indeterminate. So, every compiler is free to behave how it wants. For example, in Microsoft Visual C++, in Debug mode, the area of allocated memory by malloc() is all set to 0xCDCDCDCD and when in Release mode it is random. In modern versions of GCC, it is set to 0x000000 if you don't enable code optimizations, and random otherwise. I don't know about other compilers, but you get the idea.

Paleoclimatology answered 26/7, 2017 at 10:32 Comment(1)
Can you elaborate on "In modern versions of GCC, it is set to 0x000000 if you don't enable code optimizations, and random otherwise."? Possibly include a reference?Muniz
Y
4

void *malloc(size_t size) is just supposed to keep aside the specified amount of space. That's all. There is no guarantee as to what will be present in that space.

Quoted from the man pages:

The malloc() function allocates size bytes and returns a pointer to the allocated memory. The memory is not initialized. If size is 0, then malloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().

Apart from calloc() you can use the memset() function to zero out a block of memory.

Yu answered 26/7, 2017 at 10:15 Comment(0)
T
3

The first time you call malloc(3), it asks to the operating system to get memory for the heap space.

For security reasons, the unix/linux kernel (and many other operating systems) in general zeroes the page contents that is to be given to a process, so no process can access that memory's previous contents and do nasty things with it (like searching for old passwords, or similar things).

If you do several allocations and deallocations of memory, when the malloc module reuses the previous memory, you'll see garbage coming from malloc(3).

Touchy answered 27/7, 2017 at 8:36 Comment(0)
I
2

Zero's are assigned to page contents at first time in linux kernel.

Below program explains the memory initialisation difference in malloc and calloc:

#include<stdio.h>
#include<stdlib.h>

#define SIZE 5

int main(void) {
    int *mal = (int*)malloc(SIZE*sizeof(int));
    int *cal = (int*)calloc(SIZE, sizeof(int));

    mal[4] = cal[4] = 100;

    free(mal); free(cal);

    mal = (int*)malloc(SIZE*sizeof(int));
    cal = (int*)calloc(SIZE, sizeof(int));

    for(int i=0; i<SIZE; i++) {
        printf("mall[%d] = %d\n", i, mal[i]);
    }
    for(int i=0; i<SIZE; i++) {
        printf("call[%d] = %d\n", i, cal[i]);
    }
}
Impinge answered 28/10, 2019 at 7:51 Comment(0)
S
0

I use malloc to allocate everything from the heap(dynamic memory) while i should use calloc instead nowaday , and memset is great for filling you memory segment with any chosen character.

Compile and work great with GCC:

#include <stdio.h>
#include <stdlib.h>
#include <mem.h>

int main() 
{

  int *arr;
    int sz = 100000;
  arr = (int *)malloc(sz * sizeof(int));

    memset(arr, 0, sz*sizeof(int) );


   int i;
    for (i = 0; i < sz; ++i) {
        if (arr[i] != 0) {
          printf("OK\n");
         break;
       }
 }

 free(arr);
 return 0;
}

ref: http://www.cplusplus.com/reference/cstring/memset/

Shimberg answered 23/9, 2020 at 17:22 Comment(2)
Why not use calloc if you set the bytes of the memory block pointed by arr to 0?Midiron
I'd rather use calloc myself because it allow me more flexibility like dynamically changing the allocated block size but the point here was to answer the author's question accurately with little changes to the original code.Shimberg
C
-1

well, the value is not initialized in malloc. And it does print "OK" in VS Code.

so in VS Code, the output is : "OK" followed by a garbage value.

in a web based compiler (here's the link : https://www.programiz.com/c-programming/online-compiler/ ),

the output was "LOL" followed by '0'

so some compilers do initialize the value..but actually the value in malloc is not intialized. so it will return a garbage value when printed as in the above example in VS Code.

int main()
{
    int *arr;
    int sz = 100000;
    arr = (int *)malloc(sz * sizeof(int));

    int i;
    for (i = 0; i < sz; i++)
    {
        if (arr[i] != 0)
        {
            printf("OK\n");
            break;
        }
        else
        {
            printf("LOL \n");
            break;
        }
    }

    printf("%d", arr[0]);

    free(arr);
Charlottecharlottenburg answered 22/5, 2022 at 11:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.