Where does this declaration come from: main _2a((argc,argv), int argc, char * argv[])
Asked Answered
G

2

6

I am migrating lots of Oracle Pro*C Code under HP-Unix to a Linux Environment.

In a program there is only such a main method defined:

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

I have never seen such a decalaration before - and haven't found anything with google. Anyhow, it works and from what I see is used as a main function.

Can anybody tell something about this?

Edit: Good hint - there is a macro definition:

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

Still makes no clear view (for me..)

Goods answered 18/11, 2014 at 15:33 Comment(3)
_2a is most likely a macro, defined in some header file.Docia
Maybe _2a is a macro.Apatite
good hint - i can see a macro definition. I am updating the question.Goods
B
7

We just have to expand the macro. You can use gcc -E to invoke just the preprocessor; it will also expand all #include directives, so the output is going to be ugly.

But let's do it manually. The arguments to the macro are token sequences, not expressions.

The macro definition is:

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

and the invocation is:

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

The arguments are:

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

Performing the replacements gives us:

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

This would more commonly be written on multiple lines:

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

This is an old-style function declaration. It's still legal in all versions of C (up to and including the 2011 standard), but it's been officially obsolescent since 1989. The disadvantage of this old form is that it didn't communicate parameter information to callers, so a compiler couldn't warn you if you called a function with the wrong number of types of arguments.

I'll bet there's an alternate definition of the _a2 macro that expands to more modern definition that includes a prototype, something like:

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

With that macro definition, the definition of main instead expands to this:

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

So the _a2 macro (the "a" presumably stands for "argument") lets you write code that can expand either to an old-style function definition (for pre-ANSI compilers) or to a modern definition with a prototype.

A reasonable way to implement it would have been:

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

But since you're very unlikely to find a C compiler that doesn't support prototypes (they've been a standard feature of the language for a quarter of a century now), it would make more sense just to remove the macro altogether and just use modern style function declarations and definitions.

Also, the definition of main is missing the return type of int. Prior to the 1999 C standard, it was legal to omit the return type, under the "implicit int" rule. The 1999 standard dropped this rule and made an explicit return type mandatory. Most C compilers in their default mode still permit the return type to be omitted, perhaps with a warning, but there's no good reason to omit it.

Bechtold answered 18/11, 2014 at 15:55 Comment(4)
This is a very well written answer which includes lots of information. I can only give +1 because I have no +2 - I am very sorry to not accept your great answer as "the answer" because I have already marked Kay's answer. it provided me with the information I needed and was slightly faster - also my own policy does not allow to switch a ticked answer - i am sorry for that. Anyhow I call on everybody to also upvote this answer.Goods
@Stefan: On the rare occasions when I post questions, I usually wait a little while before accepting an answer. (I'm not saying this to suggest that my answer should have been the accepted one; that's entirely up to you. BTW, I upvoted the other answer.)Bechtold
I wouldn't be offended if you changed your accepted answer. I usually wait a day or two, before accepting an answer, because it attracts more visitors and often the answers get more upvotes because of that, too. :)Endemic
Thank you for your nice responses - in this case I will stick the answer to Keith as his answer contains a well written and explained behaviour. thank you both.Goods
E
8

This macro is used to make K&R C-style function definition look more like "modern" C89 definitions.

Expanded the code reads:

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

Or with a better indentation:

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

Which is an ancient way to write:

int main(int argc, char *argv[])
{
    ...
}
Endemic answered 18/11, 2014 at 15:45 Comment(4)
Are you able to explain what the preprocessor is doing? I try to figure out what replacement is done before the compilation starts.Goods
I put the plain macro expansion into my question. Does this answer your question?Endemic
I am just trying to help humanity with the powers I acquired from the years of CRT monitor radiation exposure. ;)Endemic
I'll just point out that it's not quite equivalent to int main(int argc, char *argv[]). The latter causes arguments to be implicitly converted (if they're of an implicitly convertible type) or diagnosed if they're not of the expected type; with a non-prototype definition/declaration, a call with bad arguments has undefined behavior. Also, omitting the return type and letting it default to int was legal up to and including C89/C90/C95, but was never a particularly good idea.Bechtold
B
7

We just have to expand the macro. You can use gcc -E to invoke just the preprocessor; it will also expand all #include directives, so the output is going to be ugly.

But let's do it manually. The arguments to the macro are token sequences, not expressions.

The macro definition is:

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

and the invocation is:

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

The arguments are:

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

Performing the replacements gives us:

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

This would more commonly be written on multiple lines:

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

This is an old-style function declaration. It's still legal in all versions of C (up to and including the 2011 standard), but it's been officially obsolescent since 1989. The disadvantage of this old form is that it didn't communicate parameter information to callers, so a compiler couldn't warn you if you called a function with the wrong number of types of arguments.

I'll bet there's an alternate definition of the _a2 macro that expands to more modern definition that includes a prototype, something like:

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

With that macro definition, the definition of main instead expands to this:

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

So the _a2 macro (the "a" presumably stands for "argument") lets you write code that can expand either to an old-style function definition (for pre-ANSI compilers) or to a modern definition with a prototype.

A reasonable way to implement it would have been:

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

But since you're very unlikely to find a C compiler that doesn't support prototypes (they've been a standard feature of the language for a quarter of a century now), it would make more sense just to remove the macro altogether and just use modern style function declarations and definitions.

Also, the definition of main is missing the return type of int. Prior to the 1999 C standard, it was legal to omit the return type, under the "implicit int" rule. The 1999 standard dropped this rule and made an explicit return type mandatory. Most C compilers in their default mode still permit the return type to be omitted, perhaps with a warning, but there's no good reason to omit it.

Bechtold answered 18/11, 2014 at 15:55 Comment(4)
This is a very well written answer which includes lots of information. I can only give +1 because I have no +2 - I am very sorry to not accept your great answer as "the answer" because I have already marked Kay's answer. it provided me with the information I needed and was slightly faster - also my own policy does not allow to switch a ticked answer - i am sorry for that. Anyhow I call on everybody to also upvote this answer.Goods
@Stefan: On the rare occasions when I post questions, I usually wait a little while before accepting an answer. (I'm not saying this to suggest that my answer should have been the accepted one; that's entirely up to you. BTW, I upvoted the other answer.)Bechtold
I wouldn't be offended if you changed your accepted answer. I usually wait a day or two, before accepting an answer, because it attracts more visitors and often the answers get more upvotes because of that, too. :)Endemic
Thank you for your nice responses - in this case I will stick the answer to Keith as his answer contains a well written and explained behaviour. thank you both.Goods

© 2022 - 2024 — McMap. All rights reserved.