热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

获取结构内部变量的偏移量基于NULL指针,但是为什么呢?

如何解决《获取结构内部变量的偏移量基于NULL指针,但是为什么呢?》经验,为你挑选了3个好方法。

我在youtube视频上找到了一个技巧,解释了如何使用NULL指针获取结构成员的偏移量。我了解下面的代码片段(强制转换,&符等),但我不明白为什么这适用于NULL指针。我以为NULL指针不能指向任何东西。因此,我无法从心理上想象它是如何工作的。其次,编译器并不总是将NULL指针表示为0,因此有时它是一个非零值。但是比起这段代码如何正常工作呢?还是不能正常工作?

#include 

int 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实现一起使用。但是它会系统地与其他一些人产生错误的答案,并且在某些情况下,它可能会在相当一部分时间内产生正确的答案,而在其余时间中会产生错误的答案。



1> John Bolling..:

我在youtube视频上找到了一个技巧,解释了如何使用NULL指针获取结构成员的偏移量。

好吧,至少您是来这里询问随机出现的互联网建议的。当然,我们本身就是Internet资源,但是我想认为我们的结构和声誉为您提供了一个评估我们所说内容可靠性的基础。

我了解下面的代码片段(强制转换,&符等),但我不明白为什么这适用于NULL指针。我以为NULL指针不能指向任何东西。

是的,从C语义的角度来看,空指针绝对不指向任何东西,并且NULL是空指针常量。

因此,我无法从心理上想象它是如何工作的。

(有缺陷的)想法是

NULL等效于在平面地址空间中指向地址0的指针(不安全的假设);

((MyStructType * )NULL)->c指定驻留在该地址c的全部类型的假设对象的成员MyStructType标准不支持);

施加&运算符生成,这样的部件的地址它是否实际上存在(具有不是由标准所支持); 和

将结果地址转换为整数会在假定的平面地址空间中产生一个地址,以C的大小为单位表示char绝不保证);

因此,所得整数同时代表绝对地址和偏移量(遵循先前的假设,因为假设结构的假定基址为0)。

其次,编译器并不总是将NULL指针表示为0,因此有时它是一个非零值。

完全正确,这是所提出方案中的缺陷之一。

但是比起这段代码如何正常工作呢?还是不能正常工作?

尽管该标准没有提供依据来证明依赖于代码所宣称的行为是合理的,但这并不意味着它一定会失败。C实现确实需要在内部上保持一致,即它们如何表示空指针,以及(在某种程度上)它们在指针与整数之间如何转换。事实证明,代码对那些事情的假设实际上是由实现满足的。

因此在实践中,该代码确实可与许多C实现一起使用。但是它会系统地与其他一些人产生错误的答案,并且在某些情况下,它可能会在相当一部分时间内产生正确的答案,而在其余时间中会产生错误的答案。



2> king_nak..:

请注意,此代码实际上是未定义的行为。永远都不允许取消引用NULL指针,即使没有访问任何值,也只引用地址(这是linux内核利用的根本原因)

使用offsetof而不是一个保存的替代方案。


至于为什么它似乎可以使用NULL指针:它假定NULL为0。基本上,您可以使用任何指针进行计算:

MyStructType t; 
unsigned off = (unsigned)(&(&t)->c) - (unsigned)&t;

如果&t == 0,则变为:

 unsigned off = (unsigned)(&(0)->c) - 0;

减0是无操作


语句“绝对不允许取消引用NULL指针”是不正确的。总的来说,C标准没有指定允许或不允许的内容。它指定或不指定如果程序执行各种操作会发生什么。关于取消引用空指针,这并不是说不允许这样做-C标准只是说C标准没有定义行为。这意味着C实现可能会**允许操作,甚至C实现可能会定义它。(不过,“偏移量”是首选技术。)

3> Serve Laurij..:

此代码是特定于平台的。此代码可能会在一个平台上导致未定义的行为,并且可能在其他平台上起作用。这就是C标准要求每个库都实现offsetof宏的原因,该宏可以扩展为引用NULL指针之类的代码,至少您可以确保代码不会在任何平台上崩溃

typedef struct Struct
{
  double d;
} Struct;

offsetof(Struct, d)


推荐阅读
author-avatar
手机用户2502928203
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有