0

I'm trying to compile the following source code which successfully compiles on both gcc and Microsoft's cl.exe.

void SomethingBeforeExit();

void SomethingBeforeExit()
{
    // some code
    _exit(0);
}

int main(int argc, char *argv[])
{
    // some code
    atexit(SomethingBeforeExit);
}

However, I'm getting a C4113 warning from cl.exe with the following message:

SomeCode.c(10): warning C4113: 'void (__cdecl *)()' differs in parameter lists from 'void (__cdecl *)(void)'

As I said, the source code still compiles successfully and appears to work. My goal is to prevent this warning from happening in cl, as gcc doesn't generate any warnings upon compilation.

I assume that the declaration of that function is not being treated as void SomethingBeforeExit(void), however, I don't know how to specifically declare a function's parameter list as void.

I'm using VS14 and C/C++ 19.00.23918 for x86 for cl.exe and gcc v5.4.0 compilers to compare generated warnings.

David Refoua
  • 2,879
  • 1
  • 26
  • 48
  • 1
    The rule is pretty clear that the function you pass to `atexit` must be `(void)`. For historical reasons, `()` is not equivalent to `(void)` in (some versions of) C as it is in C++. – David Schwartz Aug 03 '17 at 20:08
  • Possible duplicate of [Is it better to use C void arguments "void foo(void)" or not "void foo()"?](https://stackoverflow.com/questions/693788/is-it-better-to-use-c-void-arguments-void-foovoid-or-not-void-foo) – cadaniluk Aug 03 '17 at 20:08
  • @Downvoter not a direct duplicate question, I just didn't know how `()` declaration was treated as @David Schwartz stated. This question is about the `C4113` warning specifically which is related to incorrect parameter passing, not whether using `(void)` is better than `()`. – David Refoua Aug 03 '17 at 20:18
  • 1
    Which _line_ generated the waring: `atexit(SomethingBeforeExit);` or `void SomethingBeforeExit() {` ? – chux - Reinstate Monica Aug 03 '17 at 21:00
  • @chux the first one. – David Refoua Aug 03 '17 at 22:03

2 Answers2

6

In C, empty parentheses in a function declaration doesn't mean "no parameters." Instead, it means any number of parameters (similar to ... in C++). Perhaps what you meant to declare was void SomethingBeforeExit(void).

Jonathan Leffler
  • 666,971
  • 126
  • 813
  • 1,185
Dan Waxman
  • 91
  • 1
  • 5
  • Thank you, that's the correct answer. I failed to realize that `identifier2` in the MS documentation referred to the passed parameters. – David Refoua Aug 03 '17 at 20:13
2

OP is not using a compliant C99/C11 compiler. The code is valid C (C99/C11).

// declares function SomethingBeforeExit and no info about it parameters.
void SomethingBeforeExit();

// declares/defines function SomethingBeforeExit and 
// indirectly specifies the parameter list is `(void)`
// This does not contradict the prior declaration and so updates the parameter signature.
void SomethingBeforeExit() {
   ...  
}

int main(int argc, char *argv[]) {
  ...
  // `atexit() expects a `void (*f)(void))`
  atexit(SomethingBeforeExit);

In C89 the void SomethingBeforeExit() { ...} definition still says nothing about the parameter list. This is likely the cause of OP's problem.

To fix:

void SomethingBeforeExit(void) { 
  ...
}

The prior void SomethingBeforeExit(); declaration need not update, yet would be in good to also update that so parameter checking can happen with code that does not see the definition.

chux - Reinstate Monica
  • 113,725
  • 11
  • 107
  • 213
  • The definition `void SomethingBeforeExit(){ … }` still doesn't provide a prototype for the function. The compiler needn't complain if you invoke `SomethingBeforeExit(1, 3.14159, "Apocalypse Then"):` as there is no prototype in scope to make it invalid. – Jonathan Leffler Aug 03 '17 at 21:29
  • As I read C11 §6.7.6.3 14 "An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters." indicates the function takes no parameters - aka `(void)`. The `void SomethingBeforeExit( etc.` (as declaration/definition) at least prototypes the function as returning nothing. – chux - Reinstate Monica Aug 03 '17 at 21:37
  • 1
    @JonathanLeffler Well at least my gcc version 5.4.0 agrees with you. – chux - Reinstate Monica Aug 03 '17 at 21:40
  • 100% agreed that the return type is declared — that was part of the compatibility with pre-standard C, of course. – Jonathan Leffler Aug 03 '17 at 21:43
  • @JonathanLeffler I used `#define info_type(X) _Generic((X), \ void (*)(void): "void (*)(void)", \ int: "int", \ default: "?" \ )` and `void SomethingBeforeExit() { } void SomethingBeforeExitV(void) { }` and `puts(info_type(SomethingBeforeExit)); puts(info_type(SomethingBeforeExitV));` and both print `"void (*)(void)"`. So with/without `void` make for the same function type, yet gcc only complains about `SomethingBeforeExitV(1)` and not `SomethingBeforeExit(1)` - Hmmm. - An interesting finding. – chux - Reinstate Monica Aug 03 '17 at 21:46
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/151003/discussion-between-jonathan-leffler-and-chux). – Jonathan Leffler Aug 03 '17 at 21:49
  • The chat contains the details and much discussion, but it appears that §6.9.1 Function definitions, ¶7 does mean that the function definition `SomeType FunctionName() { … }` does not provide a prototype for the function, even though the compiler knows it is a function that takes no arguments. The reason is probably 'historical' and/or 'it makes the language grammar easier to parse'. Warnings could be generated by the compiler on misuse; that would be a QoI (quality of implementation) issue. – Jonathan Leffler Aug 03 '17 at 22:56
  • 1
    Jonathan Leffler and I have agreed on all points aside form we differ on "SomeType FunctionName() { … } provides a prototype or not". IAC, we agree that `SomeType FunctionName(void) { … }` is better. – chux - Reinstate Monica Aug 03 '17 at 23:07
  • @JonathanLeffler Further research bring me closer to your "SomeType FunctionName() { … } does not provide a prototype for the function". I'd say now `SomeType FunctionName() { … }` only provide an old style or no-info-about-input-parameters prototype for the function. And need to take back "compiler knows it is a function that takes no arguments". I'd say it nows nothing input parameters about that too. My `_Generic` and pointer assignment testing was flawed. Perhaps I'll post a Q/A in the future. IAC, `SomeType FunctionName(void) { … }` remains better. – chux - Reinstate Monica Aug 04 '17 at 16:36
  • 1
    Thanks for the update. I believe we are (and always have been) 100% in agreement that the explicit `SomeType FunctionName(void) { … }` definition is preferred (and the `SomeType FunctionName(void);` declaration, too). – Jonathan Leffler Aug 04 '17 at 16:48
  • Just looked at the grammar rules in §6.7.6 Declarators. There's a rule `direct-declarator` with two relevant options: `direct-declarator ( parameter-type-list )` and `direct-declarator ( identifier-list-opt )`. That means that the empty parentheses version has an empty optional identifier-list. The `parameter-type-list` is not allowed to be empty. It supports the `, ...` at the end and that must follow at least one parameter-declaration — there'd need to be some juggling of the grammar rules to make the parameter-type-list optional instead of the identifier-list. It could be done (but hasn't). – Jonathan Leffler Aug 04 '17 at 18:17