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
答案 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 ....):
__attribute__((sentinel))
声明asn1c_make_identifier
;如果您在没有终止NULL
... 定义一个用结尾 NULL
调用它的宏,例如
#define asn1c_make_identifier(__VA_ARGS__) \
asn1c_make_identifier(__VA__ARGS__,NULL)
所以可能只是改变一下头文件就足够了。 (如果您想要令人讨厌且完全符合标准,请将NULL
替换为(char*)0
。
我也会在你的位置向asn1c
的上游作者提出这一改进建议。也许新版本已经(或将会)更正此错误。