2

Because I was trying out different ways of accessing subsections of binary data I was playing around and tried this:

typedef struct structTest {
    unsigned char c;
    unsigned short s;
    unsigned int i;
} Test;

int main(void)
{
    char blah[10] = "hello";
    void * ptr = blah;

    char e;
    long l;

    memcpy(&e, &ptr[1], sizeof(char));
    memcpy(&l, &ptr[1], sizeof(long));
    char * s = (char *) &l;
    printf("%c %s\n",e,s);

    Test t;
    t.c = 255;
    t.s = 2705;
    t.i = 150586;

    void * v = (void *) &t;

    int i;
    short sh;

    memcpy(&sh, &v[1], sizeof(short));
    memcpy(&i, &v[3], sizeof(int));
    printf("%hd, %d\n", sh,i);
    return 0;
}

The first bit prints 'e' and 'ello' which is what it was intended to. The second bit doesn't do what was intended.

What I really am not clear about is what the number in the &v[n] actually refers to, since I'd always imagined what was pointed to by a void pointer as a big blob of data without any kind of internal division. My initial guess (hence the assumption the above would work) is that it would divide it as a byte array. Clearly that was wrong. Also clearly the compiler doesn't complain about me indexing a void pointer even though without a type to define the size it has no real way to tell what size the index refers to. So it must have some understood size? Except if it were simply one size then the first bit wouldn't work unless that size was a byte.

I was thinking maybe the size was defined by the third argument maybe?

Anyone want to help me out?

DumbBrunette
  • 83
  • 1
  • 6
  • 7
    I think you're using GCC, since the ability to associate a size with `void *` is a GCC extension. Normally you would not expect `&ptr[1]` where `ptr` is a `void *` to compile. [The extension treats `void *` as a byte pointer](http://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html). – unwind Feb 14 '14 at 14:01
  • I'm using clang. But you're right if I turn on those warnings it doesn't work. – DumbBrunette Feb 14 '14 at 14:05
  • Use a C compiler and set it to compile the code as C. – Lundin Feb 14 '14 at 14:06
  • You could have tried: `printf("%zu\n", sizeof(*ptr));` or `printf("%zu\n", sizeof(ptr[0]));`. – alk Feb 14 '14 at 14:26
  • @EliasVanOotegem I was trying to skip the char which I'd assume was at index[0]? – DumbBrunette Feb 14 '14 at 14:32
  • @alk heh, yeh, that would have been the obvious thing to do. – DumbBrunette Feb 14 '14 at 14:32
  • @DumbBrunette: It can be, but you can't know that for sure: compilers pad structs – Elias Van Ootegem Feb 14 '14 at 14:32
  • clang tries to be GCC-compatible at least with the extensions that people use in the wild (and sadly this does include indexing `void *`) so it also will treat `void *` as `char *` for indexing purposes. – zwol Feb 14 '14 at 14:35
  • @EliasVanOotegem oh of course! I put pragma pack and it works as intended now... – DumbBrunette Feb 14 '14 at 14:36
  • @DumbBrunette: While you accepted my answer, I was adding an important note: if you _are_ going to do pointer arithmetic, don't use a `void` pointer, but cast it to `char *`. The `char` type is always going to be 1 in size, so you can shift to the correct member of the struct using `offsetof` – Elias Van Ootegem Feb 14 '14 at 14:43

1 Answers1

3

What you're doing is trying to access the members of a struct through a void pointer. What you are forgetting, though, is that compilers are free to add padding bits/bytes of member fields of the struct.
You have no guarantee that the second member of the struct will always be found in the same place.

You can get the correct offset for a member using the offsetof macro (which is found in the stddef.h header).
However, this still won't work as you'd expect it to, because pointer arithmetic on a void pointer isn't allowed. So you still will have to cast it to the type you're working with.
Check the answers to this question for details.

The reason why the first bit works (non-standard behaviour though, given that pointer arithmetic on a void pointer isn't allowed) is that char is guaranteed to be 1 byte in size. Always. Everywhere. Full stop.
That's why, if you are going to do pointer arithmetic on a void *: cast that pointer to char *.

To do what you're trying to do, you could use bit-field specifiers, if needs must you can add #pragma pack to disable padding.
But what I'd really do is simply use the cast-to-char-pointer trick - an example:

#include <stdio.h>
#include <stddef.h>

struct tst
{
    char a;
    int b;
};

int main ( void )
{
    char a;
    int b;
    void *p;
    struct tst tst= {.a = 'q', .b = 1};
    p = &tst;
    //deref((cast  void) add offset)
    a = *(((char *)p) + offsetof(struct tst, a));
    b = *(((char *)p) + offsetof(struct tst, b));
    printf ("%c, %d\n", a, b);
    return 0;
}

The output, then, is:

q, 1
Community
  • 1
  • 1
Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149