473,597 Members | 2,342 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Function call and type promotion

Hi everyone -

I am not quite sure to understand what is really going on when a
function defined in one translation unit calls a function defined in a
different translation unit without knowing its prototype. Let's say for
instance :

foo.c

#include <stdio.h>

void foo(float a, int b)
{
printf("%f (0x%x) %d (0x%x)\n", a, &a, b, &b);
}

main.c

int main(void)
{
float a = 5.0;
int b = 10;

foo(a, b);

return 0;
}

According to what I have read, the argument a should be promoted to
double, but the function foo expects to get a float as its first
parameter. The result is undefined as the binary representation / space
occupied by a float and a double are completely different. I'm OK with
that.

However, if I change the foo function definition to foo(char a, char b)
the arguments should be promoted to int. As foo would expect two char
as parameters the address of a and b should be *(ebp + 4) and *(ebp +
5). This is not the case, it appears that b is located sizeof(int)
deeper in the stack than a so the function displays the correct values.

I do not understand why the behavior is different ... did I miss
something related to type promotion and the circumstances under which
it occurs?
Thank you,
Yannick

Jun 27 '08 #1
9 2364
Yannick wrote:
Hi everyone -

I am not quite sure to understand what is really going on when a
function defined in one translation unit calls a function defined in a
different translation unit without knowing its prototype. Let's say for
instance :

foo.c

#include <stdio.h>

void foo(float a, int b)
{
printf("%f (0x%x) %d (0x%x)\n", a, &a, b, &b);
}

main.c

int main(void)
{
float a = 5.0;
int b = 10;

foo(a, b);

return 0;
}

According to what I have read, the argument a should be promoted to
double, but the function foo expects to get a float as its first
parameter. The result is undefined as the binary representation / space
occupied by a float and a double are completely different. I'm OK with
that.

However, if I change the foo function definition to foo(char a, char b)
the arguments should be promoted to int. As foo would expect two char as
parameters the address of a and b should be *(ebp + 4) and *(ebp + 5).
This is not the case, it appears that b is located sizeof(int) deeper in
the stack than a so the function displays the correct values.

I do not understand why the behavior is different ... did I miss
something related to type promotion and the circumstances under which it
occurs?
The only thing you've missed is that "undefined behavior"
does not guarantee some kind of run-time error, and might even
match what somebody was hoping for.

No function with a "promotable " parameter[*] can be called
correctly without having a prototype in scope at the point of
the call, for exactly the reason you've described: If there's
no prototype, the caller promotes the arguments and this means
they do not match the actual types the function expects. What
happens next is undefined: The program might abort, it might
run but give weird results, it might make vanilla pudding ooze
from your keyboard -- or it might "work." If it works, though,
it's because of luck (good? bad?) and not because of skill.
[*] Note that "K&R-style" functions never have promotable
parameters, even if they seem to be written that way:

void foo(f)
float f;
{ ... }

takes a non-promotable `double' parameter which is then demoted
to `float'. The function is roughly equivalent to

void foo(double _fake_f) {
float f = _fake_f;
...
}

--
Er*********@sun .com
Jun 27 '08 #2
Yannick <yd******@hotma il.comwrites:
I am not quite sure to understand what is really going on when a
function defined in one translation unit calls a function defined in a
different translation unit without knowing its prototype. Let's say
for instance :

foo.c

#include <stdio.h>

void foo(float a, int b)
{
printf("%f (0x%x) %d (0x%x)\n", a, &a, b, &b);
}

main.c

int main(void)
{
float a = 5.0;
int b = 10;

foo(a, b);

return 0;
}

According to what I have read, the argument a should be promoted to
double, but the function foo expects to get a float as its first
parameter. The result is undefined as the binary representation /
space occupied by a float and a double are completely different. I'm
OK with that.
Right, except that the binary representations *may or may not* be
completely different. (I've seen systems where a double is simply a
float with extra mantissa bits added at the end; on such a system,
with the right parameter passing conventions, a certain byte ordering,
and a strong tail-wind, it just might "work". Such is the nature of
undefined behavior.)
However, if I change the foo function definition to foo(char a, char
b) the arguments should be promoted to int.
Not quite. Remember, when the compiler is handing the call to foo, it
can't see the definition of foo, so nothing in the definition of foo
can affect how the arguments are promoted.

(First, an important piece of terminology: A *parameter* is an object,
local to a function, defined and declared between the parentheses in
the prototype. An *argument* is an expression passed to a function,
appearing between the parentheses in a function call. In the
evaluation of a function call, each argument is evaluated, and the
resulting value is assigned to the corresponding parameter.)

If there's no prototype determining the parameter type (i.e., either
there's no prototype at all or the argument corresponds to the "..."
in a variadic function), float is promoted to double. If foo happens
to be expecting a double argument, you're ok; if not, you're in the
land of undefined behavior.

If you passed a char *argument*, it would be promoted to int (or
possibly to unsigned int given sufficiently exotic system
characteristics ). And you'd be ok if the function expected an int; if
it doesn't, you have UB.
As foo would expect two
char as parameters the address of a and b should be *(ebp + 4) and
*(ebp + 5). This is not the case, it appears that b is located
sizeof(int) deeper in the stack than a so the function displays the
correct values.
The *parameters* are of types float and int, respectively, because you
defined them that way. If you examine their addresses within the body
of foo, you're looking at where foo *expects* them to be. But since
you've already invoked undefined behavior, you'll *probably* get the
right addresses, but there are no guarantees.
I do not understand why the behavior is different ... did I miss
something related to type promotion and the circumstances under which
it occurs?
The point is that the behavior *isn't* different. The addresses of
the parameters shouldn't be affected by what was passed as arguments
(though their values certainly will be).
--
Keith Thompson (The_Other_Keit h) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Jun 27 '08 #3
Understood. Thanks for the help!
Yannick

Jun 27 '08 #4
Yannick wrote:
>
I am not quite sure to understand what is really going on when a
function defined in one translation unit calls a function defined
in a different translation unit without knowing its prototype.
Let's say for instance :

foo.c

#include <stdio.h>

void foo(float a, int b) {
printf("%f (0x%x) %d (0x%x)\n", a, &a, b, &b);
}
You failed to #include "foo.h", which should read:

void foo(fload a, int b);
>
main.c

int main(void) {
float a = 5.0;
int b = 10;

foo(a, b);
return 0;
}
And here you also failed to #include "foo.h". The purpose of
header files is to make characteristics of cfiles available to
other cfiles, so that they can be correctly used.

.... snip musing on undefined behavior ...

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home .att.net>
Try the download section.
** Posted from http://www.teranews.com **
Jun 27 '08 #5
On 2008-05-21 21:51:32 +0200, CBFalconer <cb********@yah oo.comsaid:
And here you also failed to #include "foo.h". The purpose of
header files is to make characteristics of cfiles available to
other cfiles, so that they can be correctly used.
[..] what is really going on when a function defined in one translation
unit calls a function defined in a different translation unit without
knowing its prototype. [...]

That's why I intently didn't use a header file for this one. I didn't
want the compiler to know about the function prototype.

I know one should always provide the compiler with functions prototypes
but that's not the point of this discussion.
Yannick

Jun 27 '08 #6
In article <20080521191350 16807-ydaffaud@hotmai lcom>
Yannick <yd******@hotma il.comwrote:
>I am not quite sure to understand what is really going on when a
function defined in one translation unit calls a function defined in a
different translation unit without knowing its prototype.
Depending on what the correct prototype is, this *can* even be
guaranteed to work. It may well work despite a lack of (Standard)
guarantee, however (as others have described else-thread).
>Let's say for instance :
[snippage; vertical space editing]
>void foo(float a, int b) { printf("%f (0x%x) %d (0x%x)\n", a, &a, b, &b); }
...
>However, if I change the foo function definition to foo(char a, char b) ...
the address of a and b should be *(ebp + 4) and *(ebp + 5).
Given that you mention "ebp", we can guess (without any absolute
guarantee of being right, especially someday in the future when
some other manufacturer uses that name for something entirely
different) that you refer to the x86 architecture, and one of
the common C compilers for that architecture.
>This is not the case, it appears that b is located sizeof(int)
deeper in the stack than a so the function displays the correct values.
Most x86 compilers, most of the time, under mostly-default conditions
-- and all these "most"s are in fact important -- "just happen"
(for a number of historical reasons) to pass "float" parameters as
"double"s on the hardware-provided stack addressed via %esp (I use
this wording to distinguish it from the FPU stack). They pass
"char" parameters only after widening them to "int", again on the
hardware-provided stack addressed via %esp.

Most of these compilers (under said conditions) do this whether or
not there is a prototype present at the site of the call, and hence
produce runtime code for foo() that assumes that the "float"
parameter was widened to "double", and that the "char" parameters
were widened to "int".

C compilers do not have to do this, and if they choose not to be
compatible with "most" x86 C compilers, they *can* use some sort
of "better" parameter-passing mechanism(s). Many C compilers can
even be told to use these "better" mechanisms even while maintaining
some compatibility, either on a function-by-function basis (using
#pragma or __attribute__ or __fastcall or __other_magic_w ord, or
perhaps on a more global basis, by something like -mregparm=N).
The "better" conventions *can* be the *default*: since Standard C
says that the C programmer, not the implementation, is in the wrong
for failing to provide proper prototypes, Standard C allows the
code to fail in the absence of proper prototypes. It is merely
the (strong) draw of compatibility (and/or convenience) that keeps
C compiler vendors using the "worse" calling conventions that make
poorly-written, non-Standard code "work" anyway.

("Better" is in quotes since "better"-ness is necessarily somewhat
an "eye of the beholder" thing. The "best" way to do something is
a lot like the "best" flavor of ice cream.)
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: gmail (figure it out) http://web.torek.net/torek/index.html
Jun 27 '08 #7
Yannick wrote:
CBFalconer <cb********@yah oo.comsaid:
>And here you also failed to #include "foo.h". The purpose of
header files is to make characteristics of cfiles available to
other cfiles, so that they can be correctly used.

[..] what is really going on when a function defined in one
translation unit calls a function defined in a different
translation unit without knowing its prototype. [...]

That's why I intently didn't use a header file for this one. I
didn't want the compiler to know about the function prototype.

I know one should always provide the compiler with functions
prototypes but that's not the point of this discussion.
Undefined behavious is, well, undefined. There is no discussion.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home .att.net>
Try the download section.
** Posted from http://www.teranews.com **
Jun 27 '08 #8
CBFalconer <cb********@yah oo.comwrites:
Yannick wrote:
>CBFalconer <cb********@yah oo.comsaid:
>>And here you also failed to #include "foo.h". The purpose of
header files is to make characteristics of cfiles available to
other cfiles, so that they can be correctly used.

[..] what is really going on when a function defined in one
translation unit calls a function defined in a different
translation unit without knowing its prototype. [...]

That's why I intently didn't use a header file for this one. I
didn't want the compiler to know about the function prototype.

I know one should always provide the compiler with functions
prototypes but that's not the point of this discussion.

Undefined behavious is, well, undefined. There is no discussion.
There was a discussion, and it was fairly interesting. Perhaps you
missed it. It included some good points about why the behavior is
undefined, as well as circumstances when it isn't undefined (when the
promoted argument types match the declared parameter types). And,
yes, it also included some harmless digressions into the actual
behavior a system is likely to exhibit in the presence of this
undefined behavior; this was relevant to the reasons that the standard
defines (or undefines) things the way it does, and to the practice of
diagnosing problems caused by undefined behavior.

--
Keith Thompson (The_Other_Keit h) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Jun 27 '08 #9
Keith Thompson <ks...@mib.orgw rote:
CBFalconer <cbfalco...@yah oo.comwrites:
Yannick wrote:
I know one should always provide the compiler
with functions prototypes but that's not the
point of this discussion.
Undefined behavious is, well, undefined. *There
is no discussion.

There was a discussion, and it was fairly interesting.
Perhaps you missed it. *It included some good points
about why the behavior is undefined, as well as
circumstances when it isn't undefined (when the
promoted argument types match the declared parameter
types). *And, yes, it also included some harmless
digressions into the actual behavior a system is
likely to exhibit in the presence of this
undefined behavior;
IMHO, the last bit is not harmless.
this was relevant to the reasons that the standard
defines (or undefines) things the way it does, and
to the practice of diagnosing problems caused by
undefined behavior.
Unfortunately, many newbies will focus exclusively
on the behaviour of undefined behaviour and in some
cases end up convincing themselves that there is no
other possible behaviour from any other system.
Worst case scenario, they will use the construct
and conclude that any system that doesn't exhibit
the desired behaviour is flawed and not worth
considering as a target platform.

In this case, the problem of unprotoyped functions
is so old and so well known, very few compilers
will not provide a facility for at least warning
of unprototyped function use.

Prevention is better than cure. C99 requires at
least a declaration, but I prefer to break
conformance and, were possible, force the compiler
to error out if I use an unprototyped function.

--
Peter
Jun 27 '08 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

31
3470
by: nikola | last post by:
Hi all, I was working with a simple function template to find the min of two values. But since I would like the two values to be different (type) I dont know what kind of value (type) it will return. I tried to write something like this: template <class Type1, class Type2, class Type3> Type3 findMin(Type1 x, Type2 y){ return (x < y) ? x : y;
8
4839
by: BigMan | last post by:
Can someone cite the rules for type promotion in C++? And, in particular, what is the type of the result of adding 2 values of type char?
6
2123
by: bluekite2000 | last post by:
I have Vector<complex<float> > V(5); V.rand(); Vector<float> V1(V); //specialized function here to return norm(V). This works fine Vector<double> V2(5); V2.rand(); Vector<float> V3(V2);//error no matching function for call to norm(double)
28
15349
by: Michael B. | last post by:
I tend to use rather descriptive names for parameters, so the old style of declaration appeals to me, as I can keep a declaration within 80 chars: void * newKlElem (frame_size,num_blocks,num_frames,frame_locator) size_t frame_size; unsigned short num_blocks; unsigned short num_frames; Kl_frame_locator *locator; { /* code goes here */
18
2086
by: Razvan | last post by:
Hi! What is the purpose of such a function ? int function(void)
3
3639
by: Beta What | last post by:
Hello, I have a question about casting a function pointer. Say I want to make a generic module (say some ADT implementation) that requires a function pointer from the 'actual/other modules' that takes arguments of type (void *) because the ADT must be able to deal with any type of data. In my actual code, I will code the function to take arguments of their real types, then when I pass this pointer through an interface function, I...
5
4646
by: kris | last post by:
Hi I have written a program which prints the hostid on a linux system. The programm uses gethostid() function call which is defined in the library file unistd.h. But my programm gets compiled without any warnings even if I didnot include any of the header files. can I know how does this happen i.e how does the compiler identifies this function gethostid. Is there any default path from where the compiler picks up the definition from.
3
3226
by: =?ISO-8859-15?Q?Jean=2DFran=E7ois?= Lemaire | last post by:
Hello, I'm having a discussion with someone who sustains that function parameters, when they are smaller than an int (say short or char) are automatically promoted to int before being passed to the caller. Despite looking in the Standard (ISO/IEC 9899:TC2), &6.5.2.2 "Function calls", I'm still unsure, because this is rather technical reading. Can someone enlighten me?
8
2385
by: vaib | last post by:
hi all , It really seems that C never ceases to amaze . All this time i've been doing C and i thought i was quite adept at it but i was wrong . So without wasting any more time , here's the confusion . I read in K&R that ANSI introduced the concept of function prototyping in C and it was missing there initially ( it borrowed the concept from C++ ) _but_ it did it make it compulsory to actually include the function declaration in the...
0
7969
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
8272
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
1
8035
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
8258
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
6688
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
5847
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
3927
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
2404
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
0
1238
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.