14

Case 1:

void hello(void) {
    //something
}

int main()
{
    hello(1); //error
    return 0;
}

Case 2:

int main(void) {
    //something
    return 0;
}

Execution:

./a.out something something //No error, Why?

Why there is no error? main will not be able to take any arguments. So why it is possible to provide arguments from the command line?

Spikatrix
  • 19,378
  • 7
  • 34
  • 77
GeekyJ
  • 1,610
  • 1
  • 18
  • 32
  • Why would you expect an error? – Oliver Charlesworth Oct 24 '15 at 10:40
  • @OliverCharlesworth Because we are passing argument to main? compiler doesn't come into picture but at runtime/loadtime why there is no error? – GeekyJ Oct 24 '15 at 10:43
  • 2
    The shell *always* sends its arguments to the program - even if you provide none (usually it sends `argv[0]`). `main` is not the same kind of function as `hello`. – Jongware Oct 24 '15 at 10:54
  • I'm interested in hearing the answer too. There must be something inside the runtime that calls main with those arguments. So why is there no linker error when you define a main that deviates from the standard? (Even `main(float)` generates only a warning, no error. And only with -Wall even.) – Mr Lister Oct 24 '15 at 11:00
  • 1
    Out of curiosity, I performed a little experiment. I compiled the same program twice, once with each different signature. (VS2013 if you're interested, because it's what I had on hand.) Ignoring debugging information and timestamps, the two resulting files were exactly the same, down to the byte. This indicates to me that the compiler either replaces the void signature with the standard one (that wouldn't change your program at all), or is smart enough to know that it doesn't matter if you aren't using the args anyway. (Of course, we'd expect the compiler to know that. :) ) – jpfx1342 Oct 24 '15 at 20:37

5 Answers5

14

Because the C compiler and the command line interpreter (or whatever is used to invoke your program) are different things.

The C language allows various ways how main () could be declared.

The command line interpreter will make any arguments available to the program. If the program ignores them, that's none of its business.

The command line interpreter doesn't even know that you used C to compile your program. On my computer, the program could be written in C, C++, Objective-C, Objective-C++, Swift, Fortran, Ada, and so on. Each of these compilers may or may not do things to accept commands from the command line.

gnasher729
  • 47,695
  • 5
  • 65
  • 91
9

Not checking the specification nor compiled result, it will cause no error because the C runtime will get the arguments and pass them to main(), but this type of main() will ignore the passed arguments, and if it is caller's duty to clean up the memory (stack) used as the arguments, it will cause no problems just as getting some arguments and not using them in the code.

This code won't emit errors in C:

void hello(); // in C, the compiler won't check arguments

int main() {
    hello(1); //no error
    return 0;
}

void hello(void) {
    //something 
}
MikeCAT
  • 61,086
  • 10
  • 41
  • 58
  • I never knew that `void hello();` would behave differently from `void hello(void);` Interesting. And I learned C in 1989. Oh well. – Mr Lister Oct 24 '15 at 11:07
  • I think you make an excellent point here, but it's kind of hidden away: "the 'arguments' of `main` *do not need stack cleanup*". Is that correct? (Both meant as how I read it from your answer and, well, factually.) – Jongware Oct 24 '15 at 11:29
  • 3
    @Jongware The arguments passed to `main` are cleaned off the stack by the function that *calls* `main`, which is a shim provided by the C library, and that function doesn't know or care how `main` was declared. This isn't special to `main`; this is the normal C calling convention for all functions. (Compiler extensions like `__stdcall` can change the convention. Bad Things will happen if you apply them to `main`--or to any other function called by the C library.) – zwol Oct 24 '15 at 12:14
  • @zwol: yes - when disassembling one can see that `main` gets called from some startup code, and any arguments are passed "as usual". However, in that case the startup code needs to supply the arguments - and they did not appear "pushed" in any way in *that* function. If they did, it'd be an infinite sequence of pushing - "elephants all the way". – Jongware Oct 24 '15 at 12:18
  • 2
    @Jongware Ah, the *other* thing you might've been trying to ask. Where does `argv` come from in the first place? The kernel loads it into memory along with the program, as part of the [`execve`](http://linux.die.net/man/2/execve) operation, and constructs an initial call frame that tells the C library's startup code where to find it. It's *usually* at the very top of the stack, but it doesn't have to be, and it's never deallocated. – zwol Oct 24 '15 at 12:22
  • 1
    @Jongware If the C library's startup code were to return from that initial stack frame, the program would crash. It is required to call `exit` instead. See for instance the very end of https://sourceware.org/git/?p=glibc.git;a=blob;f=csu/libc-start.c . – zwol Oct 24 '15 at 12:24
  • 2
    @zwol: that must be the answer the OP is looking for. The runtime startup *always* tells the C code where to find the arguments - whether there is one or not. And it does not care if the C code actually bothers to check. – Jongware Oct 24 '15 at 12:28
  • 1
    You can't rely on this kind of code compiling and running without errors. C11 standard, 6.5.2.2.6: "If the number of arguments does not equal the number of parameters, the behavior is undefined." You're relying on implementation quirks, and a different implementation or any sort of optimization might break this code. – user2357112 supports Monica Oct 24 '15 at 19:28
  • @user2357112: Nice catch. As additional context to the reader, I would like to point out that this quote only applies to function call expressions and therefore does not apply to `main` (as `main` is not called from within the program's code), so the reasoning demonstrated in this answer still applies to `main`, even if the particular example shown is undefined behaviour. – Jordan Melo Nov 18 '15 at 22:14
5

Because ./a.out something something is not directly calling your main function. THe main function is being called by the c runtime library. The command line arguments are placed in a region somewhere on the stack (very beginning) by the loader/c runtime. It is upto you if you want to access these arguments or not.

Plus as pointed out in one of the comments as well at least one command line argument is always passed anyways (the name of the program ./a.out to be precise) - so you must have wondered about an error in that case as well.

tapananand
  • 2,905
  • 2
  • 17
  • 32
3

Recall that ISO C specifies the two possible signatures of main: int main(void) and int main(int, char *[]) and the equivalent versions thereof, like int main(int, char **) because of array-to-pointer decay. This is covered more in detail here.

This question can be answered by considering the opposite question: how does the C runtime know what main to call? C doesn't have overload resolution! This is explained here. Briefly, the others are pushed but not accessed because there's no indication from C to do that.

edmz
  • 7,675
  • 2
  • 21
  • 43
2

When you compile your program using gcc program_name.c, compiler will report any possible compile time warning or error. Since the command line arguments are not passed at the compile time, compiler is unaware of it and program just ignore that arguments.

In case of hello, compiler knows the prototype of this function and expects no argument to be passed in its call and hence reports error in case of any argument passed to it.

haccks
  • 97,141
  • 23
  • 153
  • 244