0

I was testing a code and I don't understand why it prints "The higher value is 0". This is the main function:

int main() {
    double a,b,c;
    a=576;
    b=955;
    c=higher(a,b);
    printf("The higher value is %g\n", c);
    return 0;
}

and in another .c I have this function:

double higher(double a, double b){
    if (a>b)
        return a;
    return b;
}

NOTE: if I put higher() in main.c it works correctly, but in that way it tells me the higher value is 0. It also works if I cast the return in higer() like this:

return (int)b;

Is there a difference if a function that returns a double is in the same .c as main() or in a different one?

1 Answers1

5

Compile with a C99 or C11 compiler and read the warnings. You are using the function without prototype.

Without a prototype, pre-C99 assumes a function to return int by default. C99 and later, require a prototype.

Even without additional warnings enabled:

$ cat test.c
int main()
{
    int i = f();
    return 0;
}

int f(void)
{
   return 1;
}

$ gcc -std=c11 test.c
test.c: In function ‘main’:
test.c:13:2: warning: implicit declaration of function ‘f’ [-Wimplicit-function-declaration]
  int i = f();

Note that gcc will not warn if compiling -std=c90, but will if enabling warnings -Wall.

So, as higher() is expected to return an int, the value is converted to double by the assignment (the type of c is not changed).

And now for the funny part: undefined behaviour (UB, memorize this phrase!) due to different signature for call and implementation of the function.

What might happen is according to procedure call standard (PCS) and the application binary interface (ABI) - check Wikipedia. Briefly: higher itself returns a double. That is likely passed in a floating point CPU register to the caller. The caller, OTOH, expects the return value (due to the missing prototype) in an integer CPU register (which happens to hold 0 by chance).

So, as they apparently have misscommunication, you get the wrong result. Note that this is a bit speculatively and depends on the PCS/ABI. All to remember is this is UB, so anything can happen, even demons flying out of your nose.

Why use prototypes:

Well, you allready noticed, the compiler has no idea, if you call a function correctly. Even worse, it does not know, which argument types are used and which result type is returned. This is particlularily a problem, as C automatically converts some types (which you did encounter here).

As classical K&R (pre-standard) C did not have prototypes, all arguments to unknown functions were assumed int/double for scalar arguments on a call. The result defaults to int. (Long time ago, I might be missing some parts; I started some coding with K&R, messed up types (exactly your problem here, but without a clean solution), etc., threw it in a corner and happily programmed in Modula-2 until some years later I tried ANSI-C).

If compiling code now, you should at least conform to (and compile for) C99, better use the current standard (C11).

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
  • Even some non-C99 compilers will warn about that fact if you turn all warnings on. A "dumb" compiler might warn about the fact that `higher()` wasn't declared while a smarter compiler could also warn about the `printf()` format `%g` expecting a value of type `double` where an `int` is passed (due to the fact that `higher()` implicitly returns an `int`). –  Jul 02 '15 at 23:39
  • Yes, I added the prototype and it worked correctly. But if it assumes that higher() returns an int why it doesn't cast higher(a,b) to a double instead of assigning 0 to c? – Pedro Balaguer Jul 02 '15 at 23:40
  • 1
    @ChronoKitsune: i did not state different. You should always at least have `-Wall`, I recomend `-Wextra -Wconversions`, too. Note that the warning-option which actually trigger the waring is given in the message by gcc. – too honest for this site Jul 02 '15 at 23:45
  • @Olaf It was an extra bit of information before your edit. I apologize if my phrasing seemed to contradict your post. –  Jul 02 '15 at 23:49
  • @PedroBalaguer With functions that require a variable number of arguments in C, there is no way to know how many arguments there are or even what the type of each argument is without some form of indicator. Simply put, `printf()` requires format specifications such as `%g` because it can't determine whether an `int` or a `double` or a `char *` was passed to it. –  Jul 02 '15 at 23:54
  • 2
    Correction: a pre-C99 compiler assumes a called function returns `int` if there's no visible *declaration*. That declaration needn't be a prototype. (A prototype is a function declaration that specifies the types of the parameters.) Still, there's no good reason to use non-prototype declarations. – Keith Thompson Jul 02 '15 at 23:59
  • @ChronoKitsune: Sorry, I did not want to offend; it's just quite hot here, so my temper is a little bit - challenged;-) – too honest for this site Jul 03 '15 at 00:02