Thread: Question concerning struct hack

  1. #1
    Registered User
    Join Date
    Aug 2011
    Posts
    31

    Question concerning struct hack

    Hi, I am trying to do a struct hack

    Code:
    typedef struct{
       uint8_t a;
       uint8_t b[1];
    }c;
    Then I want to my array b to have size of 4 bytes instead so i did this

    Code:
    c test;
    uint8_t* pValue;
    pValue = (uint8_t*)malloc(4);
    
    //Assign value to where pValue is pointing to
    *pValue = 0xffff;
    
    //memcpy to test.b 
    memcpy(test.b,pValue,4);
    Will this work out? if i do a size of the struct c now, will it reflect the additional bytes that i allocate?

  2. #2
    Registered User Maz's Avatar
    Join Date
    Nov 2005
    Location
    Finland
    Posts
    194
    No. If you have not specifically allocated larger space for struct test, it will only be the size it's type specifies. Thus your memcpy will write the rest of the data (3 bytes) to unallocated memory.

    You could do

    Code:
    c *test=calloc(1,sizeof(c)+3);
    if(NULL!=test)
    {
        /* Now you have pointer to struct type c, but there's actually more space allocated */
    }
    NOTE: If you use some other data types but chars, the struct padding may bite your but. Eg, data is typically aligned to 4 byte boundaries, and if you declare for exampl a struct

    Code:
    struct foo
    {
        short data1;
        int    data2:
    }
    it typically (NOT WITH ALL ARCHITECTURES THOUGH) will be in memory as follows:

    2 bytes for data1
    2 badding bytes to align data correctly
    4 bytes for integer data2

    There is compiler specific pragmas for example to do packing of data

    (gcc has #pragma pack and #pragma pop)

    But I would not encourage using these. And in any case, if you go to the road of such "hacking", you need to be carefull with alignment and little/big endian formats.

    For example, if we run following code in big and little endian machines, we'll have different results (can you tell me why, if you can't, avoid doing such a "hacks" )

    Code:
    char *x;
    short y;
    
    y=1;
    
    x = (char *) &y;
    
    printf("%d",(int) (*x));

  3. #3
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    You do something like this
    Code:
    c * test = malloc( sizeof *test + myArraySize * sizeof(test->b[0]) );
    for ( i = 0 ; i < myArraySize ; i++ ) {
      test->b[i] = 0;
    }

    > Will this work out? if i do a size of the struct c now, will it reflect the additional bytes that i allocate?
    No, because declaring a literal struct instance will ONLY ever have a 1-element array associated with it.
    If you want it to be a variable length, then you must allocate a block with that amount of space.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  4. #4
    Registered User
    Join Date
    Aug 2011
    Posts
    31
    Salem, what is the sizeof *test referring to? is it the same as sizeof(c)?

  5. #5
    Registered User Maz's Avatar
    Join Date
    Nov 2005
    Location
    Finland
    Posts
    194
    Yes.

    test is a pointer to data type of c. so sizeof(test) would be size of pointer, and sizeof(*test) should be size of the type (c in this case)

  6. #6
    Registered User
    Join Date
    Aug 2011
    Posts
    31
    hmm okie my code goes like this

    Code:
    c* pValue;
    pValue= (c*)malloc(2 +  sizeof(uint8_t));
    pValue->a= 1
    uint16_t setValue= 0x1234;
    memcpy(c->b,(uint8_t*)&setValue,sizeof(setValue));
    the malloc will allocate 3 bytes in total. because the first byte refers to the size of a and the rest of the 2 bytes refer to the size of the setValue which takes the size of 2 bytes.

    Is this correct?

  7. #7
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    Well it's technically correct in this instance, but if your struct was more complicated (more members, different types, with the internal padding that implied), then it would just be horrible.

    The allocation I showed you simply does not care about how many members the struct has, or what type the Flexible Array Member is. All you need to know at the point of the malloc call is the name of the FAM - that's all.


    Also, read the FAQ on why casting malloc is a bad idea in a C program.
    Are you trying to suppress "cannot convert void*" error messages by any chance? If so, then stop using a C++ compiler to compile C code.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  8. #8
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by Edelweiss View Post
    Hi, I am trying to do a struct hack

    Code:
    typedef struct{
       uint8_t a;
       uint8_t b[1];
    }c;
    Then I want to my array b to have size of 4 bytes instead so i did this

    Code:
    c test;
    uint8_t* pValue;
    pValue = (uint8_t*)malloc(4);
    
    //Assign value to where pValue is pointing to
    *pValue = 0xffff;
    
    //memcpy to test.b 
    memcpy(test.b,pValue,4);
    Will this work out? if i do a size of the struct c now, will it reflect the additional bytes that i allocate?
    At the risk of asking a painfully stupid question... why not just change the structure definition?
    Code:
    typedef struct{
       uint8_t a;
       uint8_t b[4];
    }c;
    And please don't use singe letter names in typedefs... name it to define the type you are creating, not the next letter in the alphabet...

  9. #9
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    > At the risk of asking a painfully stupid question... why not just change the structure definition?
    Perhaps we're just discussing "proof of concept" at this stage, with a nice simple example.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  10. #10
    Registered User
    Join Date
    Aug 2011
    Posts
    31
    lol ohhh because in my implementation the size of b may varies according to the variable i want to store inside, hence asking about the concept behind struct hack.

    Code:
    c * test = malloc( sizeof *test + myArraySize * sizeof(test->b[0]) );
    Some doubts about the example that u gave, the sizeof(c) is 2 bytes, so if i want my array to have size of 4 bytes, myArraySize should be 3 or 4? Because I am assuming that size of test includes that 1 byte for a and 1 byte for b array.

  11. #11
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by Edelweiss View Post
    lol ohhh because in my implementation the size of b may varies according to the variable i want to store inside, hence asking about the concept behind struct hack.
    Then you should either
    A) Make one struct big enough to hold the biggest data set ( a union might be a plan, here)
    or
    B) Make multiple structs specific to each data set.

    I know everyone here is approching this as a theoretical exercise but trying to implement this in reality would be a total nightmare.

  12. #12
    Registered User
    Join Date
    May 2011
    Location
    Around 8.3 light-minutes from the Sun
    Posts
    1,949
    Quote Originally Posted by Edelweiss View Post
    Code:
    c * test = malloc( sizeof *test + myArraySize * sizeof(test->b[0]) );
    Is there any reason you are still casting malloc after you have been told not to? I will make it easy for you: FAQ-Casting Malloc() <DONT DO IT>
    Quote Originally Posted by anduril462 View Post
    Now, please, for the love of all things good and holy, think about what you're doing! Don't just run around willy-nilly, coding like a drunk two-year-old....
    Quote Originally Posted by quzah View Post
    ..... Just don't be surprised when I say you aren't using standard C anymore, and as such,are off in your own little universe that I will completely disregard.
    Warning: Some or all of my posted code may be non-standard and as such should not be used and in no case looked at.

  13. #13
    Registered User Maz's Avatar
    Join Date
    Nov 2005
    Location
    Finland
    Posts
    194
    Quote Originally Posted by CommonTater View Post
    Then you should either
    A) Make one struct big enough to hold the biggest data set ( a union might be a plan, here)
    or
    B) Make multiple structs specific to each data set.

    I know everyone here is approching this as a theoretical exercise but trying to implement this in reality would be a total nightmare.
    Actually no. This kind of implementations are quite common in interfaces which are meant to be generic. The netlink socket interface for data exchange between linux kernel and userspace is a good example. The data is exchanged in a buffer which is structurized as follows:

    stars with message header, where one field states the "type" of the message, and other states the total lenght. (Theres also some fields not really relevant here).
    Then theres a type specific struct
    and at the end there is appended a set of optional attributes. Each attribute being a struct with number telling attribute type, lenght of attribute, and actual data payload.

    So basically, this whole construct is dynamic, and it contains dynamic structs. And to ease handling of all this there is a set of macros written, handling the correct alignment etc.

    So it is not rare at all to have structures with dynamic arrays. Actually, that is quite standard way to exhange data. (between kernel and userspace, between processes, between computers..) Almost all network protocols flowing on top of ethernet / IP consist of such a structures. Still, even though this is common, it still has plenty of pitfalls. The hardest parts for me is alignment (padding bytes) and little/big endian conversions when exchanging data between different systems / casting values.

  14. #14
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Quote Originally Posted by Salem View Post
    You do something like this
    Code:
    c * test = malloc( sizeof *test + myArraySize * sizeof(test->b[0]) );
    for ( i = 0 ; i < myArraySize ; i++ ) {
      test->b[i] = 0;
    }
    This is much harder to pull off with just a pointer:
    Code:
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    int main( void )
    {
        struct foo { char *bar; } *baz = malloc( 100 + sizeof *baz );
        baz->bar = (char*)((&baz->bar) +1);
        strcpy( baz->bar, "hello world" );
        printf( "%s!\n", baz->bar );
        free( baz );
        return 0;
    }
    Ugly, and requires a cast for your pointer to "work right". It seems like I should be able to just point at the address of the pointer itself, because that's basically what making it an array would be doing, but it doesn't like that at all.


    Quzah.
    Hope is the first step on the road to disappointment.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Interesting Linux hack
    By jeffcobb in forum Linux Programming
    Replies: 13
    Last Post: 04-20-2010, 06:50 AM
  2. are strings a hack in c?
    By stabu in forum C Programming
    Replies: 15
    Last Post: 08-06-2008, 06:45 PM