4

Note: this is not the same as func() vs func(void) in c99, because the question here specifically asks about the implementation of a zero-argument function following a valid declaration.

Should the implementation of a zero-argument include the void keyword? Specifically, does the C standard have anything to say about the implementation of the following two functions? Note that both foo1 and foo2 are declared as zero-argument functions; the only difference is in the implementation, not in the declaration:

#include <stdio.h>

int foo1(void);  // inform compiler that foo1 and foo2 are zero-args fns.
int foo2(void);

int main() {
  printf("%d\n", foo1());
  printf("%d\n", foo2());
  return 0;
}

int foo1(void) { return 22; }
int foo2() { return 22; }

I note that gcc -Wall -std=c99 -Wpedantic foo.c -o foo compiles and executes without any warnings or errors, but is there any violation of the standard going on?

fearless_fool
  • 33,645
  • 23
  • 135
  • 217
  • 2
    Which C Standard? `foo() {}` and `foo(void) {}` are exactly the same. – J. Doe Mar 03 '20 at 23:49
  • 2
    You might want to add the [tag:language-lawyer] tag. – Fabio says Reinstate Monica Mar 03 '20 at 23:51
  • @J.Doe Cool - if they're really equivalent, then that's the answer to my question. – fearless_fool Mar 03 '20 at 23:51
  • @fearless_fool it seemed to me that you want a cite from "the" standard as an answer. That's why i asked what language standard you are interested in. – J. Doe Mar 03 '20 at 23:52
  • 2
    The linked thread answers this as well. – IS4 Mar 03 '20 at 23:53
  • 2
    The topic you referenced gives the answer: **An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters.** So, `int func2() {...}` is identical to `int func2(void);`. – 273K Mar 03 '20 at 23:53
  • @S.M. That answer only applies to C99. As the OP stated, this is not a duplicate of that question because [the answer has changed](https://port70.net/~nsz/c/c11/n1570.html#6.11.6). – Andrew Henle Mar 03 '20 at 23:55
  • @AndrewHenle "obsolescent" != "obsolete". – 273K Mar 04 '20 at 00:00
  • @S.M. Are you claiming that [**6.7.6.3 Function declarators (including prototypes)** of the C11 standard](https://port70.net/~nsz/c/c11/n1570.html#6.7.6.3) has the same meaning as the quoted section of the C99 standard in the linked question? It looks completely different to me. What about C17? This is no dupe - the linked question is pure C99 to the point of being titled **`func()` vs `func(void)` in c99**. – Andrew Henle Mar 04 '20 at 00:02
  • @J.Doe How about c99 for starters? But if the two forms are equivalent for any "modern" version of C, that's sufficient. If there are substantive differences depending on the version, I'd like to know that too. – fearless_fool Mar 04 '20 at 00:06
  • @AndrewHenle The same thing in C11. https://port70.net/~nsz/c/c11/n1570.html#6.7.6.3p14 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**. – 273K Mar 04 '20 at 00:07
  • @S.M. Thanks for the comments, but note that I'm asking about the implementation rather than the declaration. The declaration clearly specifies that the functions take zero args. – fearless_fool Mar 04 '20 at 00:17
  • @S.M. If there is no earlier prototype, then `int x() {...}` is different to `int x(void) { ... }` . The difference is explained [in the thread linked by OP](https://stackoverflow.com/a/41805712/1505939). But that is offtopic for this question which specifically asks about the behaviour in the presence of a prototype – M.M Mar 04 '20 at 00:23
  • 1
    I wonder if anyone has pointed it out. If you don't use `-std=c99`, GCC defaults to its own dialect called `-std=gnu99` or `-std=gnu11`. So doing `-Wall` isn't really going to complain about standards violation. To really see standards violation, you need to enable `-std=c99 -Wpedantic` and GCC will warn. – Unmanned Player Mar 04 '20 at 02:45
  • An interesting tangent: If you leave `void` out of the function declarations, you can call both functions with extra arguments and no warnings: foo1(5,3); foo2(5,3); --works on gcc, clang, tcc with warnings enabled. But if you move the definitions above main(), it will error: too many arguments to function. – hellork Mar 04 '20 at 03:56
  • So explicitly declaring functions as (void) instead of () ensures that they can *never* be called with arguments--even if defined further down in the code. As another way of saying the same thing. – hellork Mar 04 '20 at 04:08
  • @UnmannedPlayer: "To really see standards violation, you need to enable -std=c99 -Wpedantic and GCC will warn." That's the most valuable comment I've seen all week -- thank you. Updating post accordingly. – fearless_fool Mar 04 '20 at 04:26
  • @fearless_fool UnmannedPlayer That's not just wrong, it's _dangerously_ wrong. The best way to get accurate information about standards violations _in your own code_ using GCC is to specify `-std=gnuXX -Wall -Wpedantic` (where XX is the C standard version you want). `-Wall -Wpedantic` gives you all the standard-mandated diagnostics; `-Wpedantic` by itself does not. More seriously, in current GCC the hyperconformant `-std=cXX` mode changes only three things relative to `-std=gnuXX`, and all three have the potential to expose bugs in the system headers. You don't want to deal with that. – zwol Mar 09 '20 at 14:06
  • @zwol Please read about [`-isystem`](https://gcc.gnu.org/onlinedocs/gcc/Directory-Options.html) switch to GCC. Files included from `-isystem` won't undergo standards conformance checks. In fact, GCC includes `/usr/include` by default like that and I include older versions of custom compiled libpcap's headers using `-isystem` for this exact reason. I don't think it's _dangerously wrong_ :) – Unmanned Player Jan 16 '22 at 22:39

1 Answers1

3

The code you have posted is correct. int foo2(void); declares foo2 as taking no arguments, and forms a prototype.

The function definition must be compatible with this; and a definition with empty parentheses is compatible with this prototype. This is specified in C11 6.7.6.3/15, which is a mouthful:

For two function types to be compatible, both shall specify compatible return types. Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of the ellipsis terminator; corresponding parameters shall have compatible types. If one type has a parameter type list and the other type is specified by a function declarator that is not part of a function definition and that contains an empty identifier list, the parameter list shall not have an ellipsis terminator and the type of each parameter shall be compatible with the type that results from the application of the default argument promotions. If one type has a parameter type list and the other type is specified by a function definition that contains a (possibly empty) identifier list, both shall agree in the number of parameters, and the type of each prototype parameter shall be compatible with the type that results from the application of the default argument promotions to the type of the corresponding identifier. (In the determination of type compatibility and of a composite type, each parameter declared with function or array type is taken as having the adjusted type and each parameter declared with qualified type is taken as having the unqualified version of its declared type.)

The reason there's so much text on this point is that C originally only had K&R style functions, and then prototypes were added . So there has to be text to cover all possible combinations of K&R style mixed with prototype style . The section beginning with my bolded part refers to using a K&R-style function definition when the function has previously been declared with a prototype.


Also relevant: use of empty parentheses is obsolescent in C11 (6.11.6).

Certain features are obsolescent, which means that they may be considered for withdrawal in future revisions of this International Standard. They are retained because of their widespread use, but their use in new implementations or new programs is discouraged.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • To make it completely clear: are you saying that both function definitions (`foo1` and `foo2`) are considered compatible with their respective prototypes? – fearless_fool Mar 04 '20 at 00:29
  • 2
    @fearless_fool yes , I thought it went without saying that `int foo1(void)` is compatible with `int foo1(void)` – M.M Mar 04 '20 at 00:30