va_arg在cygwin x64中给出了一些奇怪的东西

时间:2014-08-19 18:42:33

标签: c gcc cygwin

asn1c编译器具有以下代码(它的C代码, C++):

return asn1c_make_identifier(
                        AMI_MASK_ONLY_SPACES | AMI_NODELIMITER,
                        0, ((!stdname || (arg->flags & A1C_INCLUDES_QUOTED))
                                ? "\"" : "<"),
                        exprid ? exprid->Identifier : typename,
                        ((!stdname || (arg->flags & A1C_INCLUDES_QUOTED))
                                ? ".h\"" : ".h>"), 0);

为简单起见,我可以编写以下内容(它不会影响错误):

return asn1c_make_identifier(
                        AMI_MASK_ONLY_SPACES | AMI_NODELIMITER,
                        0, "str1",
                        "str2",
                        "str3", 0);

函数asn1c_make_identifier具有以下定义char * asn1c_make_identifier(enum ami_flags_e flags, asn1p_expr_t *expr, ...)和标准va_list处理循环:

va_start(ap, expr);
while((str = va_arg(ap, char *)))
    size += 1 + strlen(str);
va_end(ap);

所以,我们得到3个字符串和一个零。一切似乎都很好(并且它在Linux中有效),但是在cygwin中我得到了非零str的第四次迭代。结果发生了分段错误。但是,如果我在(char*)之前编写0,则代码可以正常工作。这是一个真正的cygwin错误还是源代码中的错误?它不是我的代码,它包含许多类似的零。

P.S。关于gcc版本

gcc --version
gcc (GCC) 4.8.3

3 个答案:

答案 0 :(得分:3)

问题是0被解释为整数,而不是指针。由于编译器无法知道变量参数的类型应该是什么,因此它无法转换为您想要的类型。

因此,当编译器将表达式0视为参数时,它会将其解释为类型int的类型void *的表达式(或char *或任何其他指针。

由于您正在为x64进行编译,sizeof(int)为4且sizeof(char *)为8,因此va_arg正在从堆栈中读取的数据多于传递给它的数据。它

相反,您应该传递常量NULL,其类型为void *,或者为了最大程度的可移植性,请传递表达式(char *)NULL

答案 1 :(得分:2)

看起来你在Linux上很幸运,而int - 值0可能与周围的垃圾一起被解释为你应该提供的哨兵char* NULL

好像你的运气已经用完了cygwin,并且a)它是一个64位系统而且b)周围的垃圾没有与你提供的零int一起形成空指针。

欢迎来到未定义行为的奇妙世界。

只需将哨兵投向char*即可。

答案 2 :(得分:1)

这可能是源代码的错误,因为,Drew McGowen answered,终止0实际上应该是NULL 指针(不是int) - 至少在Linux上(因为在x86-64 int上 - s和指针有不同的大小)。要挑剔,那应该是(char*)0

然而,您可以(假设GCC ....):

  1. 使用__attribute__((sentinel))声明asn1c_make_identifier;如果您在没有终止NULL ...
  2. 的情况下调用它,它会发出警告
  3. 定义一个用结尾 NULL调用它的宏,例如

    #define asn1c_make_identifier(__VA_ARGS__) \
      asn1c_make_identifier(__VA__ARGS__,NULL) 
    
  4. 所以可能只是改变一下头文件就足够了。 (如果您想要令人讨厌且完全符合标准,请将NULL替换为(char*)0

    我也会在你的位置向asn1c的上游作者提出这一改进建议。也许新版本已经(或将会)更正此错误。