Arithmetic on function address

P

pete b

Given this code:
void f(void){}
int main(void){return (int)f+5;}

Is there anything wrong with this in terms of the standards? Is this
legal C code? One compiler I'm working with compiles this quietly, even
with the most stringent and pedantic ANSI and warning levels, but
generates code that only loads the address of "f" and fails to make the
addition before returning a value from "main".

GCC "does the right thing".

Is there something I'm missing?
 
D

Dann Corbit

Given this code:
void f(void){}
int main(void){return (int)f+5;}

Is there anything wrong with this in terms of the standards? Is this
legal C code? One compiler I'm working with compiles this quietly, even
with the most stringent and pedantic ANSI and warning levels, but
generates code that only loads the address of "f" and fails to make the
addition before returning a value from "main".

GCC "does the right thing".

Is there something I'm missing?

Interesting question. I do get warnings:

dcorbit@DCORBIT2008 /c/tmp
$ gcc -W -Wall -ansi -pedantic bozuk.c
bozuk.c: In function 'main':
bozuk.c:3:12: warning: cast from pointer to integer of different size

dcorbit@DCORBIT2008 /c/tmp
$ cat bozuk.c
void f(void) {}
int main(void) {
return (int)f+5;
}

c:\tmp>cl /W4 /Ox bozuk.c
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.30729.01 for x64
Copyright (C) Microsoft Corporation. All rights reserved.

bozuk.c
bozuk.c(3) : warning C4305: 'type cast' : truncation from 'void (__cdecl
*)(void)' to 'int'
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.

/out:bozuk.exe
bozuk.obj

Now, the type of f is a function pointer. Since f+5 is equivalent to f
[5] and since f is not an array of 6 or more function pointers (but
rather, a single pointer), I guess that the behavior is undefined.
 
K

Keith Thompson

pete b said:
Given this code:
void f(void){}
int main(void){return (int)f+5;}

Is there anything wrong with this in terms of the standards? Is this
legal C code? One compiler I'm working with compiles this quietly, even
with the most stringent and pedantic ANSI and warning levels, but
generates code that only loads the address of "f" and fails to make the
addition before returning a value from "main".

GCC "does the right thing".

Is there something I'm missing?

The code violates no constraints as far as I can tell, so the
implementation is not required to diagnose any problems.

Conversion from a pointer-to-function type to an integral type is
not forbidden, but nothing in the standard defines its behavior,
so the behavior is undefined by omission.

The only values the standard says you can return from main are 0,
EXIT_SUCCESS (likely to be 0), and EXIT_FAILURE. Other values are
permitted, but might communicate strange and/or meaningless things
to the environment.

This looks like a theoretical question (which is fine, of course),
but if it isn't, what are you trying to do?
 
F

Fred

Given this code:
void f(void){}
int main(void){return (int)f+5;}
Is there anything wrong with this in terms of the standards?  Is this
legal C code?  One compiler I'm working with compiles this quietly, even
with the most stringent and pedantic ANSI and warning levels, but
generates code that only loads the address of "f" and fails to make the
addition before returning a value from "main".
GCC "does the right thing".
Is there something I'm missing?

Interesting question.  I do get warnings:

dcorbit@DCORBIT2008 /c/tmp
$ gcc -W -Wall -ansi -pedantic bozuk.c
bozuk.c: In function 'main':
bozuk.c:3:12: warning: cast from pointer to integer of different size

dcorbit@DCORBIT2008 /c/tmp
$ cat bozuk.c
void f(void) {}
int main(void) {
    return (int)f+5;

}

c:\tmp>cl /W4 /Ox bozuk.c
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.30729.01 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

bozuk.c
bozuk.c(3) : warning C4305: 'type cast' : truncation from 'void (__cdecl
*)(void)' to 'int'
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:bozuk.exe
bozuk.obj

Now, the type of f is a function pointer.  Since f+5 is equivalent to f
[5] and since f is not an array of 6 or more function pointers (but
rather, a single pointer), I guess that the behavior is undefined.

The warnings are due to casting f to an int. It is likely that on your
system a function pointer is 64 bits, and an int is 32 bits.
 
K

Keith Thompson

Dann Corbit said:
Given this code:
void f(void){}
int main(void){return (int)f+5;}
[snip]
Interesting question. I do get warnings: [...]
Now, the type of f is a function pointer. Since f+5 is equivalent to f
[5] and since f is not an array of 6 or more function pointers (but
rather, a single pointer), I guess that the behavior is undefined.

No, f+5 would be equivalent to &(f[5]) (but only if f were an object
pointer). But f is converted to int before 5 is added to the result;
there's no pointer arithmetic happening.

For f+5 to make sense, f would have to point to an element of an array
of functions. There's no such thing in C.
 
B

Ben Bacarisse

The code violates no constraints as far as I can tell, so the
implementation is not required to diagnose any problems.

Conversion from a pointer-to-function type to an integral type is
not forbidden, but nothing in the standard defines its behavior,
so the behavior is undefined by omission.

Surely it is implementation defined (6.3.2.3 p6)?

<snip>
 
K

Keith Thompson

Ben Bacarisse said:
Surely it is implementation defined (6.3.2.3 p6)?

<snip>

Whoops, you're right. Conversion of a pointer-to-function to a
pointer-to-object, or vice versa, is undefined (except for null
pointers); that's what I was thinking of. Sorry about the
misinformation.

I just noticed something odd. C99 6.4.2.3p6 says:

Any pointer type may be converted to an integer type. Except as
previously specified, the result is implementation-defined. If
the result cannot be represented in the integer type, the
behavior is undefined. The result need not be in the range of
values of any integer type.

I see no "previously specified" behavior for pointer-to-integer
conversions. (Conversion of a constant 0 to a pointer yields a null
pointer, but nothing is guaranteed for the reverse conversion;
(int)(void*)0) needn't yield 0.)
 
P

pete b

Keith said:
This looks like a theoretical question (which is fine, of course), but
if it isn't, what are you trying to do?

I am not trying to accomplish anything besides running the GCC testsuite
on some other compiler that I am trying to analyze. This is part of the
testsuite and passes with GCC, no problem... I was just wondering if this
is a bug in the compiler that I am trying to run on this code? I want to
be sure that this should generate code correctly before I cry "bug".
That is, that it should generate code to add the constant after the
pointer is converted to an integer.

Thanks for any help.
 
B

Ben Bacarisse

Keith Thompson said:
I just noticed something odd. C99 6.4.2.3p6 says:

Does C99 have different numbering from n1256.pdf or is that a typo?
Any pointer type may be converted to an integer type. Except as
previously specified, the result is implementation-defined. If
the result cannot be represented in the integer type, the
behavior is undefined. The result need not be in the range of
values of any integer type.

I see no "previously specified" behavior for pointer-to-integer
conversions. (Conversion of a constant 0 to a pointer yields a null
pointer, but nothing is guaranteed for the reverse conversion;
(int)(void*)0) needn't yield 0.)

I was puzzled by that, too. My guess was that maybe there had been,
once, a requirement that a null pointer would convert back to (int)
zero.
 
B

Ben Bacarisse

pete b said:
I am not trying to accomplish anything besides running the GCC testsuite
on some other compiler that I am trying to analyze. This is part of the
testsuite and passes with GCC, no problem... I was just wondering if this
is a bug in the compiler that I am trying to run on this code? I want to
be sure that this should generate code correctly before I cry "bug".
That is, that it should generate code to add the constant after the
pointer is converted to an integer.

You can't tell from the code if there is a bug! The fact that (int)f
(when f is the name of a function) is implementation defined means that
a conforming C implementation must document what happens. You have a
bug in the implementation if either (a) the documents don't say what
will happen or (b) the compiler does not do what the documents say.
 
K

Keith Thompson

Ben Bacarisse said:
Does C99 have different numbering from n1256.pdf or is that a typo?

Typo; the numbering is the same (and I was using n1256 anyway).
I was puzzled by that, too. My guess was that maybe there had been,
once, a requirement that a null pointer would convert back to (int)
zero.

Jun Woong posted the answer in comp.std.c:

6.3.1.2 Boolean type [which precedes 6.3.2.3]

When any scalar value is converted to _Bool, the result is 0 if
the value compares equal to 0; otherwise, the result is 1.
 
P

pete b

Ben said:
You can't tell from the code if there is a bug! The fact that (int)f
(when f is the name of a function) is implementation defined means that
a conforming C implementation must document what happens. You have a
bug in the implementation if either (a) the documents don't say what
will happen or (b) the compiler does not do what the documents say.

Ok... I understand this... that's just it... the code that this compiler
generates does the conversion and actually returns the address of the
function as an integer. It just silently discards the addition. If
this is implementation behavior, shouldn't it (as you say below about
compilers needing to give more than just what the standard says) either
complain about an invalid value or do the addition also, since it
accepts the conversion in the first place? The function address is
converted to an integer since this is what is returned, so the integer
value should be available for more computation if needed.

Any other behavior, such as what is happening here, is a bug in the
compiler IMHO.
 
E

Eric Sosman

Ok... I understand this... that's just it... the code that this compiler
generates does the conversion and actually returns the address of the
function as an integer. It just silently discards the addition. If
this is implementation behavior, shouldn't it (as you say below about
compilers needing to give more than just what the standard says) either
complain about an invalid value or do the addition also, since it
accepts the conversion in the first place? The function address is
converted to an integer since this is what is returned, so the integer
value should be available for more computation if needed.

Any other behavior, such as what is happening here, is a bug in the
compiler IMHO.

Looking up-thread, the code in question is:

void f(void){}
int main(void){return (int)f+5;}

What evidence do you have that the compiler "silently discards
the addition?" (Note that `return 42+5;' might not involve an
ADD instruction, either.)
 
B

Ben Bacarisse

pete b said:
Ok... I understand this...

I think you missed the point. What does the documentation say?
that's just it... the code that this compiler
generates does the conversion and actually returns the address of the
function as an integer. It just silently discards the addition. If
this is implementation behavior, shouldn't it (as you say below about
compilers needing to give more than just what the standard says) either
complain about an invalid value or do the addition also, since it
accepts the conversion in the first place?

Let me take a silly example: if the Bozo C compiler's document on
conformance states: "Converting a function pointer to an int gives a
numerical value 5 less than the bit pattern used to represent the
pointer would suggest" then it is permitted to optimise the +5 away.

All I was doing was saying that you can't look to the C standard for an
answer, you must look to the compiler's documentation.
The function address is
converted to an integer since this is what is returned, so the integer
value should be available for more computation if needed.

Any other behavior, such as what is happening here, is a bug in the
compiler IMHO.

There may well be a bug, but you can't tell by logic alone. What does
the documentation say about this conversion? If it says the conversion
is well-defined (for example, the result is always in range) then you
can say that what you observe is a bug. Double check your facts and
report it. I say double check because it is surprisingly easy to get
odd results from code that is undefined for some other reason that you
have missed. Today's compilers are subtle beasts.
 
S

Stargazer

Ok... I understand this... that's just it... the code that this compiler
generates does the conversion and actually returns the address of the
function as an integer.  It just silently discards the addition.  

Hardly. Address of "f" is constant, so when compiler converts that to
an integer, it becomes an integer constant. When you return sum of two
integers, the compiler is not required to generate extraneous code to
add them in run-time; it may just compute the result and return it
hardcoded. Consider as in (simplified) Eric's example if you had "int
main(void) {return 42+5;} the compiler may just generate appropriate
code to return 47, and you will see no traces of 42 or 5 in assembly
code.

This is usually done in optimizer stage; I believe if you turn on a
certain optimization level in GCC, it will also "discard" the
addition.
If
this  is implementation behavior, shouldn't it (as you say below about
compilers needing to give more than just what the standard says) either
complain about an invalid value or do the addition also, since it
accepts the conversion in the first place? The function address is
converted to an integer since this is what is returned, so the integer
value should be available for more computation if needed.

Returned is that integer +5. If (int)f indeed was used in computation,
it would appear. The compiler happens to know all the "if needed" in
compile-time and may through everything that is not needed from
generated code.

Daniel
 
B

Barry Schwarz

Hardly. Address of "f" is constant, so when compiler converts that to
an integer, it becomes an integer constant. When you return sum of two

That may be true on your system, but on the two systems I commonly
use, it is fairly common for programs to execute at different
locations and the compiler will have no idea where any particular
instance of the program will reside. Neither will the compiler know
if a function that happens to reside between main and f has been
changed so that f is now at a different displacement from main. Trying
to compute (int)f+5 at compile time is not an option.
 
S

Stargazer

That may be true on your system, but on the two systems I commonly
use, it is fairly common for programs to execute at different
locations and the compiler will have no idea where any particular
instance of the program will reside.

This is resolved during linking ("static" linking) or program loading
("dynamic" linking). Following your logic, we can never use addresses
of external functions or data as constants because they are not known
at compile time.
Neither will the compiler know
if a function that happens to reside between main and f has been
changed so that f is now at a different displacement from main.

"f" is an address constant as defined in 6.6.9 of the C Standard. So,
as clause "6.6.2" defines:

"A constant expression can be evaluated during translation rather than
runtime, and
accordingly may be used in any place that a constant may be."

Daniel
 
B

Barry Schwarz

This is resolved during linking ("static" linking) or program loading
("dynamic" linking). Following your logic, we can never use addresses
of external functions or data as constants because they are not known
at compile time.


"f" is an address constant as defined in 6.6.9 of the C Standard. So,
as clause "6.6.2" defines:

"A constant expression can be evaluated during translation rather than
runtime, and
accordingly may be used in any place that a constant may be."

Except that 6.6-8 says

"Cast operators in an arithmetic constant expression shall only
convert arithmetic types to arithmetic types, except as part of an
operand to a sizeof operator whose result is an integer constant."

which seems to preclude treating (int)f+5 as a constant since the cast
converts a pointer type.
 
K

Keith Thompson

Barry Schwarz said:
On Mon, 31 May 2010 02:07:37 -0700 (PDT), Stargazer


Except that 6.6-8 says

"Cast operators in an arithmetic constant expression shall only
convert arithmetic types to arithmetic types, except as part of an
operand to a sizeof operator whose result is an integer constant."

which seems to preclude treating (int)f+5 as a constant since the cast
converts a pointer type.

So ``(int)f+5'' isn't a constant expression, but since ``f'' is an
address constant it's likely that ``(int)f+5'' is still capable
of being evaluated at compile or link time.
 
S

Stargazer

Except that 6.6-8 says

"Cast operators in an arithmetic constant expression shall only
convert arithmetic types to arithmetic types, except as part of an
operand to a sizeof operator whose result is an integer constant."

which seems to preclude treating (int)f+5 as a constant since the cast
converts a pointer type.

The reason that pointer-to-integer conversions are not included in the
specification is, I think, that it may cause undefined behavior if
pointer value cannot be represented by the target integer type
(6.3.2.3.6). Not due to your reason - that "f" may change between
compile-time and run-time. It cannot since it's constant.

Note that conversion of integer constant to a pointer is explicitly
defined to result in an address constant - and according to 6.3.2.3.5
it may not cause undefined behavior.

Daniel
 

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,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top