i want to use functions that don't return int, without declaring them

G

G Patel

Hi,

If I want to call functions that don't return int without declaring
them, will there be any harm?

I only want to assign the function(return value) to the type that it
returns, so I don't see how the return value comes to play here.


Ex


int main
{
double val;

val = func();
/* when return value comes here, its a double */

return EXIT_SUCCESS;
}


double func(void)
{
return 5;
/* this will convert 5 to a double value before passing back*/
}



I know why we have prototypes, but declarations I have no clue how they
are useful. The actual function's code will convert the return value
to the proper type, in the call location if we(programmer) know what
type the function is returning, then why do we need declarations? How
do they help?
 
C

Clark S. Cox III

Hi,

If I want to call functions that don't return int without declaring
them, will there be any harm?

I only want to assign the function(return value) to the type that it
returns, so I don't see how the return value comes to play here.

This is undefined behavior, don't do it. It may work by chance on some
platforms, but you have no guarantee of that.

Imagine, for instance, a platform that has seperate floating point
registers and integer registers that are used to return function results
Ex

int main
{
double val;

On this hypothetical platform, the compiler says "hey, this function
func must be 'int func()', so I should look for the return value in the
integer register".
It calls the function, and fetches the value in the integer register
(which is likely garbage).
It then converts that garbage int value to a double, val now has a
garbage value.
val = func();
/* when return value comes here, its a double */

return EXIT_SUCCESS;
}


double func(void)
{
return 5;
/* this will convert 5 to a double value before passing back*/

Yes it will, and on our hypothetical platform, it stuffs that value
into the floating point register, while leaving the integer register
with an undefined value.


....Or none of that could happen, such is the nature of undefined
behavior. Moral of the story: don't do it.

Also, note that your code won't even compile on a C99 compiler where
implicit int is no longer supported.
I know why we have prototypes, but declarations I have no clue how they
are useful. The actual function's code will convert the return value
to the proper type, in the call location if we(programmer) know what
type the function is returning, then why do we need declarations? How
do they help?

Because without them, your program exhibits undefined behavior. Period.
 
P

Peter Nilsson

G said:
If I want to call functions that don't return int without
declaring them, will there be any harm?
Yes.

I only want to assign the function(return value) to the type
that it returns, so I don't see how the return value comes to
play here.

int main

Not a good example.
{
double val;

val = func();
/* when return value comes here, its a double */

No. It comes as an int, becasue that's what you've implicitly
told the compiler. Note that your code is a constraint violation
under C99.
return EXIT_SUCCESS;
}

double func(void)
{
return 5;
/* this will convert 5 to a double value before passing back*/
}

I know why we have prototypes,

It seems that you don't.
but declarations I have no clue how they are useful.
The actual function's code will convert the return value
to the proper type, in the call location if we(programmer)
know what type the function is returning, then why do we need
declarations? How do they help?

Implementations typically have a precise mechanism for returning
function values. This may be a stack or specific register. The
calling function only knows the return mechanism by knowing
the (possibly implied) function signature.

More pragmatically: func may return an 8 byte value on a stack,
or it may return a value through a floating point register. Given
an implicit int return type, the calling function may assume that
func returns a 4 byte int on the stack, or a value through an
integer register.
 
D

Default User

G said:
Hi,

If I want to call functions that don't return int without declaring
them,
Why?

will there be any harm?

It's illegal and your compiler should issue a diagnostic for an
undeclared function. Only functions returning int could be undeclared
under the old standard. As of C99, that's not even legal.



Brian
 
W

Walter Roberson

:Imagine, for instance, a platform that has seperate floating point
:registers and integer registers that are used to return function results

That hypothetical is, as I recall, instantiated for MIPS R4x00/
R8000/ R1000/ R12000 ABI on SGI's IRIX, and probably other
systems as well.

The IRIX mips3/mips4 ABI also has rules for returning small structures
in registers. Which is, incidently, a point that the OP missed: that
structures can be returned as results, and if you don't declare that as
your return type, you are fairly unlikely to just happen to get
everything squished into the right registers.
 
G

G Patel

Clark said:
This is undefined behavior, don't do it. It may work by chance on some
platforms, but you have no guarantee of that.

Imagine, for instance, a platform that has seperate floating point
registers and integer registers that are used to return function
results

Ohh, makes sense now. But now how do non-prototyped function calls
handle parameter passing. Someone communication is made between the
call and the actual function code. Parameter passing works without a
prototype, why can't a return variable work without a declaration?

On this hypothetical platform, the compiler says "hey, this function
func must be 'int func()', so I should look for the return value in the
integer register".
It calls the function, and fetches the value in the integer register
(which is likely garbage).
It then converts that garbage int value to a double, val now has a
garbage value.


Yes it will, and on our hypothetical platform, it stuffs that value
into the floating point register, while leaving the integer register
with an undefined value.

Yes, but parameter passing without declarations would be a "bigger"
case of what you are saying, but C compilers handle it properly.


Ex.

double func(); /* parameters not specified */

int main(void)
{
double i;

i = func(4, 6);

return EXIT_SUCCESS;
}

/* in another file */
double func(short x, short y)
{
return (double)x + y;
}


/* This works fine, even though main doesn't know anything about func's
parameters. It still manages to call func. How come return values are
more tricky? */
 
L

Luke Wu

G said:
Clark said:
results

Ohh, makes sense now. But now how do non-prototyped function calls
handle parameter passing. Someone communication is made between the
call and the actual function code. Parameter passing works without a
prototype, why can't a return variable work without a declaration?



Yes, but parameter passing without declarations would be a "bigger"
case of what you are saying, but C compilers handle it properly.


Ex.

double func(); /* parameters not specified */

int main(void)
{
double i;

i = func(4, 6);

return EXIT_SUCCESS;
}

/* in another file */
double func(short x, short y)
{
return (double)x + y;
}


/* This works fine, even though main doesn't know anything about func's
parameters. It still manages to call func. How come return values are
more tricky? */


Because parameters/arguments are passed on the stack and the method is
well defined. Return values are less well defined (can be passed back
many different ways, depends on the type).
 
K

Keith Thompson

G Patel said:
Ohh, makes sense now. But now how do non-prototyped function calls
handle parameter passing. Someone communication is made between the
call and the actual function code. Parameter passing works without a
prototype, why can't a return variable work without a declaration?

No, parameter passing doesn't necessarily work correctly without a
prototype. With no prototype in scope, the compiler will make certain
assumptions about the types of the parameters, based on the types of
the arguments that you pass. For example, an argument of type short
will be promoted to int, and an argument of type float will be
promoted to double. If this isn't what the called function is
expecting, you get undefined behavior -- which includes working just
as you expect.

[...]
Yes, but parameter passing without declarations would be a "bigger"
case of what you are saying, but C compilers handle it properly.


Ex.

double func(); /* parameters not specified */

int main(void)
{
double i;

i = func(4, 6);

return EXIT_SUCCESS;
}

/* in another file */
double func(short x, short y)
{
return (double)x + y;
}


/* This works fine, even though main doesn't know anything about func's
parameters. It still manages to call func. How come return values are
more tricky? */

The integer constants 4 and 6 are of type int, and are passed that
way. The function expects to receive arguments of type short. If int
and short arguments happen to be passed by the same mechanism (e.g.,
if they're both passed in registers), your program may happen to work.
(It will likely continue to work until it breaks at the most
inconvenient possible moment.)

Incidentally, the program above is not what you actually compiled.
The macro EXIT_SUCCESS is defined in <stdlib.h>; if you're using it,
you must have a "#include <stdlib.h>" that you didn't show us. I'm
guessing your actual program also had a printf() call that displayed
the result of your function call (and, I hope, a "#include <stdio.h"),
leading you to believe that the compiler handled it "properly".

In this particular case it was easy enough to figure out what you
meant, but in general it's a good idea to post a complete compilable
program (preferably a small one); otherwise we'll get hung up on the
differences between what you posted and what you actually compiled.
 
E

E. Robert Tisdale

G said:
If I want to call functions that don't return int
without declaring them, will there be any harm?

Why didn't you try it?
> cat main.c
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
// When return value comes here, its a double.
double val = func();
fprintf(stderr, "val = %f\n", val);
return EXIT_SUCCESS;
}

double func(void) {
// This will convert 5 to a double value before passing back.
return 5;
}
> gcc -Wall -std=c99 -pedantic -o main main.c
main.c: In function `main':
main.c:6: warning: implicit declaration of function `func'
main.c: At top level:
main.c:11: error: conflicting types for 'func'
main.c:6: error: previous implicit declaration of 'func' \
was here

If you define func(void) as an external function:
> cat func.c
double func(void) {
// This will convert 5 to a double value before passing back.
return 5;
}
> cat main.c
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
// When return value comes here, its a double.
double val = func();
fprintf(stderr, "val = %f\n", val);
return EXIT_SUCCESS;
}
> gcc -Wall -std=c99 -pedantic -o main main.c func.c
main.c: In function `main':
main.c:6: warning: implicit declaration of function `func'
val = 16.000000

you get garbage because, in this case, func(void) returns a double
on top of the floating-point stack
but function main is looking for an int in register eax
which it converts to a double.
I only want to assign the function(return value) to the type that it
returns, so I don't see how the return value comes to play here.

The calling function needs to know the type of the return value
so that it knows where to look for it
and so that it can convert it to the type of val if necessary.

[snip]
I know why we have prototypes
but I have no clue how declarations are useful.
The actual function's code will convert the return value
to the proper type, in the call location
if we(programmer) know what type the function is returning,
then why do we need declarations?
How do they help?

The calling function needs to know the type of each argument
and the type of the return value
so that it can perform any necessary conversions.
Suppose, for example, that you have declared

double f(double);

and you want to invoke

double val = f(13);

Without the declaration,
the calling function will try to pass an int to f(double)
and because f(double) expects a double, it will get the wrong value.
But, with the declaration,
the calling function will convert the int 13 to a double 13.0
and pass it to f(double)
so that f(double) gets the type that it expects.
 
C

Clark S. Cox III

:Imagine, for instance, a platform that has seperate floating point
:registers and integer registers that are used to return function
results

That hypothetical is, as I recall, instantiated for MIPS R4x00/
R8000/ R1000/ R12000 ABI on SGI's IRIX, and probably other
systems as well.

I know, PowerPC is the one I had in mind, but I wanted to keep from
using specific implementations as an example.
 
C

Clark S. Cox III

Yes, but parameter passing without declarations would be a "bigger"
case of what you are saying, but C compilers handle it properly.


Ex.

double func(); /* parameters not specified */

int main(void)
{
double i;

i = func(4, 6);

return EXIT_SUCCESS;
}

/* in another file */
double func(short x, short y)
{
return (double)x + y;
}


/* This works fine, even though main doesn't know anything about func's
parameters. It still manages to call func. How come return values are
more tricky? */

They aren't more tricky, it's the exact same issue. If it works for
you, you just got lucky. It's entirely possible that int and short just
happen to be passed in the same way on your platform, and everything
just happens to line up, but, again, you can't rely on that.
 
K

Keith Thompson

Luke Wu said:
G Patel wrote: [...]
/* This works fine, even though main doesn't know anything about
func's parameters. It still manages to call func. How come return
values are more tricky? */


Because parameters/arguments are passed on the stack and the method is
well defined. Return values are less well defined (can be passed back
many different ways, depends on the type).

No, arguments have the same problems as return values. Arguments can
be passed on the stack, in registers, or by carrier pigeon. The code
invokes undefined behavior, as I explained elsethread.
 
L

Luke Wu

Keith said:
Luke Wu said:
G Patel wrote: [...]
/* This works fine, even though main doesn't know anything about
func's parameters. It still manages to call func. How come return
values are more tricky? */


Because parameters/arguments are passed on the stack and the method is
well defined. Return values are less well defined (can be passed back
many different ways, depends on the type).

No, arguments have the same problems as return values. Arguments can
be passed on the stack, in registers, or by carrier pigeon. The code
invokes undefined behavior, as I explained elsethread.

The code does not invoke undefined behavior preC99, because the
arguments being passed will be promoted to in, so will the parameters.

Arguments don't have the same problems if they are equal to and below
the rank of int, and the corresponding parameters in the function
definitions are also. Both arguments and paremetesr will be promoted
to int explicity, and everything is fine.
 
K

Kiru Sengal

Luke said:
Keith said:
Luke Wu said:
G Patel wrote:
[...]

/* This works fine, even though main doesn't know anything about
func's parameters. It still manages to call func. How come
return
values are more tricky? */


Because parameters/arguments are passed on the stack and the method
is
well defined. Return values are less well defined (can be passed
back
many different ways, depends on the type).

No, arguments have the same problems as return values. Arguments can
be passed on the stack, in registers, or by carrier pigeon. The code
invokes undefined behavior, as I explained elsethread.


The code does not invoke undefined behavior preC99, because the
arguments being passed will be promoted to in, so will the parameters.
>

Yes it does invoke undefined behaviour. New-style function definitions
don't have this "parameter promotion" feature you're talking about. If
the poster had an old-style function with short parameters, then yes
they will be promoted upto int(or unsigned int) just like the passed in
arguments. But all this is irrelevant, because any new code should have
prototyped declarations and new-style prototyped function definitions
(of course you can use the later as the former in many cases).
Arguments don't have the same problems if they are equal to and below
the rank of int, and the corresponding parameters in the function
definitions are also. Both arguments and paremetesr will be promoted
to int explicity, and everything is fine.

Since the calling site was not in the scope of any explicit
declarations, yes the arguments will be promoted, but the corresponding
parameters in the function definition (shorts) will not be promoted
(that's only with old style definitions). The call is sending INTs or
UNSIGNED INTs (based on value preserving rules and promotion) but the
function is expecting SHORTS (not expecting short promoted types). This
invokes undefined behaviour like Keith said.


Under C99, many things I've explained above as being "okay" are also
disallowed.
 
K

Keith Thompson

Luke Wu said:
Keith said:
Luke Wu said:
G Patel wrote: [...]
/* This works fine, even though main doesn't know anything about
func's parameters. It still manages to call func. How come
return values are more tricky? */


Because parameters/arguments are passed on the stack and the
method is well defined. Return values are less well defined (can
be passed back many different ways, depends on the type).

No, arguments have the same problems as return values. Arguments can
be passed on the stack, in registers, or by carrier pigeon. The code
invokes undefined behavior, as I explained elsethread.

The code does not invoke undefined behavior preC99, because the
arguments being passed will be promoted to in, so will the parameters.

Arguments don't have the same problems if they are equal to and below
the rank of int, and the corresponding parameters in the function
definitions are also. Both arguments and paremetesr will be promoted
to int explicity, and everything is fine.

I don't think that's correct.

Here's the code in question:

double func(); /* parameters not specified */

int main(void)
{
double i;

i = func(4, 6);

return EXIT_SUCCESS;
}

/* in another file */
double func(short x, short y)
{
return (double)x + y;
}

In the absence of a prototype, the call func(6, 4) passes two int
arguments to func, which is expecting two short arguments. If the
first line had been replaced with:

double func(short x, short y);

the arguments would have been implicitly converted from int to short
before being passed to the function. It's common, but not required,
for int and short arguments to be passed the same way.

C99 6.5.2.2 says:

If the expression that denotes the called function has a type that
does not include a prototype, the integer promotions are performed
on each argument, and arguments that have type float are promoted
to double.
[snip]
If the function is defined with a type that does not include a
prototype, and the types of the arguments after promotion are not
compatible with those of the parameters after promotion, the
behavior is undefined, except for the following cases:
[snip]

I believe C90 has the same rules.

Even if the arguments were objects of type short, their values would
be promoted to int, invoking undefined behavior. The solution is
simple: always use prototypes.

Pre-ANSI C didn't have prototypes, and it didn't allow parameters of
type short (you could declare parameters to be of type short, but they
were really int), so this issue didn't arise.
 
P

Peter Nilsson

Luke said:
The code [snipped] does not invoke undefined behavior preC99,
because the arguments being passed will be promoted to in, so
will the parameters.

This is loose wording which C99 has corrected.

Given...

void baa(void)
{
short y = 42;
foo(y);
}

void foo(short x)
{
printf("%d\n", (int) sizeof(x));
}

....then x is a parameter, y is an argument. Since foo() is
unprototyped in the call from baa(), y will be promoted to
int. However, x will always have the type short, because that's
how it's declared. [Of course, x is potentially subject to
integral promotion within expressions which might use it.]
 
M

Mark McIntyre

The code does not invoke undefined behavior preC99, because the
arguments being passed will be promoted to in, so will the parameters.

Arguments don't have the same problems if they are equal to and below
the rank of int, and the corresponding parameters in the function
definitions are also. Both arguments and paremetesr will be promoted
to int explicity, and everything is fine.

I once ported a programme that made this kind of assumption. It broke
horribly on a different arch, because on the original platform, short and
int were the same width, whereas on the new platform, it was long and int.
Thus the caller pushed two longs onto the stack (or possibly into a
register, or into one carrier pigeon's pouch), and the callee read two
shorts back off. *bang*
On another platform I worked with, ISTR that shorts got pushed in
registers, and longs on the stack, so the callee would have been looking in
completely the wrong place.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top