-3

How do I unindex a struct? Example:

typedef struct String_s {
    int current_location;
    int size;
    char data[0];
} String;

char* String_getCString(String *str){
    return &str->data[0];
}

//this is supposed to take the result of 'String_getCString' and reverse the process to get the String*
//i.e. String_getCString(CString_getString(str)) == str
String* CString_getString(char *str){ 
    //???
}

int foo(char *cstr){
    printf("%s\n", cstr);
    fflush(0);
    free(CString_getString(cstr));
}

int main(int argc, char *argv[]){
    const char *hello_world = "hello world";
    String *str = (String*)malloc(sizeof(String)+1000*sizeof(char));
    str->size = 1000;
    str->count = strlen(hello_world);
    char *cstr = String_getCString(str);
    strcpy(cstr, hello_world);
    foo(cstr);
    return 0;
}
chacham15
  • 13,719
  • 26
  • 104
  • 207

1 Answers1

3

I'm not 100% sure I understand what you want CString_getString to do, but if you want it to return the address of the overall String object when passed the address of the embedded data field, then that's straightforward, but dangerous:

#include <stddef.h>

String *CString_getString(char *str)
{
    return (String *)(str - offsetof(String, data));
}

If the type of the field you wished to "unindex" were anything other than [signed/unsigned/] char, you would need to cast the input pointer to char * before the subtraction, as well as casting to the desired return type afterward.

This is dangerous because CString_getString has no way of knowing whether you've passed in a str that really is the embedded data field of a String object. If you get it wrong, the C compiler sits back and watches it blow up on you at runtime. But, arguably, this is no worse than anything else one does in C all the time, and this can be a useful technique. It is, for instance, heavily used in the guts of Linux: http://lxr.free-electrons.com/ident?i=container_of

zwol
  • 135,547
  • 38
  • 252
  • 361
  • Isn't just _doing_ the subtraction here undefined behavior? See e.g. http://stackoverflow.com/a/5341785/318716. – Joseph Quinsey Feb 17 '14 at 18:56
  • 1
    @JosephQuinsey Last I heard, that was still an open question. The definition of "object" in C99 can be read either narrowly or broadly. Applied to this case, the narrow definition would forbid pointer arithmetic beyond the boundaries of the `data` array, and the broad definition would allow it within the limits of the original block returned from `malloc`. In practice, enough code (like this) relies on the broad definition that compilers have to follow suit. If C11 changed the picture I am not aware of it. – zwol Feb 18 '14 at 00:36
  • @zwol I actually dont think this was ever undefined behavior, this is littered throughout the linux kernel: http://stackoverflow.com/questions/15832301/understanding-container-of-macro-in-linux-kernel – chacham15 Jul 09 '16 at 20:08
  • @chacham15 You can't take the Linux kernel as a guide to what is and is not undefined behavior. You can only take it as a guide to what you can get away with in some versions of GCC. – zwol Jul 11 '16 at 13:42