c--正确的结构元素指针算术

问题描述 投票:0回答:1

我是新手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 以类型ef表示 unsigned char. 当我的linting代码时,我的linter报告了一个警告

从 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*) 毫无意义地使应用程序崩溃。

谁能告诉我如何正确计算指向正确通道的指针,而不出现别名和paddingalignment问题?

谢谢。

c pointers math struct strict-aliasing
1个回答
2
投票

避免这种指针魔术,相信编译器能理解这个开关。


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
投票

两个简单的解决方案是:

  • 忽略你的 "linter". 忽略你的 "interest",让代码保持原样。
  • 改变 adc_ch_ptr = (ADC_CH_t*)((UINT8*)&(adc_ptr->CH0) + sizeof(ADC_CH_t) * channel);adc_ch_ptr = &adc_ptr->CH0 + channel;.

其中任何一种都依赖于地址运算工作超出C标准的要求,并且结构中没有任何奇怪的(不必要的)填充。下面是使用严格符合C标准的代码的稍微复杂一点的解决方案。

上面改动后的代码只是将 CH* 成员,就像它们是一个数组的 ADC_CH_t; 添加一个整数 channel 指向数组中第一个元素(索引为0)的指针产生一个指向数组中另一个元素的指针(索引是 channel). 原始代码做了同样的运算,只是以字节为单位,而不是以类型为单位的元素。ADC_CH_t. 似乎没有必要使用字节,因为用元素进行运算应该产生同样的结果。所以不清楚原作者为什么选择使用字节,因为这样做出来的代码比较繁琐。

使用严格符合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;
    }
    
© www.soinside.com 2019 - 2024. All rights reserved.