468,463 Members | 2,034 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 468,463 developers. It's quick & easy.

printing the address of a function


i am trying to print the address of a function without getting a
compiler warning (i am compiling with gcc with alot of flags).

if i try this:

printf("%p", f);

i get:

warning: format %p expects type 'void *; but argument 2 has type 'void
(*) void)'

if i try this:

printf("%p", (void *)f);

i get:

ISO C forbids conversion of function pointer to object pointer type

if i try this:

printf("%x", ((unsigned int)f));

it compiles cleanly, but this solution bugs me because it assumes that
an unsigned int is the same length as a pointer.

any suggestions as to the "correct" way to print the address of a function?

thanks,
rCs

Sep 25 '06
57 4895
Jun Woong wrote:
Robert Gamble wrote:
Actually, a program can be strictly conforming while containing
undefined behavior as long as any output does not depend on the result
of the undefined behavior.
If a program contains UB to be triggered, then its output or even
whether or not you can get the intended output always depend on that
UB, which makes the program not s.c.
If you're saying what I think you are saying, it's not correct.
The following function could be used in a strictly conforming
program:
void foo( void ) {
if ( 0 )
1 / 0;
}
Division by zero would result in undefined behavior, but we
can easily see that that operation could never be executed
in this example. (There are more practical examples of this,
such as where something is done one way if a pointer is null,
but if the pointer is non-null, it is used to access the
pointed-to object.)
Sep 29 '06 #51
Douglas A. Gwyn wrote:
>
Jun Woong wrote:
If a program contains UB to be triggered, then its output or even
whether or not you can get the intended output always depend on that
UB, which makes the program not s.c.

If you're saying what I think you are saying,
I think that "UB to be triggered"
means the opposite of "that operation could never be executed"
it's not correct.
The following function could be used in a strictly conforming
program:
void foo( void ) {
if ( 0 )
1 / 0;
}
Division by zero would result in undefined behavior, but we
can easily see that that operation could never be executed
in this example.
--
pete
Sep 29 '06 #52

Jack Klein wrote:
On Mon, 25 Sep 2006 20:53:51 GMT, Frederick Gotham
<fg*******@SPAM.comwrote in comp.lang.c:
Robert Seacord posted:
ISO C forbids conversion of function pointer to object pointer type

Actually, the warning is incorrect. The C standard does not forbid
this. It produces undefined behavior by the lack of a definition. The
C standard specifically defines converting a pointer to any object
type to and from pointer to void, and certain other conversions
between pointers to object types with an appropriate cast.

It just plain does not define any conversions for pointers to
functions, hence any attempt to do so is undefined.
This is the first I have heard of this restriction. The smallest addressable
unit of memory in C is the byte (sidestepping the technicalities of bitfields
and so forth...). Four separate built-in pointer types are guaranteed to
store accurately the address of a byte:

char*
char signed*
char unsigned*
void*

(and all their const/volatile/restrict variants.)

Nowhere in the standard does it say that they can address every byte
that exists in a machine, merely every byte that an executable can
access.
In the formal abstract machine model of the C standard, code is not
addressed with bytes... Code is something that's executed by the
environment in an unspecified (by omission) way.
So, many existing implementations don't use the addressing space of
objects (bytes are something related to objects such as char, int,
etc.) for code. There are implementations where the code is in ROM,
there are also interpreted implementations (where the whole text code
or intermediate code is not entirely in the physical low-level memory,
but a place remains on the HD).

Or, if you prefer, the problem is not that void* can't address all the
bytes, it's that a function pointer doesn't identify any particular
byte! It identifies a *function* (or some code, if you prefer).
In particular, an implementation is allowed to represent a function
pointer by an ordinal number, used to import the function from a shared
library (thus, allowing lazy loading of the module containing the
function), or it might use any representation it likes.

But, even with naive implementations, there are many platforms where
function pointers are totally incompatible with object pointers (and
their sizes are even different).

Sep 30 '06 #53

ku****@wizard.net wrote:
Old Wolf wrote:
Jack Klein wrote:
Robert Seacord posted:

ISO C forbids conversion of function pointer to object pointer type
>
Actually, the warning is incorrect. The C standard does not forbid
this. It produces undefined behavior by the lack of a definition.
I think that if ____ causes undefined behaviour according to
the ISO C standard, then it is true to say that ISO C forbids ____ .
That is my understanding of the English word "forbid".
It does not matter whether it is explicitly undefined, or
undefined by omission.

To forbid something is to say that it should not be done. Except on
those occasions when the standard uses the word "shall", it generally
doesn't say things like that about programs.
It does say things like that about programs.

"
In this International Standard, ''shall'' is to be interpreted
as a requirement on an
implementation or on a program; conversely, ''shall not'' is to
be interpreted as a
prohibition.
"
"
A source file shall not end in a
partial preprocessing token or in a partial comment.
"

That's just an example, there are many other occurences of "shall" and
"shall not" that applies to programs, and not implementations.

Ok, that's not "forbidden", that's only "prohibited", with the words of
the standard.

However, I do think that the wording of the standard is unsactifactory
here, since, programs having UB or constraints violation can clearly be
legal C programs for some specific implementations that document &
allow those UB & constraints violations.

Worst, the definition of a strictly conforming program seems quite
stupid:
"
A strictly conforming program shall use only those features of the
language and library
specified in this International Standard.2) It shall not produce output
dependent on any
unspecified, undefined, or implementation-defined behavior, and shall
not exceed any
minimum implementation limit.
"

It's a bit as saying.
You shall not do that.
And if you're a strictly conforming, you shall not violates orders that
I give with the word "shall".
In general, it doesn't say
that a program shouldn't do something, it merely tells you what the
consequences of doing something are.
Not always.
It's also a conclusion that's not necessarily true: if a piece of code
whose behavior is undefined according to the C standard, has behavior
that is defined by one or more particular implemenations, it may be
quite reasonable to write such code when putting together a program
that is intended to only be used with those implementations.
Yes, I agree. And I think that the C rationale included something like
"allowing non-portable programs".
It also applies to ill-formed programs, since an implementation is
allowed to compile it & give it a documented behavior, and many
implementations do!
It's often
the case that such code is the only way to achieve the desired effect.
Not every program needs to be portable, or even can be portable.
Yes.

Sep 30 '06 #54
SuperKoko wrote:
ku****@wizard.net wrote:
....
To forbid something is to say that it should not be done. Except on
those occasions when the standard uses the word "shall", it generally
doesn't say things like that about programs.
It does say things like that about programs.
Yes, I explicitly acknowledged that fact when I said 'Except on those
occasions when the standard uses the word "shall"...'.
That's just an example, there are many other occurences of "shall" and
"shall not" that applies to programs, and not implementations.
Yes, but the overwhelming majority of the standard's statements
governing programs are not introduced with the word "shall". Of
course, even when it does, as far as the standard is concerned it is
simply a way of describing a constraint (if it occurs in a constraint),
or means exactly the same thing as saying that the behavior is
undefined (when it occurs outside a constraint). However, interpreted
as ordinary english, statements saying that something "shall" or "shall
not" be done are commands, whereas saying that the behaviour is
undefined is simply giving the reader information.
However, I do think that the wording of the standard is unsactifactory
here, since, programs having UB or constraints violation can clearly be
legal C programs for some specific implementations that document &
allow those UB & constraints violations.
That's precisely what the authors wanted. Most UB is there precisely in
order to allow some implementations to define the behavior. That's less
true of constraints, which is precisely why constraint violations
require a diagnostic.
In general, it doesn't say
that a program shouldn't do something, it merely tells you what the
consequences of doing something are.
Not always.
"In general" means essentially the same thing as "not always"; it's
just a difference in emphasis, like the difference between "half full"
and "half empty". In any event, if you felt there was a contradiction,
following that sentence up with a counter-example would have been
helpful.

Sep 30 '06 #55
Richard Tobin writes:
>Another possibility is that code addresses have the same values as
data addresses but refer to separate memory,
Francis Glassborow wrote:
>IIRC that is called a Harvard architecture and was also used by some of
the DOS models where different segments were used for data and program.
Michal Necasek wrote:
Except DOS was not Harvard architecture.

But yes, DOS, or rather 16-bit real- and protected- mode environments
typically provided memory models where the (near) pointer to a function
was completely different from a (near) pointer to a data item. They were
offsets into different segments, and while data and code memory was not
physically distinct and could be aliased, the pointers were not
interchangeable.

That said, I don't recall most of these environments being quite ISO C
compliant with regards to pointer handling.
True. In particular, most x86 compilers failed to properly convert
16-bit 'near' null pointers (e.g., 6A80:0000) into true 'far' null
pointers (0000:0000).

Most Win32 programmers don't realize that the "flat" 32-bit memory
address model of Windows actually uses the 32-bit segment registers
of the 386+, but simply arranges all of them (DS, ES, FS, GS) to
overlap exactly, so that it appears that all code, data, stack, etc.
segments lie in the same 32-bit range.

If [Win32] programs ever get to the point that 4GB is not enough for
the entire program (code + data + stack + heap), this could be an
opportunity to [re]introduce different segments for code and data.
OTOH, 64-bit CPUs will probably be the preferred solution by then.

-drt

Oct 1 '06 #56

David R Tribble wrote:
True. In particular, most x86 compilers failed to properly convert
16-bit 'near' null pointers (e.g., 6A80:0000) into true 'far' null
pointers (0000:0000).
They're not required to.
In particular memory model all the "normal" pointers types (i.e.
pointer types on which you don't apply any __near or __far specifier)
are of the same distance (near or far).
__near and __far are extensions, and thus, are allowed to follow
different rules than real pointers (or, if you prefer, in a small
memory model, __far pointers are not pointers, these are only
pseudo-pointers that don't obey rules of pointers, and, in particular
can't be cast to void*).

And a strictly conforming program can't see this issue.

A similar issue appears with calling conventions with win32 compilers:
A int(__stdcall*)() can't be implicitly converted to a int(*)().
You must not deduce that the compiler is not compliant. You must just
deduce that int(__stdcall*)() is not a function pointer as defined by
the ISO standard. It's a pseudo-function-pointer
__stdcall-function-pointer which obeys to very similar rules (the same
rules than function pointers, if you replace all the occurences of
function-pointer with __stdcall-function-pointer in the ISO standard)
than normal function pointers, except that it lives in a different
world than function pointers.
That's the same thing with __far pointers in small memory models.
__far pointers live in a different world than normal pointers, though
there is a special additionnal rule (and a few other additionnal rules)
which allows one to convert a normal pointer to a __far pointer (and
there this conversion has not to obey to pointer-to-pointer conversion
rules of the ISO standard).

AFAIK, Borland C++ 5.0 (which included a DOS compiler) is quite well
C89 compliant. The compliance is probably not perfect (well, at this
day, I've not seen any real compliance problem with it in strict mode),
but this is not fundamentally due to the memory model.
Most Win32 programmers don't realize that the "flat" 32-bit memory
address model of Windows actually uses the 32-bit segment registers
of the 386+, but simply arranges all of them (DS, ES, FS, GS) to
overlap exactly, so that it appears that all code, data, stack, etc.
segments lie in the same 32-bit range.

If [Win32] programs ever get to the point that 4GB is not enough for
the entire program (code + data + stack + heap), this could be an
opportunity to [re]introduce different segments for code and data.
OTOH, 64-bit CPUs will probably be the preferred solution by then.
IIRC, there was a 32 bits OS on x86 (I don't remember which one) which
allowed the programmer to use as many segments he wanted, and, CS, DS
and SS could be refering to different segments.
Note that i586 and lower can't use more than 4GB of physical memory.
i686 and higher can use up to 64GB of physical memory.

Oct 1 '06 #57
SuperKoko wrote:
IIRC, there was a 32 bits OS on x86 (I don't remember which one) which
allowed the programmer to use as many segments he wanted, and, CS, DS
and SS could be refering to different segments.
Note that i586 and lower can't use more than 4GB of physical memory.
i686 and higher can use up to 64GB of physical memory.
Use of non-flat 32-bit memory models was fairly common with DOS
extenders, as well as in certain specialized environments. For instance
the PharLap 386|DOS extender used a special selector to map the first
megabyte of physical memory.

At least Microsoft and Watcom had (have) compilers that supported
segmented 32-bit programming.
Michal
Oct 2 '06 #58

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

42 posts views Thread by Prashanth Badabagni | last post: by
4 posts views Thread by CsharpNewcommer | last post: by
7 posts views Thread by salmonella | last post: by
9 posts views Thread by junky_fellow | last post: by
reply views Thread by kmladenovski | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.