从 Ada 调用 scanf

Posted

技术标签:

【中文标题】从 Ada 调用 scanf【英文标题】:Calling scanf from Ada 【发布时间】:2012-01-08 09:58:15 【问题描述】:

如何?也就是说,大概有一个适当的 pragma import 声明,但声明会是什么样子?

(我对如何从 Ada 调用更不规则的 C 函数感兴趣,而不是如何解析字符串本身,所以我不是在寻找纯 Ada 解决方案。我的设置是 Gnat,Ubuntu Linux, x64 如果有区别的话。)

【问题讨论】:

另见Q&A。 【参考方案1】:

This paper 指出

Ada 无法声明一个函数,该函数接受不同数量的不同类型的参数。可以声明一组“printf”函数,它们接受一个字符串、一个字符串和一个整数、一个字符串和一个浮点数、一个字符串和 2 个整数,等等,然后将每个函数声明为 Import (C) 2.但这需要大量声明,针对应用程序中的每种不同用途声明,因此确实不实用。

scanf() 也是如此,Ada 2012 的额外好处是让您可以在 outaccess 参数规范之间进行选择(在早期版本中,您必须使用 access,因为函数没有'不允许有out 参数)。

此外,我不认为 C 编译器必须对可变参数函数使用与普通函数相同的参数传递机制(the reference 暗示了这一点,我记得但现在不能查找最近关于这些线路的对话)。

也就是说,这里有一个示例,它似乎在带有 GCC 4.6.0 的 Mac OS X 上运行良好:

with Ada.Text_IO; use Ada.Text_IO;
with Interfaces.C; use Interfaces.C;
procedure Variadic is
   function Scanf (Fmt : char_array; Result : access int) return int;
   pragma Import (C, Scanf, "scanf");
   Status : int;
   Result : aliased int;
begin
   Status := Scanf (To_C ("%d\n"), Result'Access);
   Put_Line ("status: " & int'Image (Status));
   if Status = 1 then
      Put_Line ("result: " & int'Image (Result));
   end if;
end Variadic;

(不确定格式参数中的\n!)

【讨论】:

你是正确的参数传递机制。在 C 中,调用可变参数函数需要与函数的实际定义兼容的可见可变参数声明(原型);否则行为未定义。但是由于历史原因,大多数 C 编译器使用与非可变函数兼容的调用约定(ANSI C 之前没有原型,但无论如何都必须调用printfscanf,并且可以随时更改约定从那时起就会破坏现有的代码)。 "\n" 在 Ada 字符串文字中只是一个反斜杠,后跟一个字母 n,而 To_C 不会将其转换为换行符。而不是"%d\n",你需要写"%d" & Ada.Characters.Latin_1.LF之类的东西。但在这种特殊情况下,没有必要。 scanf 格式字符串(包括换行符)中的任何空白字符序列都会读取零个或多个连续空白字符,因此"%d " 等效于"%d\n"注意: 请注意,如果 scanf"%d" 读取溢出的整数值,则行为是未定义 @Keith,我明白你对“\n” 的意思。混合语言思维 => 混乱!不良行为的另一个机会:告诉 Ada 传递int 的地址并在格式中指定“%g” 纯C也有同样的问题;一些 C 编译器会警告 scanf("%g", &an_int_variable),但它们不是必需的,并且行为未定义。【参考方案2】:

一种解决方法是在 C 中声明多个非可变包装函数,并将它们导入 Ada。

例如:

int scanf_i(const char *format, int *i_ptr) 
    return scanf(format, i_ptr);


int scanf_d(const char *format, double *d_ptr) 
    return scanf(format, d_ptr);

然后用pragma Import在Ada中声明重载的scan()函数。

这样,您不会尝试从 Ada 调用可变参数函数;所有固定到可变的转换都发生在 C 端。只要正确编写所有包装器,您就可以获得比直接调用 scanf() 更多的类型检查。

您只需要为要传递的每组参数类型设置一个不同的包装器。

如果您只有几个调用,这可能没问题,但它不能很好地扩展。

【讨论】:

以上是关于从 Ada 调用 scanf的主要内容,如果未能解决你的问题,请参考以下文章

Ada - (Streams) 如何在事先不知道字符串长度的情况下正确调用 String'Read()

在汇编中,如何存储来自 scanf 调用的输入?

在 x86_64 AT&T 中调用 scanf 时出现分段错误

连续调用scanf的问题总结

scanf 调用时出现“无法打开输出文件”错误

int的打印数组会在ada中生成奇怪的输出