Thread: Declare 'int main()', in C, isn't the same as in C++

  1. #1
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078

    Declare 'int main()', in C, isn't the same as in C++

    Well... ok... will do the same thing as int main(void), but to declare a function without arguments, in C, is the same as telling the compiler it must accept ANY number of arguments in the function call, but ignore them.

    For example:
    Code:
    int f() { return 0; }
    This function can be called as:
    Code:
    x = f();
    x = f(1);
    x = f(1, "fred");
    x = f(1, "fred", 3.14, NULL);
    ...
    And the compiler is ok with it... Try it...

    In C++ the empty arguments list means 'void'.

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by flp1969
    to declare a function without arguments, in C, is the same as telling the compiler it must accept ANY number of arguments in the function call
    That is true for a function declaration that is not also a function definition, but in a function definition, an empty parameter identifier list declares that the function has no parameters, i.e., it is equivalent to having void as the parameter type list. In your example, the function named f is defined as such, hence it has no parameters, so "the compiler is ok with it" is not guaranteed to be true, even if it happens to be true for the compiler with which you tested your code.

    See:
    Quote Originally Posted by C99 Clause 6.7.6.3 Paragraphs 6,10,14
    A parameter type list specifies the types of, and may declare identifiers for, the parameters of the function.

    The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters.

    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.
    Also, while an empty parameter identifier list in a function declaration does imply that the compiler "must accept ANY number of arguments in the function call", because there is no parameter type list, the forward declaration is not a function prototype, hence failure to make the call match the function definition results in undefined behaviour, so in your example, the function calls other than the first result in undefined behaviour (apart from the fact that the declaration of f is also its definition). Consequently, a compiler doing whole program static analysis might be able to discover this mismatch, and hence emit a diagnostic despite your claim that "the compiler is ok with it".
    Last edited by laserlight; 03-21-2019 at 05:30 AM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    6.11.7 (FUTURE language defintions) states that "The use of function declarators with empty parentheses (not prototype-format parameter
    type declarators) is an obsolescent feature". It means the compiler follows previous standards (C90? ANSI?) and still accepts this declarations (and definitions)...

    As far as I know, there is no provision for "unspecified" or "undefined behavior" on Annex J for this type of calls. And 6.5.6.3 (not 6.7.6.3) states clearly, in both cases, declarations and definitions, will behave as if there is no arguments. But says nothing about the function call. It is implied that all arguments will be discarded (as it is!).

    Yes... I poorly choose the word "must". I meant this behavior is observed in every compiler I know (including the old Turbo-C for MS-DOS and old copilers for some old Unix flavors).

  4. #4
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    A simple test:
    Code:
    $ gcc -std=c99 -pedantic -xc -Wall -Wextra -include stdio.h - <<EOF
    int f(){return -1;}
    int main(){printf("%d\n", f(1,2,3));}
    EOF
    $ ./a.out
    -1
    Where are the warnings or errors?
    Last edited by flp1969; 03-21-2019 at 09:17 AM.

  5. #5
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    Because in C, int f() is synonymous with int f(...), except that saying that is invalid syntax.

    In the same vein that historic C assumed everything was an int, so
    Code:
    f(){return -1;}
    was also implicitly declaring f as returning int.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  6. #6
    Registered User
    Join Date
    Dec 2017
    Posts
    1,632
    Quote Originally Posted by flp1969 View Post
    Where are the warnings or errors?
    Do you know what "undefined behavior" is?
    It certainly doesn't require errors or warnings.
    A little inaccuracy saves tons of explanation. - H.H. Munro

  7. #7
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    Quote Originally Posted by john.c View Post
    Do you know what "undefined behavior" is?
    It certainly doesn't require errors or warnings.
    Yep... show me where, in the standard, this type of CALL is undefined behavior... Clearly, the declaration and definition isn't (it is "obsolete", by 6.11.7, but not "undefined")...

    This example is my atempt to refute @laserlight conclusion:

    ... Consequently, a compiler doing whole program static analysis might be able to discover this mismatch, and hence emit a diagnostic despite your claim that "the compiler is ok with it.
    Last edited by flp1969; 03-21-2019 at 09:49 AM.

  8. #8
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by flp1969
    And 6.5.6.3 (not 6.7.6.3) states clearly, in both cases, declarations and definitions, will behave as if there is no arguments.
    I'm quoting from C99, not C11, as I don't have a copy of C11 (yeah, I know I could look up a late draft copy, but it feels a little dishonest to cite from a standard when you're actually citing from a late draft of that standard), but these are the kind of things that are unlikely to change significantly, and from what I am aware, parameter identifier lists haven't actually been removed from C11, despite being declared obsolescent in C99... but I guess it wasn't a priority since other than empty parameter identifier lists, I have pretty much never seen parameter identifier lists used these days.

    Quote Originally Posted by flp1969
    Yep... show me where, in the standard, this type of CALL is undefined behavior.
    It is undefined behaviour as implied by the clause on function calls (duh ), but if you want an explicit listing, it is also listed in the annex on undefined behaviour:
    Quote Originally Posted by C99 Annex J.2 Undefined behavior
    • For a call to a function without a function prototype in scope, the number of arguments does not equal the number of parameters (6.5.2.2).
    • For call to a function without a function prototype in scope where the function is defined with a function prototype, either the prototype ends with an ellipsis or the types of the arguments after promotion are not compatible with the types of the parameters (6.5.2.2).
    • For a call to a function without a function prototype in scope where the function is not defined with a function prototype, the types of the arguments after promotion are not compatible with those of the parameters after promotion (with certain exceptions) (6.5.2.2).
    From what I see, the first of the items I extracted applies: the definition of f is not "a declaration of a function that declares the types of its parameters" since it is a declaration with a parameter identifier list rather than a parameter type list, hence no function prototype is in scope. The number of arguments does not equal the number of parameters for the function calls other than the first. Therefore, the behaviour is undefined.

    Incidentally, 6.5.2.2 is the clause on function calls in C99, so you could look up the relevant clause in C11. The paragraph that starts with "If the expression that denotes the called function has a type that does not include a prototype" would be the relevant one. The long paragraph might have been reworked into two or three in C11 though, but it should be around there somewhere and say pretty much the same thing since it is unlikely there would have been any need to actually get it to say something else.

    Combined with what you observed about function identifier lists being an obsolescent feature, I'm inclined to discourage the use of empty parameter identifier lists in favour of void as the identifier type list, even for function definitions, so I think that it is good that you brought this up, but I think that it is not so good to present its misuse as being okay (although hopefully newbies will realise that even if it is actually okay, it makes no sense to misuse it in practice).

    EDIT:
    Quote Originally Posted by flp1969
    I meant this behavior is observed in every compiler I know (including the old Turbo-C for MS-DOS and old copilers for some old Unix flavors).
    There's little incentive for modern compiler authors to change this though: without a prototype, if you want to warn instead of ignore, you need to refer to the function definition, but there's a good chance that that is in a library file, maybe even a dynamically linked one! Coupled with non-prototype function declarations being both obsolescent and seeing reduced use in practice, it doesn't seem all that worth it to aid programmers who insist on the old style, and after all no diagnostic is required even when you could have the compiler emit one.
    Last edited by laserlight; 03-21-2019 at 12:50 PM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  9. #9
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    Thanks, @laserlight...

    I apologize for citing 6.5.6.3. Even on C11 spec it is wrong...

    Seems to me these undefined behaviors are related to functions "without a function prototype in scope". That's not what my example demonstrates... The "spirit" of these 3 entries is where there is a call without prototyping on the same module. Like:

    Code:
    #include <stdio.h>
    
    // f() does not have a prototype "in scope" here.
    int main(void) { printf( "%d\n", f() ); }
    This is, of course, is an undefined behavior, since the compiler doesn't know the return type or if the function requires arguments...

    Anyway, I'm not saying this behavior is a good thing or if it is useful or desirable. Quite opposite, I find this very strange, but the standard allows it. It means, for me, only that empty arguments list (and prototypes without identifiers are optional, by the standard!) is semantically different from C++.

    I don't like empty list of arguments, even on C++, to functions with no arguments.

  10. #10
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    By the way... puts a prototype "in scope" with an empty argument list, and the compiler will not complain:

    Code:
    $  gcc -xc -std=c99 -pedantic -Wall -Wextra -include stdio.h -c -o test.o - <<EOF
    int f(); // prototype in scope!
    int main(){printf("%d\n", f());}
    EOF
    $
    In conformity to the standard!

  11. #11
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    No, a prototype of a function is "a declaration of a function that declares the types of its parameters". That's why when you cited the part about "The use of function declarators with empty parentheses" being an obsolescent feature, it was noted "not prototype-format parameter type declarators". In other words, this function declaration is not a function prototype:
    Code:
    int f();
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  12. #12
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    Quote Originally Posted by laserlight View Post
    No, a prototype of a function is "a declaration of a function that declares the types of its parameters". That's why when you cited the part about "The use of function declarators with empty parentheses" being an obsolescent feature, it was noted "not prototype-format parameter type declarators". In other words, this function declaration is not a function prototype:
    Code:
    int f();
    As specified in the grammar, item 6.7.5, and Annex A [A.2.1 (Expressions)], the identifiers list is optional. So int f(); is a function prototype. An obsolete construction, but valid.

    6.7.5.3 §3: "An identifier list in a function declarator that is not part of a definition of that function
    shall be empty."
    Last edited by flp1969; 03-21-2019 at 05:29 PM.

  13. #13
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    A prototype has a parameter type list (in which the identifier names are optional), not a parameter identifier list (which is what the syntax says is optional).

    If indeed it is a prototype, explain: "The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature". Why would the standard say that "function declarators with empty parentheses" are "not prototype-format parameter type declarators" when function declarations with such declarators are prototypes?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  14. #14
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    Quote Originally Posted by laserlight View Post
    A prototype has a parameter type list (in which the identifier names are optional), not a parameter identifier list (which is what the syntax says is optional).

    If indeed it is a prototype, explain: "The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature". Why would the standard say that "function declarators with empty parentheses" are "not prototype-format parameter type declarators" when function declarations with such declarators are prototypes?
    This is cited on 6.11.6, "Future language directions" (not "in the present" on C99 standard!)... and still in the future for C11 standard!
    Last edited by flp1969; 03-21-2019 at 05:42 PM.

  15. #15
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Eh, that is an incorrect interpretation on two counts:

    1. The standard isn't saying "that function declarators with empty parentheses are prototype-format parameter type declarators is an obsolescent feature". No, it is saying that "the use of function declarators with empty parentheses is an obsolescent feature". The note about "not prototype-format parameter type declarators" is used to clarify that this does not pertain to "prototype-format parameter type declarators".

    2. The standard does not say "will be an obsolescent feature", but "is an obsolescent feature". It pertains to what is its status now, i.e., what it states is for the present. It is a future language direction because it warns that in the future, obsolescent features could be removed.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 2
    Last Post: 03-11-2015, 05:20 AM
  2. Replies: 6
    Last Post: 05-08-2014, 10:57 AM
  3. (split) yet another how to declare main thread
    By DevoAjit in forum C Programming
    Replies: 1
    Last Post: 10-28-2011, 05:54 AM
  4. How to declare a global variable in Main()
    By vnrabbit in forum C Programming
    Replies: 2
    Last Post: 06-20-2002, 12:59 PM
  5. void main(), int main(), argc, argv[]????
    By Jonny M in forum C Programming
    Replies: 3
    Last Post: 03-06-2002, 09:12 AM

Tags for this Thread