Pregunta

I'm currently writing a small application to re-familiarise myself with C (it's been a while since I last wrote any) and like most people do, I've run into a memory allocation issue which I cannot figure out.

The code revolves around the setting up of different panels, windows and the associated titles. Precisely it's the allocation of memory for the title string I'm having trouble with.

The base is a structure which contains:

struct TFB_PANEL
{
    WINDOW *window;
    char *title;
};

This is typedef'd in the header file as:

typedef struct TFB_PANEL TfbPanel;

In the related C file I have the following method which initialises an array TFB of a fixed size.

int tfb_init()
{
    if (!_initialised) {
        return -1;
    }

    int i;
    for (i = 0; i < TYPE_MAX; i++) {
        TFB[i] = malloc(sizeof(TfbPanel*));
        TFB[i]->title = calloc((strlen(TYPES[i]) + 18), sizeof(char*));
        switch(i)
        {
            case A:
                sprintf(TFB[i]->title, " %s | r | h | s | t ", TYPES[i]);
                break;
            case B:
                sprintf(TFB[i]->title, " f | %s | h | s | t ", TYPES[i]);
                break;
            case C:
                sprintf(TFB[i]->title, " f | r | %s | s | t ", TYPES[i]);
                break;
            case D:
                sprintf(TFB[i]->title, " f | r | h | %s | t ", TYPES[i]);
                break;
            case E:
                sprintf(TFB[i]->title, " f | r | h | s | %s ", TYPES[i]);
                break;
        }

        TFB[i]->window = tfb_create_window(i);
    }
    return 0;
}

Now the error occurs when C is initialised. A and B get set correctly to a length of 25 characters. C on the other hand should contain 24 characters after initialisation but instead (from within tfb_create_window) I recieve

Program received signal EXC_BAD_ACCESS, Could not access memory
Reason: 13 at address 0x0000000000000000
0x00007fff930b9390 in strcmp()

Examining the stack shows TFB[C] being initialised correctly but the title element is not. This contains a null element as if I had never made the call to calloc.

Please can someone explain where I am going wrong with this or why A and B get initialised correctly but C kills the app. Everything was going beautifully until about 6am this morning, it's been down hill since then.

If it helps, tfb_create_window is defined with a call to _create_window and a call to tfb_get_title as follows:

char *tfb_get_title(int type)
{
    if (type >= TFB_MAX) {
        return (char*)NULL;
    }
    return TFB[type]->title;
}

WINDOW *tfb_create_window(int type)
{
    int height = ((LINES - WIN_OFFSET_Y) / 3);
    char *title = tfb_get_title(type);
    return _create_window(height, WIN_SIDEBAR_X, WIN_OFFSET_Y, 0, COLOUR_MAIN, title);
}

WINDOW *_create_window(int height, int width, int starty, int startx, int color, const char *title)
{
    WINDOW *window;
    window = newwin(height, width, starty, startx);
    box(window, 0, 0);
    mvwprintw(window, 0, 2, title);
    wbkgd(window, COLOR_PAIR(color));
    return window;
}
¿Fue útil?

Solución

There is a number of bugs in this code, the most severe is, that you use sizeof(Type*) instead of sizeof(Type). The later is the actual size of the object, the first is just the size of a pointer to that object (which is 8 bytes on 64 bit machines).

That means, that malloc(sizeof(TfbPanel*)) allocated too little memory for your TfbPanel, so the rest of the program has undefined behaviour.

The calloc() is wrong in the same way, but uncritical, because you allocate eight times as much memory as you need.

However, the calloc() call has another bug: you add 18 to the result of strlen(), which is the number of payload characters you add in your sprintf() calls. Which is one too few, because the result of strlen() does not include the null byte.

If you can use a POSIX-2008 compliant libc (like the glibc on linux systems), you can use asprintf() instead of sprintf(), it will automatically malloc enough space for the resulting string, avoiding any possible buffer size errors. If you can't use that function, use at least snprintf() to avoid accessing unallocated memory.

Otros consejos

If you step through with gdb to this line:

sprintf(TFB[i]->title, " f | r | %s | s | t ", TYPES[i]);

What is the values of TYPES[i]?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top