views:

83

answers:

6

I want to take some fields from packet struct using pointer arithmetic.But what is wrong with the code below ?
In first condition i think if i go 4 byte(2 short field) from beginning of packet i get tLow .But it does not give expected value.Additionally second case i want to get data field by going 12 byte from beginning of packet .What is wrong with my idea ?

struct packet{  
      short len;
      short field;
      int tLow;
      int tHigh;
      void *data;
}

int main()
{
    struct packet pack;
    struct packet *pck;

    pack.len=3;
    pack.field=34;
    pack.tLow=712;
    pack.tHigh = 12903;
    pack.data = "message";

    pck = &pack;
    int *timeLow = (int * )pck + 4; // i want to get tLow 
    printf("Time Low :%d\n",*time);

    char *msg = (char *)pck + 12 ;// want data 
    printf("Message :%s\n",msg);

    return 0;
}
+1  A: 

when you write

int *timeLow = (int * )pck + 4

you are treating 'pck' as a int pointer which depending on your system may be 4 or 8 bytes. That will not correctly offset into the struct because you are then telling it to have an offset of 4 int

instead you would need to do like this

int *timeLow = (int*)((short * )pck + 2); 
Anders K.
or (int*)((unsigned char*)pck + 4)
Anders K.
thanks,i realized my mistake by treating pck as integer .Firstly converting to char then doing the byte calculation is the safe way i think.
Qxtrml
np, easy to do wrong, done so myself.
Anders K.
A: 

Shorts are not necessarily 2 bytes long. All that is specified is that they are less-than or equal-to the size of ints. You should probably be using sizeof()

Justin Hamilton
A: 

This is wrong: pack.data = "message"; You are using unallocated memory.

Also this: int *timeLow = (int * )pck + 4; is not guaranteed to work (struct alignment varies between compilers and systems).

vulkanino
Nope; the memory that `pack.data` points to is the literal "message". That's OK. An attempt to change it would be undefined behavior, and copying a string into `pack.data` would be bad.
David Thornley
you're right, david.
vulkanino
+2  A: 

You are looking for offsetof.

Your code could look like this:

int *timeLow = (int*) ((char*)pck + offsetof(struct packet, tLow);

and as pmg pointed out,

int *timeLow = &(pck->tLow);

is the canonical way of getting a pointer to a member of a struct.

This answer also brought pointer arithmetics on the table - which I learned today thanks to pmg.

Amigable Clark Kant
@pmg, really? I can't see how.
Amigable Clark Kant
Like this you mean? http://www.cs.umd.edu/class/spring2003/cmsc311/Notes/BitOp/pointer.html
Amigable Clark Kant
@pmg: the `(char*)` cast has precedence over pointer addition. http://www.difranco.net/cop2220/op-prec.htm But yeah, use parentheses if confused.
rwong
@pmg, cool! :-) Means you are an honest person, capable of self-doubt despite immense knowledge on a topic.
Amigable Clark Kant
Amigable Clark Kant
@pmg, is it not?!
Amigable Clark Kant
It's right now ... I was seeing wrong, sorry
pmg
+2  A: 

Basically you are relying on undefined behavior. Struct alignment, etc. But anyways...

(int*)pck + 4. Will advance the pointer 4 * sizeof(int). This is wrong, we want to advance it by 1 if we assume that the struct is packed and sizeof(short) == 2, thus...

int *timeLow = (int * )pck + 1; // i want to get tLow 
printf("Time Low :%d\n",*timeLow);

prints the correct result.

As for the message, you need to do some dirty stuff. Since I'm on x86_64 the compiler chose to pad the void* on 8 byte boundaries, so the offset was 16 rather than the expected 12.

We're basically fetching a pointer to a void*. So the code will look like this:

char **msg = (char**)((char *)pck + 16) ;// want data 
printf("Message :%s\n",*msg);

NEVER write code like this, this just illustrates a point.

Maister
+1 for the NEVER. Never say never ... except when necessary :-)
pmg
@pmg: adding `k` to `(char*)` shifts the address by `k*sizeof(char)`, i.e. the element type, and `sizeof(char)` gives 1 by definition.
rwong
Oh! I'm seeing stars already ... need a rest. thanks rwong
pmg
+2  A: 

You definitely would be better using standard method

int *timeLow = &(pck->tLow);

Compilers are allowed to insert padding bytes between any member of a struct. The rules for these padding bytes are, at best, implementation defined ... so you must consult your implementation manual to be certain how (or if) and how many bytes are inserted in your specific case. Also note the number of padding bytes might change from compilation to compilation with different options or from compiler to compiler (computer to computer, ...)

You can try to use the C macro offsetof, but it's not pretty:

size_t offset = offsetof(struct packet, tLow);
/* make sure offset is a multiple of sizeof (int*) */
int *timeLow = (int*)pck + offset / sizeof (int*);

or, a little less ugly using a cast to (char*) and copying the code from other answers :-)

size_t offset = offsetof(struct packet, tLow);
int *timeLow = (int*)((char*)pck + offset);

Oh! and there's a semi-colon missing in your source

pmg
Just as you mentioned, your example code for `offsetof` depends on the compiler's padding in order to put `tLow` on an offset that is divisible by the size of `tLow`. If the compiler didn't do padding then the offset would be indivisible.
rwong
Ok i will use standard method for accessing the struct members.And i realized my mistake in pointer arithmetic by treating pck as integer.But sometimes byte operations needed and in that situation i will use converting pck to char and doing byte manipulation or offsetof macro.
Qxtrml