Более чистый арифметический синтаксис указателя для манипуляций с байтовыми смещениями - PullRequest
1 голос
/ 26 октября 2009

В следующих строках кода мне нужно настроить указатель pm на смещение в байтах в одном из его полей. Есть ли лучший / более простой способ сделать это, чем непрерывное приведение вперед и назад от char * и PartitionMap *, чтобы арифметика указателей все еще работала?

PartitionMap *pm(reinterpret_cast<PartitionMap *>(partitionMaps));
for ( ; index > 0 ; --index)
{
    pm = (PartitionMap *)(((char *)pm) + pm->partitionMapLength);
}
return pm;

Для тех, кто не может выйти из кода, он просматривает дескрипторы переменной длины в буфере, которые наследуются от PartitionMap.

Также для заинтересованных лиц, partitionMapLength всегда возвращает длины, поддерживаемые системой, на которой он работает. Данные, которые я пересекаю, соответствуют спецификации UDF .

Ответы [ 7 ]

5 голосов
/ 26 октября 2009

Я часто использую эти шаблоны для этого:

    template<typename T>
    T *add_pointer(T *p, unsigned int n) {
            return reinterpret_cast<T *>(reinterpret_cast<char *>(p) + n);
    }

    template<typename T>
    const T *add_pointer(const T *p, unsigned int n) {
            return reinterpret_cast<const T *>(reinterpret_cast<const char *>(p) + n);
    }

Они поддерживают тип, но добавляют к ним отдельные байты, например:

T *x = add_pointer(x, 1); // increments x by one byte, regardless of the type of x
5 голосов
/ 26 октября 2009

Приведение типов - единственный способ, будь то тип char *, intptr_t или другой подобный тип, а затем - ваш последний тип.

3 голосов
/ 26 октября 2009

Конечно, вы можете просто хранить две переменные: char * для перехода по буферу и PartitionMap * для доступа к нему. Делает немного яснее, что происходит.

for (char *ptr = ??, pm = (PartitionMap *)ptr ; index > 0 ; --index)
{
    ptr += pm->partitionMapLength;
    pm = (PartitionMap *)ptr;
}
return pm;
1 голос
/ 26 октября 2009

Как уже упоминали другие, вам нужны приведения, но вы можете скрыть уродство в макросе или функции. Однако следует помнить еще одну вещь - требования к выравниванию. На большинстве процессоров вы не можете просто увеличить указатель на тип на произвольное число байтов и преобразовать результат обратно в указатель на исходный тип без проблем с доступом к структуре через новый указатель из-за смещения.

Одной из немногих архитектур (даже если она самая популярная), которая позволит вам сойти с рук, является архитектура x86. Тем не менее, даже если вы пишете для Windows, вы должны принять эту проблему во внимание - Win64 действительно обеспечивает требования к выравниванию.

Так что даже доступ к члену partitionMapLength через указатель может привести к сбою вашей программы.

Вы можете легко обойти эту проблему, используя расширение компилятора, например __unaligned в Windows:

PartitionMap __unaliged *pm(reinterpret_cast<PartitionMap *>(partitionMaps));
for ( ; index > 0 ; --index)
{
    pm = (PartitionMap __unaligned *)(((char *)pm) + pm->partitionMapLength);
}
return pm;

Или вы можете скопировать потенциально невыровненные данные в правильно выровненную структуру:

PartitionMap *pm(reinterpret_cast<PartitionMap *>(partitionMaps));

char* p = reinterpret_cast<char*>( pm);

ParititionMap tmpMap;
for ( ; index > 0 ; --index)
{

    p += pm->partitionMapLength;

    memcpy( &tmpMap, p, sizeof( newMap));
    pm = &tmpMap;
}

// you may need a more spohisticated copy to return something useful
size_t siz = pm->partitionMapLength;
pm = reinterpret_cast<PartitionMap*>( malloc( siz));
if (pm) {
    memcpy( pm, p, siz);
}
return pm;
0 голосов
/ 26 октября 2009

И C, и C ++ позволяют вам перебирать массив с помощью указателей и ++:

#include <iostream>

int[] arry = { 0, 1, 2, 3 };
int* ptr = arry;
while (*ptr != 3) {
    std::cout << *ptr << '\n';
    ++ptr;
}

Чтобы это работало, добавление к указателям определено, чтобы взять адрес памяти, сохраненный в указателе, а затем добавить размер любого типа, умноженного на добавляемое значение. Например, в нашем примере ++ptr добавляет 1 * sizeof(int) к адресу памяти, хранящемуся в ptr.

Если у вас есть указатель на тип и вы хотите выдвинуть определенное количество байтов из этого места, единственный способ сделать это - привести к char* (потому что sizeof(char) определен как единица).

0 голосов
/ 26 октября 2009

Что меня озадачивает, так это то, что у вас есть 'partitionMapLength' в байтах?

Разве не было бы лучше, если бы оно было в единицах 'partitionMap', поскольку вы все равно разыгрываете его?

PartitionMap *pmBase(reinterpret_cast<PartitionMap *>(partitionMaps));
PartitionMap *pm;
...
pm = pmBase + index; // just guessing about your 'index' variable here
0 голосов
/ 26 октября 2009

Приведение должно быть выполнено, но это делает код почти нечитаемым. Для удобства чтения выделите его в функции static inline.

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