473,406 Members | 2,217 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,406 software developers and data experts.

Offset/alignement of structure members

Hello,

I'm wondering about the portability and correctness of the
following scenario.

I have a generic doubly linked list structure, and it contains
generic nodes that look like this:

struct node
{
struct node *head;
struct node *tail;
};

struct list
{
struct node *first;
struct node *last;
};

Now, I want to use the generic list and node structures so that

- The list and its nodes stay completely generic.
That means I can have generic functions like

add_head(struct list *, struct node *);

Any list can be traversed and examined and manipulated
with the generic functions, as long as the operations
are stay on the generic level.

- The nodes can contain application specific data. That is:
the node structure is extended by adding application specific
fields, which can only be accessed if the application knows
what kind of node it is using.

Application specific node would look like

struct my_app_node
{
struct node node;
int datafield1;
char *datafield2;
etc.
};

Now, an application could do:

struct list mylist;
struct my_app_node node1;
struct my_app_node node2;

init_list(&mylist);

add_head(&mylist, (struct node *)&node1);
add_head(&mylist, (struct node *)&node2);

So, is this correct ISO C? Is this portable?

I know that I could do also:

add_head(&mylist, &node1.node);
But, of course, both questions boil down to: can I trust
that offset of "node" inside my_app_node is 0? And also,
does the compiler let me get away with the cast?
I also do know that a certainly correct solution would be:

struct node
{
struct node *next;
struct node *prev;
void *data;
};

struct my_app_node
{
struct node node;
/* data fields follow */
};

struct my_app_node mynode;
mynode.node.data = &mynode;

But in this scenario I'm completely vasting one pointer.

(This all motivated by GCC 3.3's strict alignment stuff...)
Nov 13 '05 #1
3 4971
dis
"Arto Huusko" <ar*********@utu.fi> wrote in message
news:pa****************************@utu.fi...
Hello,

I'm wondering about the portability and correctness of the
following scenario.

I have a generic doubly linked list structure, and it contains
generic nodes that look like this:

struct node
struct node *head;
struct node *tail;
};

struct list
{
struct node *first;
struct node *last;
};

Now, I want to use the generic list and node structures so that

- The list and its nodes stay completely generic.
That means I can have generic functions like

add_head(struct list *, struct node *);

Any list can be traversed and examined and manipulated
with the generic functions, as long as the operations
are stay on the generic level.

- The nodes can contain application specific data. That is:
the node structure is extended by adding application specific
fields, which can only be accessed if the application knows
what kind of node it is using.

Application specific node would look like

struct my_app_node
{
struct node node;
int datafield1;
char *datafield2;
etc.
};

Now, an application could do:

struct list mylist;
struct my_app_node node1;
struct my_app_node node2;

init_list(&mylist);

add_head(&mylist, (struct node *)&node1);
add_head(&mylist, (struct node *)&node2);

So, is this correct ISO C? Is this portable?
Yes, this code fragment is conforming.
I know that I could do also:

add_head(&mylist, &node1.node);
But, of course, both questions boil down to: can I trust
that offset of "node" inside my_app_node is 0? And also,
does the compiler let me get away with the cast?


Yes, offsetof(struct my_app_node, node) is guaranteed to yield a value of 0.
The cast is necessary as a pointer to a structure object points to its
initial member after suitable conversion.

[snip]
Nov 13 '05 #2
Arto Huusko wrote:

I'm wondering about the portability and correctness of the
following scenario.

I have a generic doubly linked list structure, and it contains
generic nodes that look like this:

struct node
{
struct node *head;
struct node *tail;
};

struct list
{
struct node *first;
struct node *last;
};

Now, I want to use the generic list and node structures so that

- The list and its nodes stay completely generic.
That means I can have generic functions like

add_head(struct list *, struct node *);


You have to do two basic things. Have the data accessible by code
the application provides, because ONLY the application knows the
type of that data. Have the lists manipulated by code that
preserves those generic pointers, which have to be of type void*.
Thus your basic node will look something like:

struct node {
struct node *next;
struct node *prev;
void* dataptr;
}

This will NOT be in the published header file, which will only
contain:

typedef struct node *node;

keeping the definition totally hidden from the application. Now
your call will be something like:

add_head(node thelist, void *datum);

where thelist was created by the call:

node thelist = createlist();

and you will probably also want

void destroylist(node thelist, freedata destroydata);

where destroydata is a pointer to a routine receiving void* and
releasing the data it points to. This may be required for other
operations, and you might supply it once and for all via
createlist.

Now createlist can malloc a struct node, initialize the pointers
to null, or to point back to the head if desired, with a NULL
datum, and you can henceforce use that dummy node to hold the
first and last pointers.

An example is worth a good deal of rambling. You can see exactly
this sort of generic operation in hashlib.zip, especially noting
the example usages in wdfreq and markov. It can be found at:

<http://cbfalconer.home.att.net/download/>

--
Chuck F (cb********@yahoo.com) (cb********@worldnet.att.net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net> USE worldnet address!
Nov 13 '05 #3
CBFalconer wrote:

You have to do two basic things. Have the data accessible by code
the application provides, because ONLY the application knows the
type of that data. Have the lists manipulated by code that
preserves those generic pointers, which have to be of type void*.
Thus your basic node will look something like:

struct node {
struct node *next;
struct node *prev;
void* dataptr;
}
[...]


This approach works, and has some advantages. One is that
a given data item can exist simultaneously in an arbitrary
number of lists.

However, it also has a disadvantage: The linkage information
is split away from the "payload," and this gets you into issues
of allocating storage for the two independently, worrying about
all those tiny allocations of pared-down `struct node' objects,
and so on. All these issues are manageable, but they do require
some management -- and if you don't need the full flexibility
of CBF's method, the O.P.'s technique of embedding the links
in the same struct as the payload can be more convenient.

Back to the O.P.'s original question: In a situation like

struct node {
struct node *next;
struct node *prev;
};

struct data {
struct node links;
int payload[42];
double trouble[2];
};

.... is the `links' member of `struct data' guaranteed to be
at offset zero within the struct? Yes, says Section 6.7.2.1
paragraph 13: "A pointer to a structure object, suitably
converted, points to its initial member [...] and vice versa.
There may be unnamed padding within a structure object, but not
at its beginning." So you can safely feed a pointer to the
`links' member into your linked-list package, get that same
pointer back later on, and convert it into a `struct data*'
without trouble:

struct list mylist;
struct data mydata;
struct data *dptr;
...
add (&mylist, &mydata.links);
...
dptr = (struct data*) get (&mylist);

In fact, you can do a little bit more without too much
trouble. Suppose you want each `struct data' to exist in
exactly two lists (or in any fixed number of lists). You can
still keep the linkage information with the payload:

struct data {
struct node links1;
struct node links2;
int payload[42];
double trouble[2];
};

The `links1' element is at offset zero, and can be handled as
before. The `links2' element is not at offset zero, but it is
at a constant offset, namely `offsetof(struct data, links2)'.
You can insert the struct into a secondary list like

struct list list2;
struct data mydata;
...
add (&list2, &mydata.links2);

Later on when you want to retrieve it you must work a little
harder (the following is spelled out for clarity; in practice
you'd probably abbreviate things a bit):

struct list *lptr;
struct data *dptr;
...
lptr = get (&list2);
dptr = (struct data *)
( (char*)lptr - offsetof(struct data, links2) );

That is, you retrieve a pointer to a `links2' element (because
that's the sort of pointer you put into the list in the first
place), and then you "back up" by a known distance to find the
start of the `struct data' that contains it. Of course, you've
got to be careful not to mix `links1' and `links2' pointers in
the same list; this technique requires that you know what offset
to use.

For a small, fixed number of lists -- one is the commonest
case, sparse matrix representations sometimes use a "row" list
and a "column" list -- this technique can be recommended. It's
not good for a variable number of lists (because it's hard to
keep track of the proper offsets) or for a large number of lists
(because the bookkeeping gets unwieldy and the chance of error
grows); for those circumstances I'd prefer CBF's technique.

--
Er*********@sun.com
Nov 13 '05 #4

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

11
by: Bradford Chamberlain | last post by:
I work a lot with multidimensional arrays of dynamic size in C, implementing them using a single-dimensional C array and the appropriate multipliers and offsets to index into it appropriately. I...
13
by: John | last post by:
In the course of an assignment, I learned the hard way that I shouldn't try to free a malloc'd member of a malloc'd structure after having freed that structure (i.e., free( structure ); free(...
15
by: damian birchler | last post by:
Hi I'm wondering of what type a structure is. Of course, it is a _structure_, but an array isn't an _array_ either. So of what type is a structure? I'd say a pointer, am I right?
15
by: junky_fellow | last post by:
I am trying to find the offset of a member "mbr" inside a structure "str" as follows: offset = &(struct str *)0->mbr; But, on compilation I get the following error: cc: Error: m1.c, line 55:...
10
by: junky_fellow | last post by:
I am trying to print the offset of a particulat member in a structure, but it's not working. I am using the following expression to print the offset. &(struct my_struct *)0->member_name ...
1
by: J | last post by:
I'm interfacing with a C api (via Interop) which uses the following typedef struct... typedef struct _columnflags { BYTE bNoUpdate : 1; BYTE bSetToNull : 1; BYTE bDefault : 1; }...
1
by: Brad | last post by:
Using vb.net, I am attempting to copy a byte array buffer to a structure but need to specify an offset, in bytes, from the top of the structure. I have studied the Marshal class but it appears I...
19
by: Rahul | last post by:
Hi, Is there a way to find the offset of a class member at compile time. e.g. class A{ int i; int j; char c; }; Here the offset of c = 8 bytes from the start of an object of A (assuming 4...
7
by: p.lavarre | last post by:
How do I vary the byte offset of a field of a ctypes.Structure? How do I "use the dynamic nature of Python, and (re-)define the data type after the required size is already known, on a case by...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.