这个声明是从哪里来的:main _2a((argc,argv), int argc, char * argv[])

6

我正在将许多运行在HP-Unix操作系统下的Oracle Pro*C代码迁移至Linux环境。

在程序中只有一个这样定义的主方法:

main _2a((argc,argv), int argc, char * argv[])
{
...
}

我从未见过这样的声明 - 并且没有在Google上找到任何相关信息。 不管怎样,它能工作,并且据我所见被用作主函数。

有人可以介绍一下吗?

编辑: 有一个宏定义:

 # define _2a(list,a1,a2)                 list a1;a2;

仍然对我来说没有明确的看法(..)

3
"_2a" 很可能是一个宏,定义在某个头文件中。 - Jonathon Reinhart
2
也许 _2a 是一个宏。 - Jabberwocky
好的提示 - 我可以看到一个宏定义。我正在更新问题。 - Stefan
2个回答

8
这个宏用于使K&R C风格的函数定义看起来更像“现代”C89定义。
代码展开后,如下所示:
main (argc,argv) int argc;char * argv[];
{
...
}

或者更好的缩进方式:
main(argc, argv)
    int argc;
    char *argv[];
{
    ...
}

这是一种古老的书写方式:

int main(int argc, char *argv[])
{
    ...
}

你能解释一下预处理器在做什么吗?我试图弄清楚在编译开始之前进行了哪些替换。 - Stefan
我在我的问题中放置了简单的宏展开。这回答了你的问题吗? - Kijewski
3
我只是想用我从多年的CRT显示器辐射接触中获得的力量来帮助人类。 ;) - Kijewski
2
我只想指出,它与int main(int argc, char *argv[])不是完全等价的。后者会导致参数被隐式转换(如果它们属于可隐式转换类型)或在不符合预期类型时进行诊断;对于非原型定义/声明,使用错误参数调用将具有未定义行为。此外,省略返回类型并让其默认为int在C89/C90/C95及更早版本中是合法的,但这从未是一个特别好的做法。 - Keith Thompson

7
我们只需要扩展宏。您可以使用gcc -E来调用预处理器;它还会展开所有的#include指令,因此输出将会很丑。
但是让我们手动操作。宏的参数是标记序列,而不是表达式。
宏定义如下:
# define _2a(list,a1,a2)                 list a1;a2;

调用方式如下:

main _2a((argc,argv), int argc, char * argv[])
{
...
}

参数如下:

  • list --> (argc,argv)
  • a1 --> int argc
  • a2 --> char * argv[]

进行替换后,我们得到:

main (argc,argv) int argc; char * argv[];
{
...
}

这通常会写成多行:

main(argc, argv)
int argc;
char *argv[];
{
    ...
}

这是一种旧式的函数声明方式。虽然在所有版本的C中(包括2011标准)仍然合法,但自1989年以来就已经被正式弃用。这种旧式形式的缺点在于它没有向调用者传递参数信息,因此编译器无法警告您如果使用了错误数量或类型的参数来调用函数。我敢打赌,_a2 宏的另一种定义会扩展成更现代的定义,其中包含原型,例如:
#define _2a(list,a1,a2) (a1, a2)

使用这个宏定义,main 的定义会被扩展成以下内容:
main (int argc, char * argv[])
{
...
}

因此,_a2宏(“a”可能代表“参数”)使您可以编写能够扩展为旧式函数定义(适用于ANSI之前的编译器)或具有原型的现代定义的代码。

实现它的一个合理方法是:

#ifdef __STDC__
#define _2a(list,a1,a2) (a1, a2) 
#else
#define _2a(list,a1,a2) list a1;a2;
#endif

但是由于你几乎不可能找到一个不支持原型的C编译器(它们已经成为这门语言的标准特性超过了四分之一世纪),所以更有意义的做法就是完全删除宏,只使用现代风格的函数声明和定义。

此外,main的定义缺少返回类型int。在1999年C标准之前,可以根据"隐式int"规则省略返回类型。1999年的标准取消了这个规则,并使显式返回类型成为必需。大多数C编译器在默认模式下仍然允许省略返回类型,可能会发出警告,但没有理由省略它。


这是一个写得非常好、包含大量信息的答案。我只能给+1,因为我没有+2——很抱歉不能接受你的好答案作为“答案”,因为我已经标记了Kay的答案。它为我提供了所需信息并稍微更快——而且我的策略不允许切换被勾选的答案——我很抱歉。无论如何,我呼吁每个人也点赞这个答案。 - Stefan
@Stefan:在我发问题的时候,通常会等一段时间再接受答案。这并不是说我的回答应该被接受;这完全取决于你。顺便说一句,我已经给另一个回答点了赞。 - Keith Thompson
如果您更改了接受的答案,我不会感到冒犯。通常我会等一两天再接受答案,因为这会吸引更多的访问者,答案也往往因此获得更多的赞同。 :) - Kijewski
谢谢您的回复 - 在这种情况下,我将采纳Keith的答案,因为他的答案包含了一个写得很好并且解释得很清楚的行为。谢谢你们两个。 - Stefan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接