首页 > 其他分享 >对于编译错误*((void*)& pulse +16)的理解

对于编译错误*((void*)& pulse +16)的理解

时间:2022-12-14 15:47:07浏览次数:54  
标签:updated 16 double void pulse class

问题

声明一个pulse对象

class Pulse
{
public:
    void Update()
    {
        if(!updated_)
        {
            // ...
        }
    }
private:
    double time_in_;
    double time_out_;
    bool updated_;
    // ...
}

编译一个pulse对象的单元测试,debug正常通过,realse版本报错

error: ‘*((void*)& pulse +16)’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
             if (!updated_)
                  ^~~~~~~~
note: ‘*((void*)& pulse +16)’ was declared here
     Pulse pulse;

分析

看报错的意思应该是updated_变量没有赋初值,导致报错maybe-uninitialized,修复很容易bool updated_ = false既可。
但我决定深挖晦涩的报错*((void*)& pulse +16)是怎么来的?
首先明确一点:计算class的sizeof时成员函数不参与统计。例如:

class A
{
private:
    double x;
    double y;
    bool z;
};
// sizeof(A) = 24

class B
{
public:
    bool init();
    int get();
private:
    double x;
    double y;
    bool z;
};
// sizeof(B) = 24

其中的原理在C++有关class内部的static关键字理解中有解释
知道了class计算sizeof时可以看作没有方法的struct就不难理解*((void*)& pulse +16)的来历,
对于行如class.member的语法糖,底层实现会直接将指针偏移到member的起始位置。

struct Pulse
{
    double time_in_;    // offset = 0
    double time_out_;   // offset = sizeof(double) = 8
    bool updated_;      // offset = sizeof(double) + sizeof(double) = 16
};
因此以下几种表达等效
```cpp
pulse.updated_
(&pulse)->updated_
&(pulse.updated_) = &pulse + 16

结论:class的成员变量按顺序在内存中连续排列,调用某一变量实则通过计算offset进行内存偏移。

扩展

如何计算具体偏移量?
c++提供了offsetof方法,严格讲offsetof是一个宏而不是函数,实现略显晦涩

#define offsetof(st, m) \
    ((size_t)&(((st *)0)->m))

逐个击破,将计算成员位置的初始地址设为0(nullptr)即使nullptr并不包含m成员
此时将0强行cast成st*指针(st *)0进而访问m变量((st *)0)->m
结果取地址&(((st *)0)->m)再次转换为整数(size_t)&(((st *)0)->m)
由于整个过程中只涉及地址转换并未发生对m内存实质性访问,结果不报错。

参考

offsetof - Wikipedia

标签:updated,16,double,void,pulse,class
From: https://www.cnblogs.com/azureology/p/16982334.html

相关文章