-2
//gcc 5.4.0

#include  <stdio.h>

int main(void)
{
    // Try to reserve 1 TB of memory
    int *arr = malloc(1024 * 1024 * 1024 * 1024);
    // Unfortunately most systems will actually allow this (swapping etc.)
    if (arr == NULL) {
        printf("Was not able to reserve memory!\n");
    }
    printf("Everything is ok\n");

    return 0;
}

compiler options: -Wall -std=c99 -o a.out source_file.c

The output ("Everything is ok") is what confuses me. I obviously don't have 1TB of memory - and according to the C-Reference, malloc should return NULL if, and only if(!) something goes wrong.

This is clearly not happening here. Can somebody wiser explain?

Gewure
  • 1,208
  • 18
  • 31

5 Answers5

2

1024 * 1024 * 1024 * 1024

These are integer constants of type int. When multiplied together, the operations will get carried out on type int. The result will be of type int.

Most likely an int cannot hold a value larger than 2^32 / 2 -1 = 2.14*10^9 on your system.

Meaning that you overflow a signed int and invoke undefined behavior. Anything can happen, including malloc allocating some random chunk of data.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • He does not convert it to int and malloc accept size_t which is 64 bit in a 64 bit system. – Liran Funaro May 15 '17 at 15:46
  • 2
    @LiranFunaro The type `malloc()` accepts is irrelevant. The `1024 * 1024 * 1024 * 1024` occurs before `malloc()` is called. That multiplication occurs using `int` math - which overflows on 32-bit `int` machines. [Example](http://stackoverflow.com/q/40633059/2410359). – chux - Reinstate Monica May 15 '17 at 16:12
  • But it will happen in compile time, not run time – Liran Funaro May 15 '17 at 16:16
  • 2
    @LiranFunaro With `void *malloc(size_t size);`, at run time or compile time, the type of `size_t size`, make _no difference_. `1024 * 1024 * 1024 * 1024` overflows 32-bit `int` math. Does your compiler give a warning like "warning: integer overflow in expression [-Woverflow]" with `int *arr = malloc(1024 * 1024 * 1024 * 1024);`? The conversion to `size_t` occurs _after_ the product is calculated. By then it is too late. – chux - Reinstate Monica May 15 '17 at 16:19
  • Let's stop this argument. I'll check this when I'll get home. If I'm wrong I'll eat my hat and up vote. – Liran Funaro May 15 '17 at 16:34
  • @Olaf I don't have a picture, but if you'll edit I'll be able to upvote. "warning: integer overflow in expression [-Woverflow]" – Liran Funaro May 15 '17 at 19:39
  • @LiranFunaro: WHile he compiler is correct, arguing with a tool warning or not is a bad idea. The correct way is arguing with the standard for a standardised language like C (or C++ which has the same behaviour btw.). Next time please get familiar with the standard _first_; spares us a lot of time and anger (yes, It makes me upset being downvoted unjustified - it is not about the `-2` reps). – too honest for this site May 15 '17 at 19:43
  • @Olaf The OP didn't mention any warning. I assumed he didn't have any. – Liran Funaro May 15 '17 at 19:49
  • @LiranFunaro: Read my comment carefully again! I explicitly stated you should argume by the standard, not a compiler. There is not requirement for a diagnostic! TRhat's what undefined behaviour implies. – too honest for this site May 15 '17 at 19:57
2

according to the C-Reference, malloc should return NULL if, and only if(!) something goes wrong.

This is incorrect.

malloc() returns NULL under 2 conditions:

  1. A null pointer if the memory could not be allocated.

  2. On an allocation of 0, it is implementation defined if malloc() returns NULL or a non-NULL pointer (which can not be de-referenced).

OP's 1024 * 1024 * 1024 * 1024 overflows 32-bit int math that is undefined behavior or UB. @WhozCraig hint. A typical result is a truncated product which would be 0. OP's system is then returning a non-NULL pointer for a zero byte allocation as "Was not able to reserve memory!\n" was not printed.

If OP's platform uses a rare 64-bit int, the true allocation of memory may be deferred.


OP's is also missing out on the helpful advice of the compiler. Increase warning options. Certain the below will report a warning.

// -pedantic -Wall -Wextra -std=c99  -o a.out source_file.c

int *arr = malloc(1024 * 1024 * 1024 * 1024);
// warning: integer overflow in expression [-Woverflow]

Be sure the type of expression's math meets/exceeds the type of the target. @Olaf and reasons not to use 100 * 1000 * 1000

int *arr = malloc((size_t) 1024 * 1024 * 1024 * 1024);
Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

From the malloc(3) manpage:

By default, Linux follows an optimistic memory allocation strategy. This means that when malloc() returns non-NULL there is no guarantee that the memory really is available.

This, of course, assumes you're on Linux.

Federico klez Culloca
  • 26,308
  • 17
  • 56
  • 95
-1

You don't have 1TB physical memory, but most OSs support virtual memory, backed by disk space. When malloc asks for memory the system allocates virtual memory. A part of it will be real memory the rest will be most probably a file on your HD.

Artoo
  • 101
  • 1
-2

1024 * 1024 * 1024 * 1024 most likely invokes undefined behaviour: 1024 is a int constant. Operations are done with the "largest" type of both operands, but at least int. Both operands are int, so the multiplication types are int * int -> int. The same for the following multiplications. I.e. the final result is int. On typical systems int is not more than 32 bits. The type of the parameter is irrelevant for the expression.

The result of the multiplication overflows an int (unless it is more than 41 bits), which invokes undefined behaviour. Anything can happen (often, but not guaranteed: the result is truncated, yielding 0).

To avoid this, you should use (size_t)1024 * 1024 * 1024 *1024. This will (according to conversion rules) will perform the calculations with whichever standard type size_t has on your system.

Beforehand use a static assertion to ensure size_t can represent the result:

_Static_assert(SIZE_MAX >= 1024ULL * 1024 * 1024 *1024, "size_t is too small");`

Without this the multiplication would be truncated on a too small size_t (rules are different for signed and unsigned integers!) and you end up passing 0.

According to the standard malloc(0) is implementation defined either NULL or a pointer which must not be used to access an object.

Keeping all this in mind, your code might return a non-null pointer, which must not be used for accesses. But as you just test it, it is fine, but useless.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
too honest for this site
  • 12,050
  • 4
  • 30
  • 52
  • It would probably be wiser to use `uint64_t` instead. `size_t` is not necessarily reliable, as I found out [here](http://stackoverflow.com/questions/42574890/why-is-the-maximum-size-of-an-array-too-large). – Lundin May 15 '17 at 15:27
  • 2
    @Lundin: `malloc` takes a `size_t` parameter, `uint64_t` might be too large already, so it _is_ the correct type. That's why the static assertion beforehand using a type guaranteed to be large enough. Static allocation is somewhat more problematic. These limits are defined by the implementation limits in [5.2.4.1p1](http://port70.net/~nsz/c/c11/n1570.html#5.2.4.1p1); `malloc` should return `NULL` if the parameter value is beyond what it can even handle (that's a different matter from what is available for allocation). – too honest for this site May 15 '17 at 15:34
  • He does not convert it to int and malloc accept size_t which is 64 bit in a 64 bit system. – Liran Funaro May 15 '17 at 15:45
  • 3
    @LiranFunaro - `1024 * 1024 ...` is **already** an `int` - no conversion is required to make it so. – Oliver Charlesworth May 15 '17 at 15:46
  • 1
    @LiranFunaro: `1024` is guaranteed to fit into an `int` thus is has type `int`! C does **not** convert experssion operands to the target type, but according to the standard conversion rules as defined for each **operator**. These don't automatically convert any further than `int`, resp. the type of the largest operand. I request you to remove the downvote, as it is completely unjustified. – too honest for this site May 15 '17 at 16:14
  • But the multiply will happen in compile time, which does convert the type. – Liran Funaro May 15 '17 at 16:20
  • 2
    @LiranFunaro: Please read the standard. **It will not**! And there is no difference **when** it is processed. The standard does not even guarantee when the expression is evaluated; that is an implementation detail; in fact it is unspecified, i.e. the implemntation can do what it wants even for the same expression in different places! As a sidenote: a language which made a difference here would be near unusable. But fee free to provide a reference to the standard if you still think different. Until then: The downvote ... – too honest for this site May 15 '17 at 16:21
  • @LiranFunaro: An every-day example: if you have to identical buckets of water, each 3/4 full. Fill the water from both buckets into an identical third. Now you fill this bucket into a bathtub. Does that mean you have 1.5 buckets full of water in the tub? – too honest for this site May 15 '17 at 16:28
  • The question was for this particular case (gcc 5.2). Not the standard. – Liran Funaro May 15 '17 at 16:29
  • @LiranFunaro, do not confuse the evaluation of an integer constant expression to obtain a C-language value with the evaluation of an integer expression by the preprocessor in a conditional compilation directive. The latter is performed in the largest integer type available. The former, which is what applies here, is performed according to the same rules as would apply to runtime evaluation. In particular, the constant `1024` has type `int`, and the product of any two `int`s also has type `int`. – John Bollinger May 15 '17 at 16:29
  • 1
    @LiranFunaro: 1) The question is tagged C, which implies standard. 2) gcc **does** implement the C standard. 3) There are four experienced users (not counting myself) telling you are wrong. How about doing research before discussing from a lost position? – too honest for this site May 15 '17 at 16:31
  • 2
    @LiranFunaro, if you are arguing that GCC 5.2 deviates from the standard in this regard, then you would do well to present some documentation of that. Generally speaking, arguments based on the standard are the best and most appropriate ones available, when in fact there are any that apply. Which in this case there are. – John Bollinger May 15 '17 at 16:31
  • 1
    @LiranFunaro You are confused. See C11 6.6/4. "Each constant expression shall evaluate to a constant that is in the range of representable values for its type." Meaning 4x int. And then §11 "The semantic rules for the evaluation of a constant expression are the same as for nonconstant expressions.". Try it live in GCC: `_Generic(1024*1024*1024*1024, int: printf("it's an int and UB gave value %d", 1024*1024*1024*1024), default: puts("this will never execute"));`. Which will also give you the warning "integer overflow in expression". On my GCC this UB resulted in value 0. – Lundin May 16 '17 at 08:24
  • This answer fails to mention that assigning more than a certain maximum to `malloc` invokes UB, and `malloc` does not have to return a null pointer in this case. – Pascal Cuoq May 31 '17 at 12:08
  • @PascalCuoq: One does not "assign a certain ammount to `malloc`". You pass how large a block you want and that's it. There is little use to repeat what you can find in any documentation about `malloc`: if something fails, it returns a _null pointer_. **Why** it fails does not matter. Worse: There typically if no specific size which just passes and the next larger failing, but it depends on various factors, including fragmentation. If that's not what you mean, please clarify; as stated, your comment lacks any basis. – too honest for this site May 31 '17 at 21:10
  • @PascalCuoq: As you don't added this comment to other answers which are even less extensive, there is some bad smell of harrasing or unjustified revenge. A reference to the standard supporting whatever your problem is with the answer would clarify this. – too honest for this site May 31 '17 at 21:12
  • @PascalCuoq: Non sequitur! I'm still waiting for the reference passing a too large value to `malloc` invokes UB! In fact that would break the idea of `malloc` completely. If you pass such a value, `malloc` has to return a null pointer, which I already wrote. Argumentum ad hominem is **not** an argument. – too honest for this site May 31 '17 at 23:21
  • @PascalCuoq: Just in case you don't have the standar at hand: 7.22.3p1 states: "…If the space cannot be allocated, a null pointer is returned. …" The only exception is "If the size of the space requested is zero, the behavior is implementation-defined" - I don't see `0U` being a too large value. Why the argument in the question can indeed be `0` is clearly handled in my answer. Maybe you read it **carefully** again or ask a colleague of yours. – too honest for this site May 31 '17 at 23:40
  • Please refrain from answering off-topic questions. Your answer will inhibit the question to roomba and cause manual work to the community to delete – old_timer Jul 25 '17 at 17:18
  • @old_timer: That one is indeed a problem. I should not have VtC, as this is not just a typo problem. We all err sometimes. – too honest for this site Jul 25 '17 at 17:42