c - правильная арифметика указателя на элемент структуры - PullRequest
0 голосов
/ 03 мая 2020

Я новичок ie C программист, работающий над поддержкой устаревшего встроенного C кода, который выглядит проблемным c. В следующих фрагментах я упростила:

UINT16 adcFunc(UINT8 adc, UINT8 channel)
{
    ADC_t* adc_ptr = (ADC_t*)(adc << 4);
    ADC_CH_t* adc_ch_ptr;
    adc_ch_ptr = (ADC_CH_t*)((UINT8*)&(adc_ptr->CH0) + sizeof(ADC_CH_t) * channel);
    ...
}

Где определение структуры задано как:

typedef struct ADC_struct
{
    ...
    register8_t reserved_0x1E;
    register8_t reserved_0x1F;
    ADC_CH_t CH0;  /* ADC Channel 0 */
    ADC_CH_t CH1;  /* ADC Channel 1 */
    ADC_CH_t CH2;  /* ADC Channel 2 */
    ADC_CH_t CH3;  /* ADC Channel 3 */
} ADC_t;

С размером указателя 2 байта и UINT8 в виде typedef unsigned char. При раскрашивании кода мой линтер сообщает предупреждение

, приведенное от UINT8 * к ADC_CH_t *, увеличивает необходимое выравнивание с 1 до 2 по строке

adc_ch_ptr = (ADC_CH_t*)((UINT8*)&(adc_ptr->CH0) + sizeof(ADC_CH_t) * channel);

Код пытается вычислить правильное смещение в структуре для указателя канала adc_ch_ptr (где канал находится между 0 и 3). Для меня это выглядит как строгое нарушение псевдонимов, и я удалил приведение из (UINT8*) Бессмысленно, и это разбило приложение.

Может кто-нибудь пролить свет на то, как правильно рассчитать указатель на правильный канал без проблем с наложением и дополнением / выравниванием?

Спасибо

Ответы [ 2 ]

2 голосов
/ 03 мая 2020

Избегайте этого указателя magi c и доверьте компилятору понимание переключателя:


UINT16 adcFunc(UINT8 adc, UINT8 channel)
{
        /* this should be hidden inside a macro or an inline function*/
    ADC_t *adc_ptr = FIND_BASE_ADDRESS(adc);

    ADC_CH_t *adc_ch_ptr;

    switch (channel) {
    case 0: adc_ch_ptr = &adc_ptr->CH0; break;
    case 1: adc_ch_ptr = &adc_ptr->CH1; break;
    case 2: adc_ch_ptr = &adc_ptr->CH2; break;
    case 3: adc_ch_ptr = &adc_ptr->CH3; break;
        /* should not happen ... */
    default: return 0xffff;
        }

    /* do something with adc_ch_ptr ... */
    ...
    return something_usefull_here;
}
1 голос
/ 03 мая 2020

Два простых решения:

  • Не обращайте внимания на ваш «линтер». Оставьте код таким, какой он есть.
  • Измените adc_ch_ptr = (ADC_CH_t*)((UINT8*)&(adc_ptr->CH0) + sizeof(ADC_CH_t) * channel); на adc_ch_ptr = &adc_ptr->CH0 + channel;.

Любой из этих вариантов основан на адресной арифметике c, работающей за пределами стандарта C требует, чтобы структура не имела каких-либо странных (и ненужных) отступов. Чуть более сложные решения с использованием строго соответствующего кода C приведены ниже.

Измененный код выше просто обрабатывает элементы CH* так, как если бы они были массивом ADC_CH_t; добавление целого числа channel к указателю на первый элемент (с индексом 0) массива дает указатель на другой элемент в массиве (с индексом channel). Исходный код выполняет ту же арифметику c за исключением байтов вместо элементов типа ADC_CH_t. Похоже, нет необходимости использовать байты, поскольку арифметика c с элементами должна давать одинаковые результаты. Поэтому неясно, почему автор оригинала решил использовать байты, учитывая, что полученный код более громоздок.

Два решения, которые используют строго соответствующий C код:

  • Использование массив (определенный здесь как составной литерал) для поиска нужного адреса:
    adc_ch_ptr = (ADC_CH_t *[]) {
            &adc_ptr->CH0, &adc_ptr->CH1, &adc_ptr->CH2, &adc_ptr->CH3,
        } [channel];
    
  • Используйте switch:
    switch (channel)
    {
        case 0: adc_ch_ptr = &adc_ptr->CH0; break;
        case 1: adc_ch_ptr = &adc_ptr->CH1; break;
        case 2: adc_ch_ptr = &adc_ptr->CH2; break;
        case 3: adc_ch_ptr = &adc_ptr->CH3; break;
    }
    
...