0

我正在使用的编译器(ARM 的 Codesourcery)中有一个错误会破坏 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”大师能帮我一把吗?我有一个解决方案,它使用联合来操纵指针,但我想要一个更“c-worthy”的方法......

4

3 回答 3

2

事实证明,这根本不是编译器问题。该问题是由堆栈指针不在 8 字节边界上引起的。通过修改加载脚本以对堆栈使用 8 字节对齐来修复它。我应该补充一点,这个项目正在使用 NutOS;一段非常有用的代码。

使用股票加载脚本,堆栈指针被加载(有时,并非总是)一个不是 8 字节对齐的值。

我的 ARM9 Linux 平台没有这个问题,尽管参数编组和 va_arg() 代码与 ARM7 编译器相同。

我注意到,当调用函数时,编译器会使用它认为会导致 8 字节对齐的值加载堆栈指针。这导致 r0-r3 是 8 字节对齐的。使用 8 字节对齐,va_arg() 算术有效。

我知道这不能回答我的问题。我确实制定了一个涉及联合的解决方案,有趣的是,你认为额外的代码最终被优化了。因此,使用联合来操作 va_list 并没有真正增加太多开销。

感谢大家的回复。

于 2013-09-19T21:00:16.967 回答
0

强制转换和 & 运算符都不会返回可分配的值(也称为“L 值”,可以位于等号的左侧)。你可以试试:

int ival;
void * pap = ≈
ival = *(int32_t*)pap;
pap += sizeof(int32_t);

但是,正如前面的回答所说,va_list 实现会因平台而异,任何解决方法都不太可能是可移植的。如果您决定继续使用解决方法,至少要确保包含 va_list 的单元测试,以便您知道它是否会损坏。

于 2013-09-18T14:31:46.110 回答
0

不能保证可以va_list转换为指针(它可能通过涉及多个寄存器或字的编译器魔术来实现)。

您应该只使用stdarg(3)中记录的宏,即va_start,va_endva_arg(也许还有va_copy)。

根据定义,您的代码是不可移植的和错误的,它会触发未定义的行为

仅增加ap使用va_arg(ap, int32_t)等...;

顺便说一句,您可以下载新版本的GCC(即今天的 4.8.1,2013 年 9 月 18 日)并从源代码编译它(也许作为交叉编译器)。它可能已经解决了您遇到的编译器错误。

于 2013-09-18T13:50:02.993 回答