Is this legal C?

M

Max

This was asked in a C test. Is the following code legal C?
Is it legal in C90? C99?

#define main()
int main
#define mainbody () { return 0; }
mainbody
 
K

Keith Thompson

pete said:
Yes. Yes.


It's as legal as

int main(){return 0;}

is.

There's a fairly subtle argument that "int main()" is not actually
required to be supported, but for all practical purposes it's ok.
"int main(void)" is better, though.

Note that "main" is defined as a function-like macro, and "mainbody"
is defined as an object-like macro.

Compiling the code and/or examining the preprocessor output (assuming
your compiler supports this) won't definitively answer the question,
but it will give some good hints.
 
K

Kenny McCormack

Keith Thompson said:



I remember that this has come up before. I wasn't completely convinced
then, and I'm not completely convinced now.

A function definition that has no parameter list takes no parameters, and
is thus precisely equivalent to int main(void) in semantic terms. (Note
that we're not talking about mere definitions here.)

Since the Standard only requires of main that it be equivalent to int
main(void) or int main(int argc, char **argv), and since int main() is
equivalent to int main(void), it seems to me that int main() is perfectly
acceptable - to the compiler, at least, even though the stylist might
object.

Nice to see clc regulars returning to their home territory.

Coming soon: Casting the return value of malloc().
 
K

Keith Thompson

Richard Heathfield said:
Keith Thompson said:


I remember that this has come up before. I wasn't completely convinced
then, and I'm not completely convinced now.

A function definition that has no parameter list takes no parameters, and
is thus precisely equivalent to int main(void) in semantic terms. (Note
that we're not talking about mere definitions here.)

Since the Standard only requires of main that it be equivalent to int
main(void) or int main(int argc, char **argv), and since int main() is
equivalent to int main(void), it seems to me that int main() is perfectly
acceptable - to the compiler, at least, even though the stylist might
object.

<snip>

Reference: C99 5.1.2.2.1 Program startup

For this definition:

int main() { return 0; }

to be valid (by "valid" here, I mean that an implementation is
required to support it with well-defined behavior), it must be
"equivalent" to this definition, which is undeniably valid:

int main(void) { return 0; }

It's equivalent by itself, when considered only as a self-contained
function definition. However, the definition also provides a
*declaration*. I assert that the two definitions are *not*
equivalent, because they provide non-equivalent declarations.

Assume an implementation that does accept "int main() { return 0; }"
with the obvious meaning (this is allowed by the "or in some other
implementation-defined manner" clause if not by the "or equivalent"
clause). Or, if you prefer, assume that "int main() { return 0; }" is
valid. Then this program:

int main() { return 0; }

void foo(void) { main(42); }

is valid (the declaration of main requires an unspecified number and
type of arguments; the call to main would invoke undefined behavior if
it were executed, but it never is). But this program:

int main(void) { return 0; }

void foo(void) { main(42); }

violates the constraint in C99 6.5.2.2p2.

Therefore, a definition using "int main()" is not equivalent to one
using "int main(void)", and thus an implementation needn't support it.

Note that, assuming my reasoning is correct, "int main()" doesn't
violate any constraint. A program that uses it merely invokes
undefined behavior. As it happens, every implementation I know of
responds to this undefined behavior by quietly accepting the
definition.

I'm not convinced that this was the intent, and I'd be unsurprised to
find that the members of the committee either intended "int main()" to
be valid, or just didn't think about it.

Implementers can avoid the issue by accepting "int main()", and
programmers can avoid the issue by using not using "int main()", so
it's not a huge deal either way.

I see I posted a similar question to comp.std.c last July,
subject "Is 'int main() { /* ... */ }' valid?":

http://groups.google.com/group/comp.std.c/browse_frm/thread/fef53cc781d555a4/41630179750145a9

but re-reading that thread I see I was actually asking about a
different subtle issue.
 
K

Keith Thompson

pete said:
main isn't defined as a macro.
main() is defined as a macro.

I understand what you mean, but a macro name is an identifier; the
parentheses aren't part of the name.

See also the standard's definitions of "object-like macro" and
"function-like macro".

#include <stdio.h>

void Keith_Is_Right(void);

#define main()
int main
#define mainbody () { Keith_Is_Right(); return 0; }
mainbody

void Keith_Is_Right(void)
{
#ifdef main
puts("Yes, main is defined as a macro.");
#else
puts("Never mind.");
#endif
}
 
K

Keith Thompson

Richard Heathfield said:
Keith Thompson said: [...]
Reference: C99 5.1.2.2.1 Program startup

For this definition:

int main() { return 0; }

to be valid (by "valid" here, I mean that an implementation is
required to support it with well-defined behavior), it must be
"equivalent" to this definition, which is undeniably valid:

int main(void) { return 0; }

It's equivalent by itself, when considered only as a self-contained
function definition. However, the definition also provides a
*declaration*. I assert that the two definitions are *not*
equivalent, because they provide non-equivalent declarations.

I disagree, because of 3.5.4.3 (C89):

"An identifier list declares only the identifiers of the parameters of the
function. An empty list in a function declarator that is part of a
function definition specifies that the function has no parameters. The
empty list in a function declarator that is not part of a function
definition specifies that no information about the number or types of the
parameters is supplied."

The wording is almost identical in C99:

6.7.5.3(14) (C99):
"An identifier list declares only the identifiers of the parameters of the
function. An empty list in a function declarator that is part of a
definition of that function specifies that the function has no parameters.
The empty list in a function declarator that is not part of a definition
of that function specifies that no information about the number or types
of the parameters is supplied."

So the empty list in int main() { return 0; } specifies that the function
has no parameters, which means that it is semantically equivalent to int
main(void), QED.

Yes, the empty list specifies, for purposes of the definition (the
body of the function), that the function has no parameters. So the
two definitions are equivalent in what they say about main's
parameters.

But they are not equivalent in other ways.

An empty list doesn't specify that a caller must pass no arguments,
because it doesn't provide a prototype.

Given "int main(void) { ... }", an attempt to call "main(42)" violates
a constraint. Given "int main(void) { ... }", such a call does not
violate a constraint (though it does invoke undefined behavior).

A prototype is not equivalent to the absence of a prototype.
 
W

Willem

Max wrote:
) This was asked in a C test. Is the following code legal C?
) Is it legal in C90? C99?
)
) #define main()
) int main
) #define mainbody () { return 0; }
) mainbody

Correct me if I'm wrong, but after preprocessing, doesn't this expand to:

---
int main

{ return 0; }
---

Which is missing the parens after main, and so is illegal ?


SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
 
E

Erik Trulsson

Willem said:
Max wrote:
) This was asked in a C test. Is the following code legal C?
) Is it legal in C90? C99?
)
) #define main()
) int main
) #define mainbody () { return 0; }
) mainbody

Correct me if I'm wrong, but after preprocessing, doesn't this expand to:

You are wrong. 'main' is defined as a function-like macro taking no arguments
and expanding to an empty string. The 'main' in "int main" does not match an
invocation of this macro and is thus not replaced.
'mainbody' on the other hand is an object-like macro which is defined as "()
{ return 0; }".

(It makes a great deal of difference if there is a space or not between the
macro name and a '(' when defining a macro.)

After preprocessing it should thus expand to:
 
H

Hans Schneider

pete said:
Yes. Yes.

Must be some gcc extension. This compiles not even with LCC-WIN32:

lc -ansic -O -c main.c
Error main.c: 4 Syntax error; missing semicolon before `mainbody'
Warning main.c: 4 no type specified. Defaulting to int
Error main.c: 5 Syntax error; missing semicolon before `end of input'
2 errors, 1 warning
1 error
 
B

Ben Bacarisse

Hans Schneider said:
Must be some gcc extension.

The reference is to check the standard, not to try various
implementations. The standard is clear that main does not get
expanded and that mainbody should be because mainbody is an
object-like macro, not a function-like one.
This compiles not even with LCC-WIN32:

Then file a bug report if you are interested. lcc-win32 fails to see
mainbody as a macro that should be expanded. If we remove the
entirely unused #define main() the lcc-win32 gets it right. That
unused definition of main is throwing it. I can guess why -- the
pre-processor must look to see if 'main' is followed by '(', and this
must be done without expanding macros. However, having seen that
'main' is not followed by a '(', the token that was looked at needs to
checked to see if it *does* expand. A simple example of the bug is
this:

#define F()
#define M
F M

which should expand to 'F' but with lcc-win32 you get 'F M'.
 
C

CBFalconer

Hans said:
Must be some gcc extension. This compiles not even with LCC-WIN32:

lc -ansic -O -c main.c
Error main.c: 4 Syntax error; missing semicolon before `mainbody'
Warning main.c: 4 no type specified. Defaulting to int
Error main.c: 5 Syntax error; missing semicolon before `end of input'
2 errors, 1 warning
1 error

That does tend to indicate some failings in lcc-win32.
 
K

Keith Thompson

Erik Trulsson said:
(It makes a great deal of difference if there is a space or not between the
macro name and a '(' when defining a macro.)
[...]

Right. But it *doesn't* matter whether there's a space between the
macro name and a '(' when *invoking* a macro.

Terminology: an "object-like macro" is one that takes no arguments; a
"function-like macro" is one that does take arguments.

For example:

#define OBJ () /* an object-like macro expanding to () */
#define FUNC() /* a function-like macro expanding to nothing */

FUNC /* Not replaced */
FUNC() /* Replaced by nothing */
FUNC () /* Also replaced by nothing; the space is irrelevant */

This isn't relevant to the example we're discussing, but I have
seen it trip up some people.

In most C syntax, whitespace is irrelevant (except as part of a
literal or to separate two tokens that otherwise might be a single
token). An exception is made in macro definitions; this special
case allows the expansion of an object-like macro to start with
'(', while allowing for function-like macros. There's no need
for such an exception for invocations of function-like macros.
If there were, it would defeat the purpose of making function-like
macros act (more or less) like function calls.

(And let me remind everyone to compile your code before posting it.
I made a really embarrassing mistake in the initial version of
the above example; I wouldn't have caught it if I hadn't tried it
out first. No, I'm not going to tell you what the mistake was.)
 
H

Herbert Rosenau

Keith Thompson said:



I remember that this has come up before. I wasn't completely convinced
then, and I'm not completely convinced now.

A function definition that has no parameter list takes no parameters, and
is thus precisely equivalent to int main(void) in semantic terms. (Note
that we're not talking about mere definitions here.)

Since the Standard only requires of main that it be equivalent to int
main(void) or int main(int argc, char **argv), and since int main() is
equivalent to int main(void), it seems to me that int main() is perfectly
acceptable - to the compiler, at least, even though the stylist might
object.

No, its not equivalent.

int main(void) ia a function returning int and receiving no
parameters.

Whereas int main() is a function returning int an receiving an unknown
number of parameters of unknown types.

--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2R Deutsch ist da!
 
C

CBFalconer

Keith said:
.... snip ...

Right. But it *doesn't* matter whether there's a space between
the macro name and a '(' when *invoking* a macro.

Terminology: an "object-like macro" is one that takes no
arguments; a "function-like macro" is one that does take arguments.

For example:

#define OBJ () /* an object-like macro expanding to () */
#define FUNC() /* a function-like macro expanding to nothing */

FUNC /* Not replaced */
FUNC() /* Replaced by nothing */
FUNC () /* Also replaced by nothing; the space is irrelevant */

This isn't relevant to the example we're discussing, but I have
seen it trip up some people.

Where do you see this in the standard? I always considered it as
separated by the lexer, which can find the "FUNC(" in the stream,
and expand it. However the stream "FUNC (" is textually
different. If I am full of it I want to find out.
 
H

Harald van Dijk

Keith said:
[...]
#define FUNC() /* a function-like macro expanding to nothing */
[...]
FUNC () /* Also replaced by nothing; the space is irrelevant */

Where do you see this in the standard? I always considered it as
separated by the lexer, which can find the "FUNC(" in the stream, and
expand it. However the stream "FUNC (" is textually different. If I am
full of it I want to find out.

6.10.3p10:
"[...] Each subsequent instance of the function-like macro name followed
by a ( as the next preprocessing token introduces the sequence of
preprocessing tokens that is replaced by the replacement list in the
definition (an invocation of the macro). [...]"

5.1.1.2p1[3]:
"3. The source file is decomposed into preprocessing tokens and sequences
of white-space characters (including comments). [...]"

Whitespace is retained by the lexer, but not as pp tokens. In FUNC (), the
next pp token after FUNC is (.
 
W

Willem

Erik Trulsson wrote:
) You are wrong. 'main' is defined as a function-like macro taking no arguments
) and expanding to an empty string. The 'main' in "int main" does not match an
) invocation of this macro and is thus not replaced.
) 'mainbody' on the other hand is an object-like macro which is defined as "()
) { return 0; }".

Oh right, I overlooked the parens after the mainbody macro.
My bad.


SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
 
C

CBFalconer

Harald said:
CBFalconer said:
Keith said:
[...]
#define FUNC() /* a function-like macro expanding to nothing */
[...]
FUNC () /* Also replaced by nothing; the space is irrelevant */

Where do you see this in the standard? I always considered it as
separated by the lexer, which can find the "FUNC(" in the stream,
and expand it. However the stream "FUNC (" is textually different.
If I am full of it I want to find out.

6.10.3p10:
"[...] Each subsequent instance of the function-like macro name
followed by a ( as the next preprocessing token introduces the
sequence of preprocessing tokens that is replaced by the
replacement list in the definition (an invocation of the macro).
[...]"

5.1.1.2p1[3]:
"3. The source file is decomposed into preprocessing tokens and
sequences of white-space characters (including comments). [...]"

Whitespace is retained by the lexer, but not as pp tokens. In
FUNC (), the next pp token after FUNC is (.

I guess I am full of it. My technique is easier to parse, but may
not fit into doing it with lex and yacc. Time to forget that
mistake.
 
H

Herbert Rosenau

Herbert Rosenau said:



Wrong for a definition of int main(), for reasons that I have posted
elsethread.
You need a prototype to specifiy the type(s) and number of parameters.
Without a prototype you can't do so.

int main() is noways a prototype because it does not specifiy a bit of
type or number of parameters. Yes, it is legal C, but gives no
information about the parameters.

int main(void) as declaration is a prototype. As definition without
preceeding declaration it works as declaration too.

There is no difference between anay function and main(). Either you
defines a prototype (implicity by definition) or you does not. Without
a prototype the compiler will not able to gather the number of
parameters and theyr type. Saying in a prototype the number of
parameters is void is clearly different from having number and type of
parameters is unknown. Without prototype but with declaration you can
call a function differently in a translation unit, with a prototype
you can't. This is why prototypes are introduced in the standard.

--
Tschau/Bye
Herbert

Visit http://www.ecomstation.de the home of german eComStation
eComStation 1.2R Deutsch ist da!
 
H

Harald van Dijk

You need a prototype to specifiy the type(s) and number of parameters.
Without a prototype you can't do so.

Do you believe this code:

int main() {
static int dummy = 0;
if (dummy < 42)
{
dummy++;
return main(0, 1, 2, 3, 4, 5);
}
return 0;
}

is valid? I'm _not_ asking if you expect compilers to complain about the
code, I'm asking if you believe this code is guaranteed to behave the same
way on all current and future C implementations.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top