我正在使用的编译器(ARM的代码源)中存在一个错误,它打破了va_arg(),我正在尝试解决这个问题。在这种情况下,'ap'是指向32位和64位参数列表的简单指针。编译器错误是va_arg()被破坏,有时会返回不正确的值。
我可以将va_list转换为任意类型的指针,并使用它来挑选列表中的值:
void foo(va_list ap)
{
int32_t ival;
double dval;
ival = *(int32_t*)≈
dval = *(double*)&ap);
}
但是,如何将'ap'作为演员类型提前或后递?
例如,这两个都给出了错误:
(int32_t*)&ap++;
++(int32_t*)&ap.
真正的'C'大师可以帮我一把吗?我有一个解决方案,使用工会来操作指针,但我想要一个更“有价值”的方法......
答案 0 :(得分:2)
事实证明,这根本不是编译器问题。问题是由堆栈指针NOT不在8字节边界引起的。通过修改加载脚本以使用堆栈的8字节对齐来修复它。我应该补充一点,这个项目正在使用NutOS;一段非常有用的代码。
使用库存加载脚本,堆栈指针(有时并非总是)加载了一个非8字节对齐的值。
我的ARM9 Linux平台没有此问题,但参数编组和va_arg()代码与ARM7编译器相同。
我注意到,当调用一个函数时,编译器会加载一个它认为会导致8字节对齐的值的堆栈指针。这导致r0-r3为8字节对齐。使用8字节对齐时,va_arg()算法可以正常工作。
我知道这不能回答我的问题。我确实制定了一个涉及工会的解决方案,有趣的是,您认为额外的代码最终会被优化出来。因此,使用联合来操纵va_list并没有真正增加很多开销。
感谢您的回复。
答案 1 :(得分:0)
无法保证va_list
可以转换为指针(可能通过涉及多个寄存器或单词的编译器魔法实现)。
您应该只使用stdarg(3)中记录的宏,即va_start
,va_end
和va_arg
(可能还有va_copy
)。
根据定义,您的代码不可移植且有问题,它会触发undefined behavior。
增加ap
仅使用va_arg(ap, int32_t)
等...;
答案 2 :(得分:0)
无论是演员还是&运算符返回可分配值(也称为“L值”,可以在等号的左侧)。你可以尝试:
int ival;
void * pap = ≈
ival = *(int32_t*)pap;
pap += sizeof(int32_t);
但是,正如之前的回答所说,va_list实现在不同平台之间会有所不同,任何解决方法都不可能是可移植的。如果您决定继续使用变通方法,至少要确保包含va_list的单元测试,以便了解它是否会中断。