Доступ к значению структурной переменной по смещению указателя - PullRequest
0 голосов
/ 19 октября 2018

У меня есть структура, которая выглядит следующим образом:

#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 голосов
/ 19 октября 2018

Лучше не использовать взлом указателя: однажды основной макет памяти будет изменен, и ваша программа может его испортить.Попробуйте вместо этого смоделировать метаданные.

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 << " ";
    }
}
0 голосов
/ 19 октября 2018

Ваша ошибка в том, что вы предположили, что класс не имеет отступов между членами.Но должно быть дополнение, чтобы удовлетворить требования выравнивания участников.Таким образом, смещения не соответствуют вашим ожиданиям.

Чтобы получить смещение члена класса, вы можете использовать макрос offsetof, предоставляемый стандартной библиотекой.Тем не менее, не зная, для чего это нужно, я скептически отношусь к тому, что это уместно.Обратите внимание, что offsetof работает, только если ваш класс является стандартным классом макета.В противном случае поведение будет неопределенным.Ваш пример WHEATHER_STRUCT - это стандартная раскладка.

cout << &pointer << ' ';

Примерно так может не иметь ожидаемого результата.Вы берете адрес указателя, он не может дать вам значение указанного объекта, который вы хотели.

Способ получения указанного значения - оператор косвенного обращения.Но оператор косвенного обращения может работать корректно, только если указатель имеет правильный тип (float* для членов с плавающей запятой, uint16_t* для членов uint16_t ...), но он не может быть правильного типа, поскольку он должен быть указателем на байтчтобы арифметика указателя работала со смещениями.

Помимо смещения, вам также необходимо знать тип переменной, чтобы интерпретировать значение.Вы можете хранить тип в некоторой структуре.Но вы не можете привести указатель к типу, определенному во время выполнения, поэтому вам нужна некоторая структура потока времени выполнения, такая как switch или таблица переходов для преобразования.

0 голосов
/ 19 октября 2018

Одно можно сказать наверняка, вы не сможете писать эти смещения вручную.Это абсолютно нестабильный способ действий, потому что ваш компилятор может выполнять оптимизации, такие как выравнивание членов структуры .

Что вы можете сделать так:

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

Обратите внимание, что это различие в размерах в байтах.

Сказав вам, как это сделать, я должен сказать, что люди говорили в комментариях, пожалуйста, найдите другой способ сделать это.Это небезопасно, если вы абсолютно не знаете, что делаете.

0 голосов
/ 19 октября 2018

У вашего подхода есть две проблемы:

Во-первых, он требует правильного определения размеров.Используйте 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() возвращает размер в байтах, который вряд ли будет числом элементов.

Наконец, ваши переменные имеют опечатку: метеорология занимается вопросом, будет ли погодабыть в порядке или нет.Вы перепутали два слова, и я уверен, что вы имеете в виду «погода».

0 голосов
/ 19 октября 2018

У вас много проблем с кодом, я постараюсь просмотреть их все:

1 - ваша структура имеет значения заполнения, которые зависят от архитектуры, на которую вы ориентируетесь, может быть, через 3 или 7 байт после первого члена (PackageID) это зависит от архитектуры и компилятора.

2- Вы неверно инициализируете указатель, оно должно быть:

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

3-cout должно быть:

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

4 - Если вы создаете массив из этой строки, я не уверен, столкнетесь ли вы с проблемой заполнения или нет.Это происходит в очень редких случаях, когда вы используете другую упаковку и заполнение из-за смешивания вашего кода с другими библиотеками, которые скомпилированы с различными директивами компилятора, или даже с использованием #pragma для изменения поведения компилятора во время компиляции.

Наконец, я уверен, что вообще нет нужды перечислять члены структуры с указателем.

Я рекомендую вам прочитать о заполнении и упаковке структуры, хорошее начало для начала - вопрос по SO: Структуранабивка и упаковка

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...