如何使用"offsetof"以一种标准的方式访问字段? [英] How do one use `offsetof` to access a field in a standard conforming way?
问题描述
假设我有一个结构并将偏移量提取到成员:
struct A {
int x;
};
size_t xoff = offsetof(A, x);
如何给定指向struct A
的指针,以一种标准的方式提取成员?当然,假设我们有正确的struct A*
和正确的偏移量.一种尝试是做类似的事情:
int getint(struct A* base, size_t off) {
return *(int*)((char*)base + off);
}
这可能会起作用,但是请注意,例如,如果指针是同一数组(或末尾的一个)的指针,则似乎仅在标准中定义了指针算术,而不必如此.因此从技术上讲,该构造似乎依赖于不确定的行为.
另一种方法是
int getint(struct A* base, size_t off) {
return *(int*)((uintptr_t)base + off);
}
也可能会起作用,但是请注意,不需要intptr_t
存在,而且据我所知,intptr_t
上的算术运算不需要产生正确的结果(例如,我记得某些CPU具有此功能处理非字节对齐的地址,这表明对于数组中的每个char
,intptr_t
以8为步长递增.)
似乎标准中有一些遗忘的东西(或者我错过了的东西).
每个 C标准, 7.19通用定义<stddef.h>
,第3段,offsetof()
定义为:
宏是
NULL
扩展为实现定义的空指针常量;和
offsetof(*type*, *member-designator*)
扩展为具有类型的整数常量表达式
size_t
,其 value 是字节的偏移量,以字节为单位 结构成员(由 member-designator 指定),从 结构的开头(由 type 指定).
因此,offsetoff()
返回一个以 bytes 个字节为单位的偏移量.
和 6.2.6.1常规,第4段指出:
存储在任何其他对象类型的非位字段对象中的值 包括 n × CHAR_BIT 位,其中 n 是该类型对象的大小,以字节为单位.
由于 CHAR_BIT 被定义为char
中的位数,所以char
是一个 byte .
因此,按照标准,这是正确的:
int getint(struct A* base, size_t off) {
return *(int*)((char*)base + off);
}
将base
转换为char *
并将off
字节添加到地址.如果off
是offsetof(A, x);
的结果,则结果地址是base
指向的structure A
中的x
的地址.
您的第二个示例:
int getint(struct A* base, size_t off) {
return *(int*)((intptr_t)base + off);
}
取决于有符号的intptr_t
值与无符号的size_t
值被无符号相加的结果.
Let's suppose I have a struct and extract the offset to a member:
struct A {
int x;
};
size_t xoff = offsetof(A, x);
how can I, given a pointer to struct A
extract the member in a standard conforming way? Assuming of course that we have a correct struct A*
and a correct offset. One attempt would be to do something like:
int getint(struct A* base, size_t off) {
return *(int*)((char*)base + off);
}
Which probably will work, but note for example that pointer arithmetics only seem to be defined in the standard if the pointers are pointers of the same array (or one past the end), this need not be the case. So technically that construct would seem to rely on undefined behaviour.
Another approach would be
int getint(struct A* base, size_t off) {
return *(int*)((uintptr_t)base + off);
}
which also probably would work, but note that intptr_t
is not required to exist and as far as I know arithmetics on intptr_t
doesn't need to yield the correct result (for example I recall some CPU has the capability to handle non-byte aligned addresses which would suggest that intptr_t
increases in steps of 8 for each char
in an array).
It looks like there's something forgotten in the standard (or something I've missed).
Per the C Standard, 7.19 Common definitions <stddef.h>
, paragraph 3, offsetof()
is defined as:
The macros are
NULL
which expands to an implementation-defined null pointer constant; and
offsetof(*type*, *member-designator*)
which expands to an integer constant expression that has type
size_t
, the value of which is the offset in bytes, to the structure member (designated by member-designator), from the beginning of its structure (designated by type).
So, offsetoff()
returns an offset in bytes.
And 6.2.6.1 General, paragraph 4 states:
Values stored in non-bit-field objects of any other object type consist of n × CHAR_BIT bits, where n is the size of an object of that type, in bytes.
Since CHAR_BIT is defined as the number of bits in a char
, a char
is one byte.
So, this is correct, per the standard:
int getint(struct A* base, size_t off) {
return *(int*)((char*)base + off);
}
That converts base
to a char *
and adds off
bytes to the address. If off
is the result of offsetof(A, x);
, the resulting address is the address of x
within the structure A
that base
points to.
Your second example:
int getint(struct A* base, size_t off) {
return *(int*)((intptr_t)base + off);
}
is dependent upon the result of the addition of the signed intptr_t
value with the unsigned size_t
value being unsigned.
这篇关于如何使用"offsetof"以一种标准的方式访问字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!