简体   繁体   English

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

[英]Access struct variable value by pointer offset

I have a struct which looks like: 我有一个看起来像的结构:

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

This struct was initialized correctly. 此结构已正确初始化。 Due to design of an algorithm I need to read these three attributes values by a pointer offset. 由于算法的设计,我需要通过指针偏移量来读取这三个属性值。 I thank about declare an array which have the size in bytes of these attributes. 我感谢声明一个数组,这些数组的大小以字节为单位。 Just like: 就像:

int sizeOfStructAttributes = {1, 2, 4};

And finally to access these values do something like: 最后,要访问这些值,请执行以下操作:

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

Expected result: 预期结果:

9 7 25000 9 7 25000

Could you help me please? 请问你能帮帮我吗?

You have many problems with the code I will try to go through them all: 您的代码有很多问题,我将尝试全部解决:

1- Your structure has padding values that depends on the architecture you are targeting maybe 3 or 7 bytes after the first member (packetID) it depends on the architecture and compiler. 1-您的结构的填充值取决于您要定位的体系结构,在第一个成员(packetID)之后,它可能取决于结构和编译器3或7个字节。

2- You are initializing the pointer in a wrong way, it should be: 2-您以错误的方式初始化了指针,应该是:

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

3- cout should be: 3- cout应该是:

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

4- In case you are creating array of this strcutrue, I am not sure if you will face a problem of padding or not. 4-如果您要创建此结构的数组,则不确定是否会遇到填充问题。 It happens in very rare cases when you use different packing and padding due to mixing your code with other libraries that are compiled with different compiler directives or even uses #pragma to modify the behavior of the compiler during the compile time. 在极少数情况下,由于将代码与使用不同编译器指令编译的其他库混合甚至使用#pragma在编译期间修改编译器的行为而使用不同的打包和填充时,会发生这种情况。

Finally I am sure there is no need at all to enumerate struct members with a pointer. 最后,我确定根本不需要使用指针枚举结构成员。

I encourage you to read about struct padding and packing, good place to start is this question on SO: Structure padding and packing 我鼓励您阅读有关struct padding和packing的文章,好的起点是SO上的以下问题: Structure padding和packing

One thing for sure, you won't be able to write these offsets manually. 可以肯定的是,您将无法手动编写这些偏移量。 This is absolutely not a stable way of doing things, because your compiler might do optimizations such as aligning your struct members . 这绝对不是一种稳定的操作方式,因为编译器可能会进行优化,例如对齐struct成员

What you can do is this: 您可以执行以下操作:

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

Notice that this is the byte difference in size. 请注意,这是字节大小的差异。

Having told you how to do that, I have to say like people said in the comments, please find another way of doing this. 告诉您如何操作后,我必须像人们在评论中说的那样说,请找到另一种方式。 This is not safe, unless you absolutely know what you're doing. 这是不安全的,除非您完全知道自己在做什么。

There are two problems with your approach: 您的方法存在两个问题:

Firstly, it requires you to get the sizes right. 首先,它要求您正确选择尺寸。 Use sizeof to do that. 使用sizeof可以做到这一点。 So your array would look like: 因此,您的数组如下所示:

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

The second (more serious) problem is that you don't allow for padding in your structure. 第二个(更严重的)问题是您不允许在结构中填充。 Almost all compilers will (unless specially instructed), insert a padding byte between packet_id and packet_size so that everything is nicely aligned. 几乎所有的编译器都将(除非特别指示)在packet_id和packet_size之间插入一个填充字节,以便所有内容都能很好地对齐。 Fortunately, there is a solution for that too - use the offsetof macro (defined in stddef.h): 幸运的是,也有一个解决方案-使用offsetof宏(在stddef.h中定义):

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

The code then becomes: 然后,代码变为:

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

Actually: the above code fixes a third problem with your code: sizeof() returns the size in bytes, which is unlikely to be the element count. 实际上:上面的代码解决了您的代码的第三个问题: sizeof()返回以字节为单位的大小,这不太可能是元素计数。

Finally, your variables have a typo: meteorology is concerned with whether the weather will be fine or not. 最后,您的变量有错别字:气象与天气是否好有关。 You have confused the two words and I am pretty sure you mean "weather". 您已经混淆了这两个词,我很确定您的意思是“天气”。

Your mistake is that you've assumed that the class has no padding between the members. 您的错误是您假设该类在成员之间没有填充。 But there must be padding in order to meet the alignment requirements of the members. 但是必须有填充,才能满足成员的对齐要求。 Thus the offsets are not what you assume. 因此,偏移量不是您所假定的。

To get the offset of a class member, you can use the offsetof macro provided by the standard library. 要获取类成员的偏移量, 可以使用标准库提供的offsetof宏。 That said, without knowing what you need it for, I remain skeptical about it being appropriate. 就是说,在不知道您需要什么的情况下,我仍然怀疑它是否合适。 Note that offsetof works only if your class is a standard layout class. 请注意, offsetof仅在您的类是标准布局类时才有效。 Otherwise the behaviour will be undefined. 否则,行为将是不确定的。 Your example WHEATHER_STRUCT is standard layout. 您的示例WHEATHER_STRUCT是标准布局。

 cout << &pointer << ' '; 

Something like this can not possibly have the output that you expect. 这样的事情可能无法获得您期望的输出。 You take the address of the pointer, it cannot possibly give you the value of the pointed object that you wanted. 您使用指针的地址,它可能无法为您提供所需的指向对象的值。

The way to get the pointed value is the indirection operator. 获得指示值的方法是间接运算符。 But, indirection operator can only work correctly if the pointer is of correct type ( float* for float members, uint16_t* for uint16_t members ...) but it cannot be of correct type since it has to be a pointer to a byte for the pointer arithmetic to work with the offsets. 但是,仅当指针的类型正确时,间接操作符才能正确工作(对于float成员, float* ,对于uint16_t成员, uint16_t* ...),但是由于它必须是指向字节的指针,因此它不能为正确的类型。使用偏移量的指针算法。

Besides the offset, you also need to know the type of the variable in order to interpret the value. 除了偏移量,还需要了解变量的类型才能解释该值。 You could store the type in some structure. 您可以将类型存储在某种结构中。 But you cannot cast the pointer to a type determined at runtime, so what you need is some runtime flow-structure such as a switch or a jump table for the conversion. 但是你可以将指针不投在运行时确定的类型,所以你需要的是一些运行时流结构,如switch或跳转表的转换。

You'd better do not use pointer hack: one day underlying memory layout will be changed and your program may corrupt it. 您最好不要使用指针黑客:有一天底层的内存布局将被更改,您的程序可能会破坏它。 Try to simulate metadata instead. 尝试模拟元数据。

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 << " ";
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM