tags:

views:

187

answers:

4
#define STRING(s) (((String*)s)-1)

what in the world is (((String*)s)-1)?

typedef 
struct String {
    int length;
    int capacity;
    unsigned check;
    char ptr[0];
} String;
+7  A: 

You're casting s to a String *. Then you're subtracting one from it (making it point to the previous whatever).

Anything more specific would need to know the definition of String - but (WILD SPECULATION) I would guess the application uses dual VB/C-style strings (null terminated, preceded by the length), and this function changes it from a form suitable for C functions (pointer to the first character) into one usable for the other type (pointer to the length).

Anon.
+1 Was about to write something like that, and it is also a common format for pascal programs to encode strings.
epatel
@Anon, String definition added
Yep, that looks about right. Since the array in the struct def has zero elements, the first character is beyond the end of the struct. Then, subtracting `sizeof(String)` from a pointer to that character (which is why the `-1` does when applied to a `String *` will result in something pointing to the start of the struct.
Anon.
A: 
  • (String*) = type cast to pointer to String object,
  • s = the string,
  • -1 = point to one String object's length before in the memory block

Don't know why the macro is made this way. Possibly the definition of String requires this, but that's just a wild guess.

mingos
you're doing the pointer arithmetic wrong: `-1` won't move the pointer by a byte, but instead will move it to the preceding `String` object
Christoph
Arr, that's true. I edited the post not to be misleading. Thanks for pointing that out.
mingos
+5  A: 

Mechanically, the macro works as others have already described it. Semantically, though, you can think of this as a form of casting from char *s to String *s.

The String structure is the header of a counted string, ie, one where you know the total length without having to scan for a NUL byte. This particular version also keeps the total allocated. You would create one as follows:

struct String *str = malloc(sizeof(*s) + maxlen);
str->length = 0;
str->capacity = maxlen;
str->checked = /* ??? */;

There should be some assorted functions around somewhere to manipulate these counted strings.

The macro itself is a hack to go from a plain char *, assumed to point to the first character of the a String as allocated above, back to a String *. It would be used something like this:

/* allocate str as above */
char *s = str->p;

Now, through a chain of function calls or returns, you somehow loose track of the String structure containing s, and you need to find it again. You write:

String *str2 = STRING(s);

This is not a particularly good way to implement a counted string in C, but it demonstrates a technique that one sees from time to time.

Dale Hagglund
Since this seems the most in-depth answer I thought I'd elaborate on why this struct hack might be used. (Disclaimer: I've never done this but have seen it done). This struct lets you put your stings on the stack rather than on the heap (doesn't help you pass them about much though). This means you avoid any calls to malloc/free. Which in turn means they can be speedy in tight inner loops.
Michael Anderson
+2  A: 

Others have answered your question. The technique of declaring ptr inside struct String with zero size is called "the struct hack", and wasn't portable until C99 (although it was widely used even before C99, and seems to work everywhere). The idea is that ptr uses 0 bytes, so if you have a pointer to ptr, and want one to the original struct, you would use the STRING macro. You are subtracting the size of the struct from the address of the ptr member, and thus getting the start address of the struct.

A better way to get the start address of a struct given a pointer to any of its members is to use offsetof() macro defined in stddef.h. offsetof(struct type, member), as the name implies, gives the offset of member in struct type:

#define STRING(x) ((String *)(((char *)(x) - offsetof(struct String, ptr))))

Then you can do:

#include <stddef.h>
#include <stdlib.h>
#include <assert.h>

typedef struct String {
    int length;
    int capacity;
    unsigned check;
    char ptr[0];
} String;

#define STRING(x) ((String *)(((char *)(x) - offsetof(struct String, ptr))))

int main(void)
{
    String *s = malloc(sizeof *s + 100);
    String *t;
    char *mystring = s->ptr;
    t = STRING(mystring);
    assert(t == s);
    return EXIT_SUCCESS;
}

offsetof() is defined in stddef.h.

Note that in C99, "struct hack" would declare ptr inside the struct as:

char ptr[];

i.e., without a size.

Alok