Search code examples
cmalloc

Segfault on assigning member of triple pointer/array of arrays


I'm trying to make an array of arrays of struct pointers, but when trying to set the first array pointer, my program exits with a segfault. I can't really see anything wrong with my code, but that's probably because I'm the one who wrote it.

The code block below is a small compilable snippet to allow the offending function, palettesToColorArray, to "run." GDB reports that the segfault is triggered on the line reading palArray[i/4][i%4] = tmpColor;.


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

typedef struct color_t {
    uint8_t red;
    uint8_t grn;
    uint8_t blu;
    uint8_t alp;
} color_t;

typedef struct pSpr_t{
    uint8_t  verMaj;
    uint8_t  verMin;
    uint8_t  palnum;
    uint8_t  size_x;
    uint8_t  size_y;
    uint8_t* sprdat;
    uint8_t* paldat;
} pSpr_t;

color_t* createColor(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha){
    color_t* newColor = malloc(sizeof(color_t));
    if(newColor == NULL){
        puts("\n!!> ERR: Memory allocation failure in createColor");
        exit(1);
    }
    newColor->red = red;
    newColor->grn = green;
    newColor->blu = blue;
    newColor->alp = alpha;
    return newColor;
}

color_t* createColorNoAlpha(uint8_t red, uint8_t green, uint8_t blue){
    color_t* color = createColor(red, green, blue, 255);
    return color;
}

color_t*** palettesToColorArray(pSpr_t* sprObj){
    int number = ((sprObj->palnum & 0xF0) >> 4) + 1;
    int colors = (sprObj->palnum & 0x0F) + 1;
    color_t*** palArray= malloc(sizeof(***palArray) * number * colors);
    if(palArray == NULL){
        puts("\n!!> ERR: Memory allocation failure in palettesToColorArray");
        exit(1);
    }
    for(int i = 0; i < (number * colors * 3); i++){
        color_t* tmpColor = createColorNoAlpha(sprObj->paldat[i], sprObj->paldat[i+1], sprObj->paldat[i+2]);
        palArray[i/4][i%4] = tmpColor;
    }
    return palArray;
}

uint8_t sDat[] = {
    0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
    0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF,
    0x00, 0xFF, 0x00, 0x01, 0x00, 0x01, 0x00, 0xFF,
    0xFF, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00,
    0xFF, 0x00, 0x02, 0x02, 0x02, 0x02, 0x03, 0x00,
    0xFF, 0x00, 0x02, 0x02, 0x02, 0x03, 0x03, 0x00,
    0xFF, 0xFF, 0x00, 0x02, 0x02, 0x02, 0x00, 0xFF,
    0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF
};

uint8_t pDat[] = {
    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xA8, 0xA8, 0xA8, 0x54, 0x54, 0x54,
    0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x54, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x54, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x54,
    0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xA8, 0xA8, 0x00, 0x54, 0x54, 0x00,
    0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xA8, 0x00, 0xA8, 0x54, 0x00, 0x54,
    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xA8, 0xA8, 0x00, 0x54, 0x54
};

pSpr_t data = {0x00, 0x00, 0x63, 0x08, 0x08, sDat, pDat};

int main(){
    color_t*** colArray = palettesToColorArray(&data);
    int palCount = ((data.palnum & 0xF0) >> 4) + 1;
    int colCount = (data.palnum & 0x0F) + 1;
    for(int i = 0; i < palCount; i++){
        printf("Palette %i:\n", i);
        for(int j = 0; j < colCount; j++){
            printf("Color %i: %x %x %x\n", j, colArray[i][j]->red, colArray[i][j]->grn, colArray[i][j]->blu);
        }
    }
    free(colArray);
    return 0;
}

I've tried shifting numbers around, but nothing I do can seem to squash the segfault. If there's another, more simple way to accomplish what I'm trying to do, please tell me, because while I can work with normal pointers just fine, this triple pointer is pushing my limits.


Solution

  • You cannot create an indexable array by throwing a bunch of asterisks in the type and allocating some space. The compiler has zero information on the length of a row.

    *** means a pointer to a pointer to a pointer. To actually create an allocated two-dimensional array of pointers, you would need to first allocate a vector of row pointers, where each row pointer is filled in with a pointer to an allocation of a row of pointers. In this case, you would allocate space for seven pointers for the vector of row pointers, and then space for four pointers seven times. Or for the latter allocate space for 28 pointers, and compute the row offsets into that to fill in the vector of row pointers. Then palArray[x][y] would use x to find the row pointer in the first allocation, and the y would be used to get the pointer in the row.

    This works completely differently than a declared array, where color_t* palArray[7][4] would process palArray[x][y] by multiplying x by the length of a row of seven pointers, adding y times the length of a pointer, and then getting a pointer from palArray plus that offset. There are no pointers to pointers (to pointers) in this case. Instead there is a single chunk of memory for 28 pointers pointed to by palArray.

    Why do you have a * 3 in number * colors * 3? Now you want three times number rows instead of number rows? Even your erroneous initialization of palArray provides no indication of that.

    By the way, it seems rather silly to have a pointer (eight bytes) to an allocated pixel (four bytes), wasting time allocating and later freeing all of those. Just put the pixels in the array instead of pointers to pixels.