Michael Hackman
Michael Hackman

Reputation: 573

segmentation fault on calloc with small allocation sizes

I've written code to simulate an election, and this works as intended, however when I modify BALLOT_SIZE to be 7, and change the definition of int v[BALLOT_SIZE] = {0,1,2,3,4,5,6} in New_Random_Ballot(), the statement struct Ballot * New_Ballot = calloc(1, sizeof(struct Ballot *)); gives me a segmentation fault. It should be noted that setting BALLOT_SIZE to 6 and modifying the definition of int v[BALLOT_SIZE] = {0,1,2,3,4,5} and all smaller settings of these variables (such as length 4, 3 or 2) executes correctly. I've removed the code that shuffles the array for clarity.

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#define BALLOT_SIZE 5
#define NUM_VOTERS 50

struct Ballot {
    int votes[BALLOT_SIZE];
};

struct Ballot * New_Random_Ballot() {

    int v[BALLOT_SIZE] = {0,1,2,3,4}; 
    struct Ballot * New_Ballot = calloc(1, sizeof(struct Ballot *));

    for (int j = 0; j < BALLOT_SIZE; j++) {
        New_Ballot->votes[j] = v[j];
    }
    return New_Ballot;
}

void Hold_Election(struct Ballot * Election[NUM_VOTERS]) {
    for (int i=0; i < NUM_VOTERS; i++) {
        Election[i] = New_Random_Ballot();
    }   
}

int main() {
    struct Ballot * Election[NUM_VOTERS];
    Hold_Election(Election);

    //free memory
    for (int i=0; i < NUM_VOTERS; i++) {
        free(Election[i]);
    }
    return 0;
}

I've tried catching the return value of the calloc call and checking it against NULL but before it is able to check the return value, it faults (in this example, BALLOT_SIZE is 7):

struct Ballot * New_Random_Ballot() {

    int v[BALLOT_SIZE] = {0,1,2,3,4,5,6}; 
    struct Ballot * New_Ballot = calloc(1, sizeof(struct Ballot *));

    if (New_Ballot == NULL) {
        perror("could not allocate ballot\n");
    }

    for (int j = 0; j < BALLOT_SIZE; j++) {
        New_Ballot->votes[j] = v[j];
    }
    return New_Ballot;
}

Upvotes: 0

Views: 158

Answers (1)

Micha&#235;l Roy
Micha&#235;l Roy

Reputation: 6481

The problem is here:

calloc(1, sizeof(struct Ballot *));  // Ballot* is a pointer of size 4 or 8

Replace with:

calloc(1, sizeof(struct Ballot));  // struct Ballot is a structure of some size

As correctly pointed out by @Olaf below, pointer sizes may depend on the CPU. The original answer above need a bit of clarification.

The ISO standard guarantees this equality, for any data type, struct or union AnyType:

sizeof(AnyType*) == sizeof(void*)

The size of void* is defined by the CPU architeture. On 32-bit platforms, a pointer is 4 bytes long, on 64-bit platforms, that's 8 bytes long.

On 16 bits platforms, such as the i386, pointers will generally be 16-bits long, but there are also far pointers and sizeof(void far*) is usually 32-bits (16 bits for segment, 16 bits for address).

On 8 bit platforms, such as the i8051, logic would dictate that pointers be 8 bit wide, but that is not very useful, so pointers are usually 16-bit wide on 8 bit CPUs. There are usually no segments associated with pointers on 8bit cpus, and it is the responsibility of the programmer to manage far memory. i.e. changing the visible extended memory page, by setting the appropriate control registers.

Upvotes: 2

Related Questions