我是新手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问题?
谢谢。
避免这种指针魔术,相信编译器能理解这个开关。
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;
}
两个简单的解决方案是:
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;
}