tags:

views:

383

answers:

8

on linux, with 16 GB of ram, why would the following segfault:

#include    <stdlib.h>

#define N    44000

int main(void) {
    long width = N*2 - 1;
    int * c = (int *) calloc(width*N, sizeof(int));
    c[N/2] = 1;
    return 0;
}

According to gdb the problem is from c[N/2] = 1 , but I do not know the reason

thanks

+6  A: 

It's probably because the return value of calloc was NULL.

The amount of physical RAM in your box does not directly correlate to how much memory you can allocate with calloc/malloc/realloc. That is more directly determined by the remaining amount of Virtual Memory available to your process.

JaredPar
Based on my calculations (assuming `int` is 4 bytes) you're asking the OS for ~14GB of contiguous space, which I could see failing in this case.
fbrereto
2.6GB, actually, but still enough to fail.
bbum
I got 14.4241GB in my calculations (assuming 4bytes int too).
Johannes Schaub - litb
I didn't calculate, I coded. :) `printf("%lu\n", (width*N) * sizeof(int));` yielded 2602922112.
bbum
87999 * 4 bytes = 343 KB
Will Bickford
But, compiled for 64 bit, it is 15487824000 -- 14.4GB, thus begging the question of why Linux's calloc() can't return a 14GB chunk o' memory.
bbum
I never use calloc I must be mis-reading something.
Will Bickford
@bbum, you're doing this on a 32-bit platform, where `unsigned long` is 32-bit. It simply overflowed. The correct number is indeed ~14Gb.
Pavel Minaev
You missed that he is multiplying by N twice; once in calculating width and again in the calloc call.
bbum
@Will, it's `(44000 * 2 - 1) * 44000 * 4`.
Pavel Minaev
Yup-- I screwed up the overflow. Bad coder, no coffee. Wait. More coffee.
bbum
wow I really can't read apparently - I read width*N as width. /bonk-self
Will Bickford
+2  A: 

You are asking for 2.6GB of RAM (No, you aren't -- you are asking for 14GB on 64 bit... 2.6GB overflowed cutoff calculation on 32 bit). Apparently, Linux's heap is utilized enough that calloc() can't allocate that much at once.

This works fine on Mac OS X (both 32 and 64 bit) -- but just barely (and would likely fail on a different system w/different dyld shared cache & frameworks).

And, of course, it should work dandy under 64 bit on any system (even the 32 bit version with the bad calculation worked, but only coincidentally).

One more detail; in a "real world app", the largest contiguous allocation will be vastly reduced as the complexity and/or running time of the application increases. The more of the heap that is used, the less contiguous space there is to allocate.

bbum
+5  A: 

Your calculation overflows the range of a 32-bit signed integer, which is what "long" may be. You should use size_t instead of long. This is guaranteed to be able to hold the size of the largest memory block that your system can allocate.

Tyler McHenry
The largest memory block a system can allocate is never a round power of 2 number. Shared memory, library mappings, file mappings, runtime metadata, etc... will always occupy some subset of allocations.
bbum
Read my answer. I did not say "The system is able to allocate blocks of any size representable in a size_t". I said "size_t can represent any size block that the system is able to allocate." These are quite different statements.
Tyler McHenry
In particular I am suggesting that his program is crashing not because he asked for too much RAM but because he inadvertently passed a negative number as the argument to calloc.
Tyler McHenry
Regardless of wether there's shared libs occupying the space, the size will eventually be a power of 2. If he's on a 32 bit linux he will 1. overflow the argument to calloc 2. run out of address space - calloc will fail even if it didn't overflow
nos
On 64-bit Linux (and all other Unices), GCC follows the LP64 model - that is, both pointers and `long` are 64-bit. See http://gcc.fyxm.net/summit/2003/Porting%20to%2064%20bit.pdf
Pavel Minaev
@Pavel: and size_t, which is the parameter to calloc.
Steve Jessop
+2  A: 

you might want to change the #define to:

#define N    44000L

Just to make sure the math is being done at long resolution. You maybe generating a negative number for the calloc.

Calloc maybe failing and returning null which would cause the problem.

Tony Lambert
That won't make any difference unless int is 16 bit, which seems unlikely. 44000*2-1 fits in 32 bit int, then is assigned to a long "width", then width*N is calculated in long since that's the larger type, then calloc will use size_t internally.
Steve Jessop
+4  A: 

You're allocating around 14-15Gb memory, and for whatever reason the allocator cannot give you that much at the moment- thus calloc returns NULL and you segfault as you're dereferencing a NULL pointer.

Check if calloc returns NULL.

That's assuming you're compiling a 64 bit program under a 64 bit linux, if you're doing something else - you might overflow the calculation to the first argument to calloc if a long is not 64 bits on your system. Try e.g.

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

#define N    44000L

int main(void)
{
    size_t width = N * 2 - 1;
    printf("Longs are %lu bytes. About to allocate %lu bytes\n",
           sizeof(long), width * N * sizeof(int));
    int *c = calloc(width * N, sizeof(int));
    if (c == NULL) {
        perror("calloc");
        return 1;
    }
    c[N / 2] = 1;
    return 0;
}
nos
it does return null, is there a way to get around that?
adk
+1  A: 

Create a 14Gb file, and memmap it.

Graham Toal
+1  A: 

Dollars to donuts calloc() returned NULL because it couldn't satisfy the request, so attempting to deference c caused the segfault. You should always check the result of *alloc() to make sure it isn't NULL.

John Bode
A: 

width * N is 87999 * 44000 = 3871956000. As an int is 8 bytes this takes up 28.85 GB. This is more than the available physical RAM (16 GB). calloc fails allocating the memory, returns the null pointer and the pointer arithmetic and dereference operations are illegal, giving a seg-fault in this particular case.

Peter Mortensen
no I wanted width*N
adk
OK, I have removed that part. What do you need so many elements for? Can't you do processing in smaller chunks?
Peter Mortensen