我在youtube视频上找到了一个技巧,解释了如何使用NULL指针获取结构成员的偏移量。我了解下面的代码片段(强制转换,&符等),但我不明白为什么这适用于NULL指针。我以为NULL指针不能指向任何东西。因此,我无法从心理上想象它是如何工作的。其次,编译器并不总是将NULL指针表示为0,因此有时它是一个非零值。但是比起这段代码如何正常工作呢?还是不能正常工作?
#includeint main(void) { /* Getting the offset of a variable inside a struct */ typedef struct { int a; char b[23]; float c; } MyStructType; unsigned offset = (unsigned)(&((MyStructType * )NULL)->c); printf("offset = %u\n", offset); return 0; }
John Bolling.. 8
我在youtube视频上找到了一个技巧,解释了如何使用NULL指针获取结构成员的偏移量。
好吧,至少您是来这里询问随机出现的互联网建议的。当然,我们本身就是Internet资源,但是我想认为我们的结构和声誉为您提供了一个评估我们所说内容可靠性的基础。
我了解下面的代码片段(强制转换,&符等),但我不明白为什么这适用于NULL指针。我以为NULL指针不能指向任何东西。
是的,从C语义的角度来看,空指针绝对不指向任何东西,并且NULL
是空指针常量。
因此,我无法从心理上想象它是如何工作的。
(有缺陷的)想法是
NULL
等效于在平面地址空间中指向地址0的指针(不安全的假设);
((MyStructType * )NULL)->c
指定驻留在该地址c
的全部类型的假设对象的成员MyStructType
(标准不支持);
施加&
运算符生成,这样的部件的地址将它是否实际上存在(具有不是由标准所支持); 和
将结果地址转换为整数会在假定的平面地址空间中产生一个地址,以C的大小为单位表示char
(绝不保证);
因此,所得整数同时代表绝对地址和偏移量(遵循先前的假设,因为假设结构的假定基址为0)。
其次,编译器并不总是将NULL指针表示为0,因此有时它是一个非零值。
完全正确,这是所提出方案中的缺陷之一。
但是比起这段代码如何正常工作呢?还是不能正常工作?
尽管该标准没有提供依据来证明依赖于代码所宣称的行为是合理的,但这并不意味着它一定会失败。C实现确实需要在内部上保持一致,即它们如何表示空指针,以及(在某种程度上)它们在指针与整数之间如何转换。事实证明,代码对那些事情的假设实际上是由实现满足的。
因此在实践中,该代码确实可与许多C实现一起使用。但是它会系统地与其他一些人产生错误的答案,并且在某些情况下,它可能会在相当一部分时间内产生正确的答案,而在其余时间中会产生错误的答案。
我在youtube视频上找到了一个技巧,解释了如何使用NULL指针获取结构成员的偏移量。
好吧,至少您是来这里询问随机出现的互联网建议的。当然,我们本身就是Internet资源,但是我想认为我们的结构和声誉为您提供了一个评估我们所说内容可靠性的基础。
我了解下面的代码片段(强制转换,&符等),但我不明白为什么这适用于NULL指针。我以为NULL指针不能指向任何东西。
是的,从C语义的角度来看,空指针绝对不指向任何东西,并且NULL
是空指针常量。
因此,我无法从心理上想象它是如何工作的。
(有缺陷的)想法是
NULL
等效于在平面地址空间中指向地址0的指针(不安全的假设);
((MyStructType * )NULL)->c
指定驻留在该地址c
的全部类型的假设对象的成员MyStructType
(标准不支持);
施加&
运算符生成,这样的部件的地址将它是否实际上存在(具有不是由标准所支持); 和
将结果地址转换为整数会在假定的平面地址空间中产生一个地址,以C的大小为单位表示char
(绝不保证);
因此,所得整数同时代表绝对地址和偏移量(遵循先前的假设,因为假设结构的假定基址为0)。
其次,编译器并不总是将NULL指针表示为0,因此有时它是一个非零值。
完全正确,这是所提出方案中的缺陷之一。
但是比起这段代码如何正常工作呢?还是不能正常工作?
尽管该标准没有提供依据来证明依赖于代码所宣称的行为是合理的,但这并不意味着它一定会失败。C实现确实需要在内部上保持一致,即它们如何表示空指针,以及(在某种程度上)它们在指针与整数之间如何转换。事实证明,代码对那些事情的假设实际上是由实现满足的。
因此在实践中,该代码确实可与许多C实现一起使用。但是它会系统地与其他一些人产生错误的答案,并且在某些情况下,它可能会在相当一部分时间内产生正确的答案,而在其余时间中会产生错误的答案。
请注意,此代码实际上是未定义的行为。永远都不允许取消引用NULL指针,即使没有访问任何值,也只引用地址(这是linux内核利用的根本原因)
使用offsetof
而不是一个保存的替代方案。
至于为什么它似乎可以使用NULL指针:它假定NULL为0。基本上,您可以使用任何指针进行计算:
MyStructType t; unsigned off = (unsigned)(&(&t)->c) - (unsigned)&t;
如果&t == 0
,则变为:
unsigned off = (unsigned)(&(0)->c) - 0;
减0是无操作
此代码是特定于平台的。此代码可能会在一个平台上导致未定义的行为,并且可能在其他平台上起作用。这就是C标准要求每个库都实现offsetof宏的原因,该宏可以扩展为引用NULL指针之类的代码,至少您可以确保代码不会在任何平台上崩溃
typedef struct Struct { double d; } Struct; offsetof(Struct, d)