Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Allocate record with variable at negative offset

Tags:

c

When allocating memory for a pointer to a record I also need space for an integer pointer located just before the allocated record. This pointer cannot be part of the record itself and it cannot be placed after the record. My current approach is the following:

#include <stdlib.h>

static int n;
struct { int f; } *p;

p = malloc(sizeof (int *) + sizeof *p);
if (p != NULL) {
    p = (void *) ((int **) p + 1);
    *((int **) p - 1) = &n;
}

Are the casts well defined? If not, what should I do instead?

Edit:

What I'm trying to achieve is to implement extensible records (OOP) and the integer pointer represent a type ID. An extended record should be compatible with its base type. However, I only need type ID:s for pointer to records. Here is a complete example:

#include <assert.h>
#include <stdlib.h>

struct T0 {
    int f;
};
int T0ID;

struct T1 {
    struct T0 base;
    int g;
};
int T1ID;

int main(void)
{
    struct T0 *x;
    struct T1 *y;

    y = malloc(sizeof (int *) + sizeof *y);
    if (y != NULL) {
        *((int **) y) = &T1ID;
        y = (void *) ((int **) y + 1);
        ((struct T0 *) y)->f = 1;
        y->g = 2;
    }
    x = (struct T0 *) y;
    assert(x->f == 1);
    return 0;
}
like image 292
August Karlstrom Avatar asked Nov 25 '25 11:11

August Karlstrom


2 Answers

I'm not sure your approach is good. Especially I'm worried about changing the value of p. You'll need that value later when you need to free the memory.

I would wrap the int* and the struct together in another struct. Something like:

static int n;
struct someData { int f; };

struct wrapper {int* pn; struct someData data;};

struct someData* pd; // Pointer to the data struct


struct wrapper* pw = malloc(sizeof *pw);

if (pw != NULL) {
    pw->pn = &n;
    pd = &pw->data;
}
like image 129
Support Ukraine Avatar answered Nov 27 '25 00:11

Support Ukraine


Your code, in which you cast (arbitrary) memory addresses to particular object types, is might yield undefined behaviour due to incorrect alignment (cf. C standard draft):

6.3.2.3 Pointers

(7) A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.

You are having a pointer declared to be of type struct{int}* pointing to a (larger memory block), and then casting the pointer and doing arithmetic operations on the (casted) pointer. As it is not guaranteed that int * and struct{int}* are aligned the same way, it is not guaranteed that the behaviour is defined.

To avoid this, encapsulate your structure and the preceeding integer in another structure, e.g. as follows:

static int n;

struct data_struct {
    int f;
};

struct enclosing_struct {
    int *nPtr;
    struct data_struct data;
};


int main() {

    struct enclosing_struct *e = malloc (sizeof(struct enclosing_struct));
    e->nPtr = &n;
    struct data_struct *dataPtr = &e->data;

    return 0;
}
like image 45
Stephan Lechner Avatar answered Nov 27 '25 01:11

Stephan Lechner



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!