cstructmallocansi-clabwindows

ANSI C Memory allocation for pointer to struct throws non-fatal run-time error


I am using ANSI C code, generated from a code generator that produces several layers of nested struct as well as function prototypes with argument lists that use pointers to the top layer struct for passing or accessing data located in the inner mmost struct.

Because the function prototypes pass pointers to struct, the application must allocate memory to access or write data to the inner most struct members. I am seeing the problem when attempting to allocate memory for the pointer to the second nested struct.

The actual error message I get is a non-fatal, run-time: "Not enough space for casting expression to 'pointer to struct data'."

I am not explicitly casting anything, so I suspect the implementation of malloc() may have an assert that generates the message when it sees some condition. The wording of this error may be specific to my environment (I am using LabWindows/CVI) but I would be interested in hearing of results of other ANSI C compilers as well.

Here is a simplified, complete, code snippet that should compile, build and run (up to the error location, which is commented in-line)

I would appreciate comments on the cause of my error, and suggestions on how to fix it.

#include <ansi_c.h> //specific to LabWindows/CVI - change as needed for your environment

struct request
{
    struct data *pData; 
};

struct data
{
    char *wsDate;   
    char *wsDuration;
    char *wsFailures;
    int __sizeM_Details;    
    struct details *M_Details;
};


struct details
{
    char *wsStep;   
    char *wsTestDesc;   
    char *wsLowLim; 
};

typedef struct request REQUEST;  // from mtdf function prototype request argument (4)
typedef struct details DETAILS; // member of REQUEST - _ns1__MTDFData_MTDFDetail  

void allocate(REQUEST *a, int numRecords);
void freemem(REQUEST *c, int numRecords);

int main(void)
{
    REQUEST b, *pB;

    pB = &b;

    allocate(pB, 10);
    freemem(pB, 10);
    return 0;   
}

void allocate(REQUEST *c, int numRecords)
{
    DETAILS m_Param;
    REQUEST b;
    struct data d;

    size_t size_c = sizeof(c);
    c = malloc(size_c); //4 bytes

    size_t size_c_data = sizeof(c->pData);
    c->pData = malloc(size_c_data);  //Breaks here - this is just a pointer, 
                                     //should it not just allocate 4 bytes
                                     //and continue?
    //  Actual error message: 
    //  "Not enough space for casting expression to 'pointer to struct data'."
    c->pData->wsDate = calloc(80, sizeof(char));

    c->pData->__sizeM_Details = numRecords;
    c->pData->M_Details = calloc((numRecords + 1) , sizeof(m_Param)); 

}

void freemem(REQUEST *c, int numRecords)
{
    free(c->pData->M_Details);
    free(c->pData->wsDate);
    free(c->pData);
    free(c);
}

Solution

  • There's several fundamental problems, here:

    1. In allocate(), all that memory you're malloc()ing is being lost at the end of your function, because you're assigning it to a local variable, c, which gets destroyed at the end of your function. You never use the address of the struct with automatic storage duration that you pass into the function. If you're passing the address of an object with automatic storage duration, then you should malloc() memory for the members, but not for the struct itself, since it obviously already has memory.

    2. Then, in freemem(), you attempt to free() the memory associated with b, which is a struct with automatic storage duration. You can only free() memory that you've dynamically allocated.

    3. You have a curious comment in allocate(), "this is just a pointer, should it not just allocate 4 bytes and continue?". If you're on a system with 32 bit pointers, then that is indeed what you allocated, but c->pData is a pointer to struct data which looks like it needs 28 bytes on a 32 bit machine, so you should be allocating a lot more than 4 bytes for it. Lines like c->pData->wsDate = ... seem to indicate that you're well aware it's a pointer to a struct data, so it's really unclear why you think you should only be allocating 4 bytes. When you allocate memory for an ANYTHING * to point to, then you need to allocate enough memory for an ANYTHING, not for an ANYTHING *, i.e. enough memory for the thing it's going to point to. The fact that you're trying to assign the memory to your pointer in the first place proves that you already have the memory for your pointer, otherwise you wouldn't be able to do that (providing that you haven't messed up some previous allocation, of course).

    4. You never check the return from malloc() and calloc(), and you should.

    5. Names beginning with a double underscore are always reserved for the implementation, so you should call __sizeM_Details something else.

    6. sizeof(char) is 1 by definition, so there's never any need to use it.

    7. It's unclear why you're allocating memory for numRecords + 1 of your struct details, rather than just numRecords as would seem intuitive. Perhaps you're looking to set that last one to NULL as a sentinel value, but if you're already storing the number of records in your struct, then this isn't really necessary.

    Here's what your code ought to look like:

    #include <stdio.h>
    #include <stdlib.h>
    
    struct request {
        struct data * pData; 
    };
    
    struct data {
        char * wsDate;   
        char * wsDuration;
        char * wsFailures;
        int sizeM_Details;    
        struct details * M_Details;
    };
    
    struct details {
        char * wsStep;   
        char * wsTestDesc;   
        char * wsLowLim; 
    };
    
    typedef struct request REQUEST;
    typedef struct details DETAILS;
    
    void allocate(REQUEST * c, const int numRecords);
    void freemem(REQUEST * c);
    
    int main(void)
    {
        REQUEST b;
        allocate(&b, 10);
        freemem(&b);
        return 0;   
    }
    
    void allocate(REQUEST * c, const int numRecords)
    {
        if ( !(c->pData = malloc(sizeof *c->pData)) ) {
            perror("couldn't allocate memory for c->pData");
            exit(EXIT_FAILURE);
        }
    
        if ( !(c->pData->wsDate = calloc(80, 1)) ) {    
            perror("couldn't allocate memory for c->pData->wsDate");
            exit(EXIT_FAILURE);
        }
    
        if ( !(c->pData->M_Details = calloc(numRecords + 1,
                                            sizeof(*c->pData->M_Details))) ) {
            perror("couldn't allocate memory for c->pData->M_Details");
            exit(EXIT_FAILURE);
        }
    
        c->pData->sizeM_Details = numRecords;
    }
    
    void freemem(REQUEST * c)
    {
        free(c->pData->M_Details);
        free(c->pData->wsDate);
        free(c->pData);
    }
    

    If allocating automatic storage for b was a mistake, and you really do want to dynamically allocate everything, including your struct request, then it should look like this:

    #include <stdio.h>
    #include <stdlib.h>
    
    struct request {
        struct data * pData; 
    };
    
    struct data {
        char * wsDate;   
        char * wsDuration;
        char * wsFailures;
        int sizeM_Details;    
        struct details * M_Details;
    };
    
    struct details {
        char * wsStep;   
        char * wsTestDesc;   
        char * wsLowLim; 
    };
    
    typedef struct request REQUEST;
    typedef struct details DETAILS;
    
    REQUEST * allocate(const int numRecords);
    void freemem(REQUEST * c);
    
    int main(void)
    {
        REQUEST * b = allocate(10);
        freemem(b);
        return 0;   
    }
    
    REQUEST * allocate(const int numRecords)
    {
        REQUEST * c = malloc(sizeof *c);
        if ( !c ) {
            perror("couldn't allocate memory for c");
            exit(EXIT_FAILURE);
        }
    
        if ( !(c->pData = malloc(sizeof *c->pData)) ) {
            perror("couldn't allocate memory for c->pData");
            exit(EXIT_FAILURE);
        }
    
        if ( !(c->pData->wsDate = calloc(80, 1)) ) { 
            perror("couldn't allocate memory for c->pData->wsDate");
            exit(EXIT_FAILURE);
        }
    
        if ( !(c->pData->M_Details = calloc(numRecords + 1,
                                            sizeof(*c->pData->M_Details))) ) {
            perror("couldn't allocate memory for c->pData->M_Details");
            exit(EXIT_FAILURE);
        }
    
        c->pData->sizeM_Details = numRecords;
    
        return c;
    }
    
    void freemem(REQUEST * c)
    {
        free(c->pData->M_Details);
        free(c->pData->wsDate);
        free(c->pData);
        free(c);
    }