pointer arithmetics and casting

D

dmjcunha

Hi,considering data and length have valid values I have
the following:

struct object {
uint32_t length;
void *data;
};

void objfunc(void *data, uint32_t length)
{
struct object *obj;
obj = malloc(sizeof(*obj) + length);
obj->length = length;

/*now come the problems*/

obj->data = obj + 1;

/*Does this mean starting at the beginning of obj address
alloc space for sizeof(*obj)? Or does it mean starting
at the end of the space allocated for obj, allocate more
sizeof(*obj)*/

if(data)
memcpy(obj->data, data, length);

/*This I think I can understand. Copy length bytes of data into
obj->data. Correct?

/*And now the weird statement I really don't understand.*/

*(struct object **)obj->data = obj; /*I not even know how to read it!*/

/*The bare I know would make the if(data) statement irrelevant
because ...obj->data = obj would overwrite it. But I really
don't know what is happening here.*/

return;
}

Could someone help me with clarifications? Thanks.
 
G

glen herrmannsfeldt

Hi,considering data and length have valid values I have
the following:
struct object {
uint32_t length;
void *data;
};

Struct has a length and a pointer to data.
void objfunc(void *data, uint32_t length)
{
struct object *obj;
obj = malloc(sizeof(*obj) + length);

Allocate space for the struct and additionally for the data.
Note that it gets more complicated if data is other than char.
obj->length = length;
/*now come the problems*/
obj->data = obj + 1;

obj+1 points to just after the struct (including any padding).
/*Does this mean starting at the beginning of obj address
alloc space for sizeof(*obj)? Or does it mean starting
at the end of the space allocated for obj, allocate more
sizeof(*obj)*/
if(data)
memcpy(obj->data, data, length);

The if is needed if it is possible that it will be called
with a NULL pointer, and hopefully with a length of zero.
/*This I think I can understand. Copy length bytes of data into
obj->data. Correct?

No, to where obj->data points. That is, just after the struct.
/*And now the weird statement I really don't understand.*/
*(struct object **)obj->data = obj; /*I not even know how to read it!*/

I have never tried anything like this, but I think it copies a
pointer to the struct over the beginning of the data.
Without knowing more, I couldn't say why you would want to
do that.
/*The bare I know would make the if(data) statement irrelevant
because ...obj->data = obj would overwrite it. But I really
don't know what is happening here.*/

The if(data) is to avoid problems if data is a NULL pointer.
In that case, I would also set the length to zero, so that
random data isn't copied out later. It would be more usual
to generate a warning or error message, but this is fine, too.
 
S

Stefan Ram

obj = malloc(sizeof(*obj) + length);
obj->length = length;

Library-grade code would be:

if( obj = malloc( sizeof( *obj )+ length ))
{ obj->length = length; ...
obj->data = obj + 1;
/*Does this mean starting at the beginning of obj address
alloc space for sizeof(*obj)?

The statement »obj->data = obj + 1;« does not allocate space.
if(data)
memcpy(obj->data, data, length);
/*This I think I can understand. Copy length bytes of data into
obj->data. Correct?

If the objects do not overlap. (I don't know whether they
overlap here.)
*(struct object **)obj->data = obj; /*I not even know how to read it!*/

This copies the value of obj to the address given in obj->data.
 
S

Stefan Ram

struct object { uint32_t length; void *data; };
struct object *obj = malloc( sizeof( struct object )+ length );
obj->length = length; obj->data = obj + 1; memcpy(obj->data, data, length);
*(struct object **)obj->data = obj;

*obj->data = obj + 1
.-------------------------------------.
| |
| *obj |
| -.----------------------. |
| || length | |
| ||----------------------| |
| +1 v| data -------------------------' data
| -|----------------------| 2nd weird .----------------------.
'---->| obj ^ <------- | |
|--------|-------------| | |
| | | | |
| | | 1st memcpy| |
| | |<----------| |
| | | | |
| length | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| v | | |
'----------------------' '----------------------'
 
J

James Kuyper

Hi,considering data and length have valid values I have
the following:

struct object {
uint32_t length;
void *data;
};

void objfunc(void *data, uint32_t length)
{
struct object *obj;
obj = malloc(sizeof(*obj) + length);

Any call to malloc() can fail. Any code that calls malloc() should
therefore always check whether or not it has failed. This code ignores
that possibility, and even if it did, it provides no mechanism for
reported the fact that it failed. It would not pass review in my group.
obj->length = length;

/*now come the problems*/

obj->data = obj + 1;

/*Does this mean starting at the beginning of obj address
alloc space for sizeof(*obj)? Or does it mean starting
at the end of the space allocated for obj, allocate more
sizeof(*obj)*/

obj is a pointer to a "struct object", so obj+1 is therefore a pointer
to where the second such object would be, if it were part of an array of
"struct object". In other words, it points just after the memory
reserved for *obj itself, which is the first part of the allocated block
of memory that is not reserved for *obj. data is a void* pointer, so it
just points at that memory without pointing at an object of any
particular data type.
if(data)
memcpy(obj->data, data, length);

/*This I think I can understand. Copy length bytes of data into
obj->data. Correct?

No, it copies those bytes into the location where obj->data points at,
which happens to be the location immediately after the one where
obj->data itself is stored (unless there's padding bytes at the end of
"struct object".
/*And now the weird statement I really don't understand.*/

*(struct object **)obj->data = obj; /*I not even know how to read it!*/

The expression (struct object**)obj->data has undefined behavior unless
obj->data points at memory correctly aligned to store a "struct
object*". That memory is guaranteed to be correctly aligned to store a
"struct object", but not necessarily a "struct object *". I presume that
this is not a problem on the platform where this code normally runs; a
struct object contains a void*, and is therefore necessarily correctly
aligned to for a void*, and on many platforms a "struct object *" is
likely to have the same alignment requirements as a "void*". However, in
principle it could be a problem on other platforms.

*(struct object**)obj->data treats the memory that obj->data points at
as a "struct object *", and the entire statement sets that pointer to
the same value as is stored in 'obj' itself, overwriting whatever value
it was given by the memcpy() call.
The code therefore has undefined behavior if length < sizeof(obj),
because not enough memory has been set aside to store such a pointer. In
my opinion, this code should check whether length is long enough, and
return before even allocating the memory if it isn't. Again, the fact
that the interface to this program provides no mechanism for reporting
failure is problem.
/*The bare I know would make the if(data) statement irrelevant
because ...obj->data = obj would overwrite it. But I really
don't know what is happening here.*/

The memcpy() is not necessarily irrelevant, because it copies a total of
length bytes. If length > sizeof(obj), then the remaining bytes will be
unchanged by execution of the "weird" statement.

If the object that data points at must always be correctly aligned, and
sufficiently long, to store a "struct object*", then I would have
declared both data and obj->data as having the type struct object**, in
which case there would be no alignment issue, and the "weird" line would
have been much simpler:

*obj->data = obj;

Another thing to note is that objfunc() allocates memory, but pointers
to the allocated memory are stored in only three locations: 'obj', which
is local to the function, and disappears when the the function returns,
obj->data, which points inside the allocated block, and *(struct
object**)data, which points at the entire allocated block. Those last
two pointers are both stored inside the allocated block, and therefore
become completely inaccessible when this function returns, so there's no
way after that point to access the allocated memory, or to free() it.
This is a memory leak!

If this is actual working code, I must have made an error in my
analysis, which is quite possible, multiple levels of indirection are
always error-prone. I've triple-checked my analysis, but that's
unfortunately not sufficient to ensure that it's correct.

However, if I'm right, this code contains an example of one such error.
I think objfunc() should either return a pointer to the allocated
memory, or write such a pointer to *data. If the latter approach is
used, then data==NULL should be treated as an error condition at the
very beginning: memory should not even be allocated if data is null -
that's the key reason I prefer the other approach. A null pointer can be
returned if malloc() fails or length is not large enough.
 
D

dmjcunha

Hi, first thank you for the answers. With the simpler version *obj->data = obj; instead of the weird one I (although understood it better) came to a more
sinister point of view. Please correct me if I'm wrong.

obj = malloc(sizeof(*obj) + length); allocates memory for sizeof(*obj) plus
length bytes.

obj->data = obj + 1; points to the address immediately after this memory
allocated (siszeof(*obj) plus length bytes).

If memcpy is executed it copies length bytes of data to the above address which
is just after the memory allocated with malloc.

With the simpler version *obj->data = obj; I understood that it sets the pointer
to the address obj which is the beginning of the allocated space for
sizeof(*obj) plus length bytes.

It happens that nothing is overwritten because *obj has space for sizeof(*obj)
plus length bytes. But it remains "in between" this intermediate length bytes
unused that was allocated with malloc and the memcpy data that was copied in
the position obj->data = obj + 1 which happens to be immediately after position
sizeof(*obj) plus length.

Am I on the right track? Am I missing some C concept?
Thanks for any consideration.
 
J

James Kuyper

Hi, first thank you for the answers. With the simpler version *obj->data = obj; instead of the weird one I (although understood it better) came to a more
sinister point of view. Please correct me if I'm wrong.

obj = malloc(sizeof(*obj) + length); allocates memory for sizeof(*obj) plus
length bytes.

obj->data = obj + 1; points to the address immediately after this memory
allocated (siszeof(*obj) plus length bytes).

obj is a pointer to a "struct object". obj+1 is therefore equivalent to
(struct object*)((char*)obj + sizeof(struct object)). The + 1 does NOT
take into consideration the 'length' that was added into the malloc()
call. As a result, obj+1 points at the beginning of the extra memory
that was allocated because 'length' was added, not at the end of that
memory.

Most of the rest of your message depends upon that misunderstanding, so
I'll not address it further. There's one exception:
With the simpler version *obj->data = obj; I understood that it sets the pointer
to the address obj which is the beginning of the allocated space for
sizeof(*obj) plus length bytes.

You said "the pointer", which is potentially confusing. There are
several declared and named pointers in this code, and whether or not
your statement is correct depends upon whether you're referring to one
of those pointers. If you are, your statement is wrong. obj->data points
at part of a block of dynamically allocated memory, which has no
declared type. That block of memory acquires the effective type "struct
object*" as a result of that assignment statement you refer to. If you
were referring to that block of memory when you said "the pointer", then
you're correct.
 
D

dmjcunha

obj is a pointer to a "struct object". obj+1 is therefore equivalent to
(struct object*)((char*)obj + sizeof(struct object)). The + 1 does NOT

take into consideration the 'length' that was added into the malloc()

call. As a result, obj+1 points at the beginning of the extra memory

that was allocated because 'length' was added, not at the end of that

memory.

That dang obj + 1, it got me! I didn't know. Thank you so much Sir!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Staff online

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top