passing float parameter

I am surprised by GCC (this is ver. 4.2.4, Ubuntu 32 bit Intel) when a function declares a float parameter and it's prototype is missing, the parameters are messed up.

Please see my code below:

~/test$ cat x1.c
#include <stdio.h>
#include <stdlib.h>
set_p(int p1, float p2, int p3, int p4)
{
        printf("p1=%i\n", p1);
        printf("p2=%.4f\n", p2);
        printf("p3=%i\n", p3);
        printf("p4=%i\n", p4);
        return(0);
}
main(int argc, char *argv[])
{
  float x1 = (argc == 2) ? atof(argv[1]) : 1.2345;
        set_p(1, x1, 2, 3);
        set_P(1, x1, 2, 3);
        return(0);
}
~/test$ cat x2.c
#include <stdio.h>
set_P(int p1, float p2, int p3, int p4)
{
        printf("p1=%i\n", p1);
        printf("p2=%.4f\n", p2);
        printf("p3=%i\n", p3);
        printf("p4=%i\n", p4);
        return(0);
}
 
I make it like this:
cc -c x1.c
cc -c x2.c
cc x1.o x2.o

The output:

~/test$ ./a.out
p1=1
p2=1.2345
p3=2
p4=3
p1=1
p2=0.0000
p3=1072939139
p4=2

As you see, the set_P function somehow messed up parameters.

***Note: the code does work on SCO with stock compiler on similar h/w.

If I add the function prototype to x1.c

int set_P(int , float , int , int );

then everything works correctly, no surprises.

My understanding is that compiler allocates longs for longs/integers/shorts and doubles for float/double when passing parametrs by value. Then if receiver function expects a float, it will get first 32 bit, which is OK on Intel. Apparently my understanding is not correct.

The code in many places lacks function prototypes, I wander if I can somehow influence compiler to pass float/double parameters and not mess up. Any ideas?

The problem is mainly due to you having two units of compilation and no prototypes.

If you call a function and you do not have a prototype, the compiler will infer a prototype from the parameters you pass to the function. If the function is in another compilation unit (as is your case), there's no way to get a compilation error if the type is wrong, since without a prototype there's no way to check. If the compiler gets the type wrong, you get unexpected behavior. That is what is happening in this case.

You need to create function prototypes to avoid this type of error.

Thanks, that is my sticking point - can I avoid creating those prototypes?
Also, why would this code run OK when compiled by the old SCO compiler?

You really, really, really shouldn't do that. I've spent days tracking down bugs from people who didn't bother because it "just worked" on their system.

Just make a simple .h file:

#ifndef __MYHFILE__
#define __MYHFILE__

extern int set_p(int p1, float p2, int p3, int p4);

#endif/*__MYFILE_H__*/

...and include it in both c files.

Undefined behavior doesn't have to be the same everywhere. That's part of the problem.

You have not told us which exact "old SCO compiler" you are talking about. There are many old SCO compilers out there.

Actually, the behavior of C in the absence of prototypes is quite well-defined. It's called "argument promotion".

The behavior of the compiler is, the behavior of the program isn't. Assumptions made about variable sizes and low-level passing semantic and such forth may no longer hold; assuming "int" isn't always a sensible default. Undeclared externals using pointers, especially, may cause segmentation faults.

Ok, no escaping the building of func prototypes then.

To fpmurphy: CSO compiler Release 5.2.0A