通过指针偏移量访问结构变量值

时间:2018-10-19 08:42:24

标签: c++ pointers struct

我有一个看起来像这样的结构

#pragma pack(1)
typedef struct WHEATHER_STRUCT {
    uint8_t packetID; // Value 9
    uint16_t packetSize; // Value 7
    float cloudLayerAltitude; // Value 25000
} Wheather_Struct

此结构已正确初始化。由于算法的设计,我需要通过指针偏移量来读取这三个属性值。我感谢声明一个数组,这些数组的大小以字节为单位。就像:

int sizeOfStructAttributes = {1, 2, 4};

最后要访问这些值,请执行以下操作:

pointer = (*this->wheather_struct->packetID)
for (i=0; i<sizeof(sizeOfStructAttributes); i++)
    cout << &pointer << ' ';
    pointer = pointer + sizeOfStructAttributes[i];

预期结果:

  

9   7   25000

能帮我吗?

5 个答案:

答案 0 :(得分:2)

您的代码有很多问题,我将尝试全部解决:

1-您的结构的填充值取决于您要定位的体系结构,可能在第一个成员(packetID)之后3或7个字节,取决于结构和编译器。

2-您以错误的方式初始化了指针,应该是:

pointer = &(this->wheather_struct->packetID);

3- cout应该是:

cout << *((datatype*)pointer) << ' '; 
//datatype should be different in each loop iteration of course.

4-如果要创建此结构的数组,则不确定是否会遇到填充问题。在极少数情况下,由于将代码与其他使用不同编译器指令编译的库混合甚至使用#pragma在编译期间修改编译器的行为而使用不同的打包和填充时,就会发生这种情况。

最后,我确定根本不需要使用指针枚举结构成员。

我鼓励您阅读有关结构填充和打包的信息,开始的好地方是SO上的以下问题: Structure padding and packing

答案 1 :(得分:0)

可以肯定的是,您将无法手动编写这些偏移量。这绝对不是一种稳定的工作方式,因为您的编译器可能会进行诸如aligning your struct members之类的优化。

您可以执行以下操作:

Wheather_Struct w;
long offsetsOfStructAttributes[3] = {0, 
                                     (char*)&w.packetSize - (char*)&w.packetID, 
                                     (char*)&w.cloudLayerAltitude - (char*)&w.packetID};

请注意,这是字节大小的差异。

已经告诉您如何做到这一点,我必须像人们在评论中说的那样说,请找到另一种方式。这是不安全的,除非您完全知道自己在做什么。

答案 2 :(得分:0)

您的方法有两个问题:

首先,它要求您正确设置尺寸。使用sizeof来做到这一点。所以你的数组看起来像:

size_t sizeOfStructAttributes = {sizeof(wheather_struct::packet_id),
                               sizeof(wheather_struct::packet_size),
                               sizeof(wheather_struct::cloudLayerAltitude) };

第二个(更严重的)问题是您不允许在结构中填充。几乎所有的编译器都将(除非特别指示)在packet_id和packet_size之间插入一个填充字节,以便所有内容都能很好地对齐。幸运的是,也有一个解决方案-使用offsetof宏(在stddef.h中定义):

size_t offsetOfStructAttributes = {offsetof(wheather_struct, packet_id),
                                 offsetof(wheather_struct, packet_size),
                                 offsetof(wheather_struct, cloudLayerAltitude) };

然后代码变为:

for (size_t offset: offsetsOfStructAttributes) {
    pointer = &(this->wheather_struct->packetID) + offset
    cout << pointer << ' ';
}

实际上:上面的代码解决了您的代码的第三个问题:sizeof()返回以字节为单位的大小,这不太可能是元素计数。

最后,您的变量有错别字:气象与天气是否好有关。您已经混淆了这两个词,我很确定您的意思是“天气”。

答案 3 :(得分:0)

您的错误是您假设类在成员之间没有填充。但是必须有填充,才能满足成员的对齐要求。因此,偏移量不是您所假设的。

要获取类成员的偏移量,您可以 使用标准库提供的offsetof宏。就是说,在不知道您需要什么的情况下,我仍然怀疑它是否合适。请注意,offsetof仅在您的类是标准布局类时才有效。否则,行为将是不确定的。您的示例WHEATHER_STRUCT是标准布局。

cout << &pointer << ' ';

类似这样的可能无法获得您期望的输出。您使用指针的地址,它可能无法为您提供所需的指向对象的值。

获得指示值的方法是间接运算符。但是,仅当指针的类型正确(对于float成员,float*,对于uint16_t成员,uint16_t*)时,间接操作符才能正确工作,但由于必须为a指向字节的指针,以便指针算术可以使用偏移量。

除了偏移量,还需要了解变量的类型才能解释该值。您可以将类型存储在某种结构中。但是您不能将指针强制转换为在运行时确定的类型,因此您需要的是一些运行时流程结构,例如switch或用于转换的跳转表。

答案 4 :(得分:0)

您最好不要使用指针黑客:有一天底层的内存布局将被更改,您的程序可能会破坏它。 尝试模拟元数据。

enum WheatherStructFields
{
    wsfPacketID,
    wsfPacketSize,
    wsfCloudLayerAltitude,
    wsfNone
};

typedef struct WHEATHER_STRUCT
{
    uint8_t packetID;
    uint16_t packetSize;
    float cloudLayerAltitude;
    void OutFieldValue(std::ostream& os, WheatherStructFields whatField)
    {
        switch (whatField)
        {
        case wsfPacketID:
            os << (int)packetID;
            break;
        case wsfPacketSize:
            os << packetSize;
            break;
        case wsfCloudLayerAltitude:
            os << cloudLayerAltitude;
            break;
        default:
            os << "Unsupported field: " << whatField;
        }
    }
} Wheather_Struct;


int main()
{
    Wheather_Struct weather = { 9, 7, 25000 };
    for (WheatherStructFields whatField = wsfPacketID; whatField < wsfNone; 
        whatField = (WheatherStructFields)((int)whatField + 1))
    {
        weather.OutFieldValue(std::cout, whatField);
        std::cout << " ";
    }
}