Q:

What is the return value of the function

#include <stdio.h>

int add(int, int);

int main(int argc, char const *argv[])
{
    int a = 2, b = 3;
    printf("%d %d %d\n", a, b, add(a, b));
    return 0;
}

int add(int a, int b) {
    int c = a + b;
    return;
}

The output of the above program is 2 3 5 how can the add function return c without explicitly returning it

A:

It's undefined behaviour.
When a function that's supposed to return a value doesn't do so and the caller uses the return value, it's undefined. Anything you might observe is purely accidental/platform specific behaviour and not be relied upon. The C standard certainly makes no guarantees.
From C11 draft, 6.9.1 Function definitions/12:

If the } that terminates a function is reached, and the value of the
  function call is used by the caller, the behavior is undefined.

A:

The @P.P.'s answer is right: that's an undefined behaviour.
What really happens, instead it strongly depends on the compiler implementation.
The most likely scenario is that the caller function will take a return value anyway. Since the callee function does not return any value, that means the caller function will take a "random"-dirty value from the stack.
I'm going to provide you an example with GCC compiler (without any optimisations):
This is the assembly code for the function add:
push   %rbp
mov    %rsp,%rbp
mov    %edi,-0x14(%rbp)
mov    %esi,-0x18(%rbp)
mov    -0x14(%rbp),%edx
mov    -0x18(%rbp),%eax
add    %edx,%eax
mov    %eax,-0x4(%rbp)
nop
nop
pop    %rbp
retq

As you can see, the addition is performed, stored in the c variable.
The result value is luckily stored in %eax register, but this has not been done for purpose.
Here there is the assembly code of the correct function:
push   %rbp
mov    %rsp,%rbp
mov    %edi,-0x14(%rbp)
mov    %esi,-0x18(%rbp)
mov    -0x14(%rbp),%edx
mov    -0x18(%rbp),%eax
add    %edx,%eax
mov    %eax,-0x4(%rbp)
mov    -0x4(%rbp),%eax   <====
pop    %rbp
retq  

As you can see, the value stored in c is properly moved in the %eax register (with the instruction marked with the <====).
In the main function (the caller), instead, the assembly is:
callq  0x400542 <add>
mov    %eax,%ecx

As you can see the caller function, after the call of the function, take the "return value" which is supposed to be in %eax register.
This means despite the fact that callee function returns actually a value or not, the caller function will takes a return value anyway.
In the example I've just shown you, the return value the caller takes is %eax register.
In conclusion if the callee function does not return a proper value the caller function (main) will take anyway what is stored in %eax register (which can be a dirty value if the callee does not return proper a value).

