0

Assume T1 and T2 are two types and the following struct is given:

struct s
{
  T1 x;
  T2 y;
}

Further, assume that we have an object of type struct s:

struct s a;

From this, we can calculate a pointer to the second struct member of the object:

T2 *q = &s.y;

What I am looking for is a portable way to calculate from q a pointer p of type struct s * such that p points at the object a.

Marc
  • 4,327
  • 4
  • 30
  • 46
  • 3
    Use [the `offsetof` macro](https://en.cppreference.com/w/c/types/offsetof)? – Some programmer dude Dec 22 '18 at 12:13
  • Linux uses `offsetof` in its [`container_of` macro](http://www.kroah.com/log/linux/container_of.html). – vgru Dec 22 '18 at 12:36
  • http://www.kroah.com/log/linux/container_of.html found this link. Has good info. – pradeepmcp Dec 22 '18 at 14:36
  • Someone voted down this question without leaving neither constructive critique nor explaining why this question should be voted down. I would be grateful if they could disclose their motivation. – Marc Jan 13 '19 at 14:01

3 Answers3

4

Given T2 *q = &s.y;, then char *x = (char *) q - offsetof(struct s, y); defines an x that points to the first byte of s. The offsetof macro is defined in <stddef.h>.

One might then expect struct s *p = (struct s *) x; define a p that points to s, depending on how pedantically one wishes to interpret the C standard. (The conversion of a pointer to char to a pointer to the structure it is the first byte of is not explicitly defined by the standard. We can take the standard’s specification that a pointer “converted back,” in C 2018 6.3.2.3 7, to its original type to cover this case.) I expect it to work in all normal C implementations.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • `offsetof` is standard, `char*` can alias anything, pointer to the structure is always the pointer to the first element. I cannot think of an implementation where this wouldn't work in practice, although I am sure someone implementing gcc optimizations will probably [laugh at my naive expectations](https://lwn.net/Articles/478657/). – vgru Dec 22 '18 at 12:34
  • @Groo: `offsetof` and aliasing other things is not the the problem. The standard says `offsetof` tells us where the bytes of structure members are, and the standard says `char` may be used to access the bytes of an object. The problem is the standard does not (clearly and explicitly) tell us that we can fiddle around with character pointers and then convert them to pointers to other objects. – Eric Postpischil Dec 22 '18 at 12:41
  • @groo that example form 7 years old link just shows how to do not use bitfields. And it was enough to wrap the bitfield into another structure to prevent this behaviour (if they planned to add another bits in the future) – 0___________ Dec 22 '18 at 12:46
  • 1
    @P__J__: The issue discussed at that link is not due to bit fields. – Eric Postpischil Dec 22 '18 at 12:47
  • @P__J__: bitfields are part of the standard, they become problematic when people think they are portable or that the compiler makes guarantees about their exact placement. Also, gcc was written more than 30 years ago, 7 years ago it was already quite mature. :) – vgru Dec 22 '18 at 12:55
  • @Groo yes they are. But there are a lots of implementation problems when they are mixed with "normal" types and not wrapped into the separate structure. – 0___________ Dec 22 '18 at 13:39
  • Would this solution be strictly conforming if I wrapped my struct in an anonymous union so that it is aliased by a char array? Like so: union u { struct s { T1 x; T2 y; }; char c[sizeof (struct s)]; }; And would this prevent strict-aliasing optimizations? – Marc Dec 22 '18 at 13:58
  • 1
    @Marc: I think the intent of the standard is that `(struct s *) ((char *) q - offsetof(struct s, y))` will give a valid pointer to the structure, and using the union would be unnecessary. If we are going to be literal about the standard, you could use the union to find the address of `s.c[0]` in a strictly conforming way, but I am not sure that can be converted to the address of `s.c` without the same interpretation of intent. (Once you did have the address of `s.c`, it could be converted to the address of `s`; the standard is clear about that.) You could ask with a language-lawyer tag. – Eric Postpischil Dec 22 '18 at 14:10
  • 2
    @Marc: A workaround I think may be strictly conforming even with a strict interpretation of the standard is to use `union u { struct s { T1 x; T2 y; } s; char c[sizeof(struct s); char t; }`. Then a pointer to member `y` converted to `char *` points to the first byte of `y`. This is also a pointer to an element of `c`—it must be, since it has the right type and it is the same byte. Then pointer arithmetic with `offsetof` can get a pointer to the first element of `c`. That must also be a pointer to `t`, again because it is the right type and the same byte. So it is a pointer to a union member,… – Eric Postpischil Dec 22 '18 at 14:14
  • … which the C standard explicitly says may be converted to a pointer to the union. (And the pointer to the union may be converted to a pointer to the struct, either directly or simply as `&p->s`.) – Eric Postpischil Dec 22 '18 at 14:15
  • @EricPostpischil: I think you should include the last comment as a part of your answer. – vgru Dec 22 '18 at 15:25
  • @Groo: It could go into a question with a language-lawyer tag, but I think it is a bit much for routine questions. – Eric Postpischil Dec 22 '18 at 15:59
  • @Groo Maybe I should have added a language-lawyer tag. – Marc Jan 13 '19 at 14:03
1

You are looking for container_of(); from Wikipedia

#define container_of(ptr, type, member) ((type *)((char *)(1 ? (ptr) : &((type *)0)->member) - offsetof(type, member)))

or a more simple, but less safe

#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)))

In your case, you would apply it like

struct *s = container_of(q, struct s, y);
ensc
  • 6,704
  • 14
  • 22
-1
T2 *py = &z.y

T1 *px = (T1 *)((char *)py - ((char *)&z.y - (char *)&z.x));
0___________
  • 60,014
  • 4
  • 34
  • 74