tags:

views:

285

answers:

5

Lets say I have this struct:

typedef struct nKey {
    int num;
    widget* widget;
} NUMBER_KEY;

and a function:

void dialKey(widget* widget) {
    // Need to print 'num' of the struct that this widget resides in
}

How do I go about accomplishing this? I tried something like:

    printf("%d", * (int *) widget - sizeof(int)); // Failure.org

edit: it is safe to assume that the widget being passed is in fact a member of a NUMBER_KEY struct

edit: looking for a solution to the problem not another method.

A: 

The safest solution is to keep pointer to related nKey structure in widget

 printf("%d", widget->myKey->num);

All other methods are unsafe

gonzo
It is a bold overstatement to say "All other methods are unsafe".
qrdl
+5  A: 

As Michael explains in his answer, you cannot do it with given constraints because there's no way to "walk back" the pointer graph. To make things more obvious, let me draw a diagram of objects (in C terms, not in OOP sense) involved:

+- NUMBER_KEY --+
|  ...          | points to      
| widget field -+-----------+
|  ...          |           |
+---------------+           |   + widget +
                            +-->|  ...   |
                                |  ...   |
                            +-->|  ...   |
                            |   +--------+
                  points to |
[widget argument]-----------+

Notice the arrows. They are one-way - you can "walk" from a pointer to pointed value, but you cannot "walk" back. So you can deference widget argument of your function to get to the widget object, but once there, there's no way to tell who else points at it - including any instances of NUMBER_KEY structs. Think about it: what if you had a dozen other pointers to the same widget, some of them from different NUMBER_KEY objects? How could it possibly track that without keeping a list of all pointers within widget object? If you actually need this, it's what you'll have to do - make widget point to its owning NUMBER_KEY.

Pavel Minaev
A: 

The memory layout of a struct is defined by your compiler, your code would only work if you have 0 byte padding between struct members. Usually this is not recommended b/c 0 byte padding would have your struct overlay across the standard read sizes of most processors.

Some useful macros for your problem:

#define GET_FIELD_OFFSET(type, field)    ((LONG)&(((type *)0)->field))
#define GET_FIELD_SIZE(type, field)      (sizeof(((type *)0)->field))

example:

NUMBER_KEY key;
key.num = 55;
int nOffSetWidget = GET_FIELD_OFFSET( NUMBER_KEY, widget);
int *pKeyNumAddress = (int *) &(key.widget) - nOffSetWidget );

printf("%d", * pKeyNumAddress );    // Should print '55'
Adam
If you #include <stdlib.h> or <cstdlib>, the offsetof() macro is standard.
David Thornley
I don't think it is going to work the way you expect. You are casting address of key.widget to *int thus pointer math with use sizeof(int) as arithmetic unit, so instead of deducting nOffSetWidget number of bytes you will deduct sizeof(int)*nOffSetWidget number of bytes.And there is standard macro offsetof - no need to invent it.
qrdl
+4  A: 

Given just a widgit* and not a widgit** being passed into dialKey, there's no way to do what you want (a widgit* value has no relationship to the NUMBER_KEY struct). Assuming that you really mean something like:

void dialKey(widget** ppWidget) 
{    
    // Need to print 'num' of the struct that this widget resides in
}

Microsoft has a nifty macro for doing this type of thing (it helps with being able to have routines that manipulate linked lists genericly in C):

#define CONTAINING_RECORD(address, type, field) ((type *)( \
                               (PCHAR)(address) - \
                               (ULONG_PTR)(&((type *)0)->field)))

You could use this like so:

NUMBER_KEY* pNumberKey = CONTAINING_RECORD( *ppWidgit, NUMBER_KEY, widgit);

printf( "%d", pNumberKey->num);
Michael Burr
+1 for `widget *` vs. `widget **` which all the existing answers have missed. `CONTAINING_RECORD` is clearly a hack, but at least it hides the gory details...
RBerteig
Very good point, I missed the pointer in struct declaration. I'll fix my answer accordingly.
Pavel Minaev
@RBertig - it may be a hack, but it's a damn useful one.
Michael Burr
You can simplify the CONTAINING_RECORD macro down to ((char *)(address) - offsetof(type, field)). offsetof() is a standard C macro from stddef.h.
caf
A: 

C standard defines offsetof() macro (defined in stddef.h header), which is handy in your case.

#include <stddef.h>

void DialKey(widget** foo) {
    printf("%d", *((char *)foo - offsetof(struct nKey, widget)));
}

Note that you have to pass an address of struct field, not a value!

qrdl