Указатели разыменования PIC32 возможная ошибка компилятора

#c #pointers #compiler-construction #pic #pic32

#c #указатели #компилятор-конструирование #рис #pic32

Вопрос:

Я работаю над некоторым кодом для PIC32MX795F512L, используя компилятор XC32. Мне нужно прочитать данные из буфера, переданного функции как void *. Мне нужно прочитать данные в виде массива 32-битных целых чисел без знака. проблема в том, что когда я преобразую void * в uint32 * и пытаюсь прочитать значение с индексом 0 этого массива, процессор вызывает общий обработчик ошибок, в то время как если я преобразую void * в uint8 * и выполняю некоторые битовые манипуляции, чтобы получить те же данные, он работает должным образом.

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

 void foo(void* data, uint32 length)
{
    uint32 i,j;
    uint32 block;
    for(i = 0, j = 0; i < length; i = sizeof(uint32),j  )
    {
        printfUART(DEBUG_UART,"Debug 0rn");
#if 0//working code
        block = ((uint8*)data)[i   3];
        block <<= 8;
        block |= ((uint8*)data)[i   2];
        block <<= 8;
        block |= ((uint8*)data)[i   1];
        block <<= 8;
        block |= ((uint8*)data)[i   0];
#else//not working code
        block = ((uint32*)data)[j];
#endif
        printfUART(DEBUG_UART,"Debug 1rn");
    }
}
  

если вы измените #if 0 на #if 1 , код будет работать так, как ожидалось, и я вижу, что "Debug 0" и "Debug 1" печатается много раз в цикле.

но если вы оставите все как есть, «Debug 0» печатается только один раз, а затем код переходит из цикла в общий обработчик ошибок процессора, установленный компилятором. Это ошибка в компиляторе XC32, или я чего-то не хватает?

Комментарии:

1. извините, это ошибка копирования-вставки

2. нет проблем. Я также думаю, что это проблема с выравниванием. Попробуйте напечатать необработанный адрес данных. Бьюсь об заклад, это не четный адрес.

3. как отмечает Руслан Герасимов (я думаю), если length не кратно 4, то вы читаете за пределами входного буфера. Вы должны написать код, чтобы убедиться, что этого не произойдет, например, округлить length до ближайшего значения, кратного 4, перед запуском цикла.

4. @Matt McNabb в целом это верно, но для этой конкретной функции передаваемый размер является константой, кратной 4, и в любом случае это просто отладочный код, так что я не беспокоюсь об этом.

Ответ №1:

Я думаю, что это проблема с выравниванием.

Если data адрес не является четным (например, потому что это фактически массив байтов), доступ к нему в 32-битном режиме может быть невозможен на вашей платформе.

Стандарт C (ISO / IEC 9899: 1999, 6.3.2.3) гласит:

Указатель на объект или неполный тип может быть преобразован в указатель на другой объект или неполный тип. Если результирующий указатель неправильно выровнен для указанного типа, поведение не определено.

Комментарии:

1. Не нарушает ли это стандарт c? Я имею в виду, не является ли то, что я написал, действительным на c?

2. Нет, это не так. Обычно вы не можете свободно конвертировать указатели. Единственная гарантия заключается в том, что вы можете получить доступ ко всем объектам с помощью указателя на символ («по байтам»).

3. Пожалуйста, проверьте ваше руководство по компилятору, вероятно, это макросы препроцессора, которые вы можете использовать для принудительного выравнивания определенных переменных.

4. @BrandonYates Обычно на PIC32 нет никакой операционной системы. Я думаю, что среда выполнения C завершится ошибкой при разыменовании указателя void.

5. @BrandonYates разыменование NULL также недопустимо для C. То, что что-то не выдает ошибку компиляции, не означает, что это допустимо.

Ответ №2:

Вместо block = ((uint32*)data)[j]; которых могут возникнуть проблемы с выравниванием, вы можете достичь желаемого эффекта с помощью:

 memcpy( amp;block, data   j, sizeof block );
  

Обратите внимание, что это концептуально отличается от версии, построенной из отдельных байтов. Это зависит от того, как ваш процессор представляет целые числа (обычно называемые «порядковым номером»).

Ответ №3:

Ниже приведен ваш код с добавленным отладочным выводом. В основном цикле ему немного не хватает массива (специально) для памяти, которую он занимает, и пока он работает нормально (хотя это может быть все еще несовместимо с платформой). Я почти уверен, что ваш foo() в порядке, и компилятор тоже, но вы, вероятно, неправильно это называете. Проверьте все адреса и значения с помощью выходных данных отладки и проверьте диапазоны.

 void foo(void* data, unsigned int length)
{
    unsigned int  i,j;
    unsigned int  block;
    for(i = 0, j = 0; i < length; i = sizeof(unsigned int ),j  )
    {
        printf("Debug 0rn");
        printf("i: 0x%drn", i);
        printf("j: 0x%drn", j );
#if 0//working code
        block = ((unsigned char*)data)[i   3];
        block <<= 8;
        block |= ((unsigned char*)data)[i   2];
        block <<= 8;
        block |= ((unsigned char*)data)[i   1];
        block <<= 8;
        block |= ((unsigned char*)data)[i   0];
#else//not working code
        block = ((unsigned int *)data)[j];
#endif
        printf("data: 0x%xrn", data );
        printf( " ((unsigned int )data  j) = 0x%xrn", ((unsigned int )data  j) );
        printf( " ((unsigned int *)data)[j] = 0x%xrn", ((unsigned int *)data)[j]);
        printf("block: 0x%xrn", block );
        printf("Debug 1rn");
        printf("rn");
        printf("rn");
    }
}

int main(void)
{
 unsigned int array[10] = {0xFE,0xED,0xBA,0xBE,0xDE,0xAD,0xBE,0xEF,0xCA,0xFE};
 printf ("array: 0x%xrn",array);
 printf ("array[1]:%xrn",array[1]);
 foo((void*) array, 50);

}
  

его вывод:

массив: 0x1d0fdde0 массив[1]: ed Debug 0 i: 0x0 j: 0x0 данные: 0x1d0fdde0 ((unsigned int )data j) = 0x1d0fdde0 ((unsigned int *)data)[j] = 0xfe блок: 0xfe Debug 1

Debug 0 i: 0x4 j: 0x1 данные: 0x1d0fdde0 ((unsigned int )data j) = 0x1d0fdde1 ((unsigned int *)data)[j] = 0xed блок: 0xed Отладка 1

Debug 0 i: 0x8 j: 0x2 данные: 0x1d0fdde0 ((unsigned int )data j) = 0x1d0fdde2 ((unsigned int *)data)[j] = 0xba блок: 0xba Отладка 1

Debug 0 i: 0x12 j: 0x3 данные: 0x1d0fdde0 ((unsigned int )data j) = 0x1d0fdde3 ((unsigned int *)data)[j] = 0xbe блок: 0xbe Отладка 1

Debug 0 i: 0x16 j: 0x4 данные: 0x1d0fdde0 ((unsigned int )data j) = 0x1d0fdde4 ((unsigned int *)data)[j] = блок 0xde: 0xde Debug 1

Debug 0 i: 0x20 j: 0x5 данные: 0x1d0fdde0 ((unsigned int )data j) = 0x1d0fdde5 ((unsigned int *)data)[j] = 0xad блок: 0xad Отладка 1

Debug 0 i: 0x24 j: 0x6 данные: 0x1d0fdde0 ((unsigned int )data j) = 0x1d0fdde6 ((unsigned int *)data)[j] = 0xbe блок: 0xbe Отладка 1

Debug 0 i: 0x28 j: 0x7 data: 0x1d0fdde0 ((unsigned int )data j) = 0x1d0fdde7 ((unsigned int *)data)[j] = блок 0xef: 0xef Debug 1

Debug 0 i: 0x32 j: 0x8 data: 0x1d0fdde0 ((unsigned int )data j) = 0x1d0fdde8 ((unsigned int *)data)[j] = блок 0xca: 0xca Debug 1

Debug 0 i: 0x36 j: 0x9 данные: 0x1d0fdde0 ((unsigned int )data j) = 0x1d0fdde9 ((unsigned int *)data)[j] = 0xfe блок: 0xfe Отладка 1

Debug 0 i: 0x40 j: 0x10 данные: 0x1d0fdde0 ((unsigned int )data j) = 0x1d0fddea ((unsigned int *)data)[j] = 0x0 блок: 0x0 Отладка 1

Debug 0 i: 0x44 j: 0x11 данные: 0x1d0fdde0 ((unsigned int )data j) = 0x1d0fddeb ((unsigned int *)data)[j] = 0x0 блок: 0x0 Отладка 1

Debug 0 i: 0x48 j: 0x12 данные: 0x1d0fdde0 ((unsigned int )data j) = 0x1d0fddec ((unsigned int *)data)[j] = 0x0 блок: 0x0 Отладка 1

Если вы обновите с более подробной информацией и / или новым результатом, любой получит больше понимания и улучшит ответ, и вы получите более надежный ответ.

Комментарии:

1. Когда кто-то отклонил ответ, хорошей идеей будет оставить комментарий, чтобы дать представление о том, что показалось неправильным.

2. возможные причины отрицательных отзывов: вы вводите переполнение буфера там, где ранее его не было (???), у вас происходит бессмысленное приведение к unsigned int в unsigned int , и вы не устраняете фактическую причину проблемы (проблема с выравниванием); и вы говорите, что «почти уверены», что все в порядке, только потому, что ваша версия, похоже, работает, но этот вывод не оправдан.

3. @MichaelDorgan спасибо, джентльмены, я исправил свой ответ.

4. вы запускаете этот код на микроконтроллере pic32? кроме того, передаваемые вами данные были выровнены как 32-разрядный массив целых чисел, поэтому в этом примере не показано, вызывает ли проблему неправильное выравнивание.

Ответ №4:

undur_gongor верен, это проблема с выравниванием. Рассмотрим следующую структуру:

 struct demo {
   uint8_t char_var;
   uint16_t short_var;
   uint32_t int_var;
};
  

Если упакованная структура была размещена по выровненному адресу 0, то у вас есть 16-разрядная переменная, начинающаяся с нечетного адреса 1, и 32-разрядная переменная по адресу 3. PIC32 может считывать только 16-разрядную переменную с четных адресов (0, 2, 4 …) и 32-разрядную переменную с аналогично выровненных адресов (0, 4, 8).

При обращении к упакованной структуре или подобной конструкции компилятор выполняет дополнительные шаги. Решение, как правило, заключается в записи отдельных байтов, на которые не влияет выравнивание. Как упоминал M.M, самое простое решение — использовать memcpy.