468,771 Members | 1,861 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

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

gcc knows about malloc()

The gcc compiler treats malloc() specially! I have no
particular question, but it might be fun to hear from
anyone who knows about gcc's special behavior.

Some may find this post interesting; some may find it
off-topic or confusing. Disclaimers at end.

The code samples are intended to be nearly minimal
demonstrations. They are *not* related to any
actual application code.

(1)
gcc assumes that malloc()'s return value is not an
alias for another pointer. This can be confirmed by compiling
and running the following code twice, once with a
-Dgetnode=malloc
option. (The special behavior will not be observed if
getnode is declared static, or the '-O' option is omitted.)

Compile and run the following code, first with
gcc -Wall -O
then with
gcc -Wall -O -Dgetnode=malloc

=== Begin sample code
#include <stdio.h>

struct node {
struct node *next;
} A[10];

void *getnode(int x)
{
return &A[x];
}

/* Insert A[x] into 3rd list slot */
void insert(x)
{
struct node *newp;
struct node *head = A;

newp = getnode(x);
newp->next = head->next->next;
/* will compiler recompute head->next, or use old value? */
head->next->next = newp;
}

int main(int argc, char **argv)
{
/* Create a circular list 0 -> 1 -> 2 -> 3 -> 0 */
A->next = A;
insert(1);
insert(3);
insert(2);
printf("Third element in list is A[%d]\n", A->next->next - A);

/* Next will "damage" the list, but should be deterministic! */
insert(0);
printf("Third element in list is A[%d]\n", A->next->next - A);
return 0;
}
=== End sample code

(2)
Speaking of malloc(), a misleading answer is sometimes given in
this ng, when coders neglect to include <stdlib.h>, and then
attempt to type-cast malloc().

While omitting the system header file is a mistake, strictly
speaking the resultant problems aren't caused by casting the
malloc() function incorrectly, but by failing to cast it at all!

In an expression such as
foo = (void *)malloc(TWELVE_MILLION);
it is malloc's *return value* that is cast, not malloc() itself.
This is another case where C syntax has different effect in
expression vs declaration.

To avoid the problem (assuming the <stdlib.h> file has been
accidentally erased, but one knows malloc's correct declaration)
one could write:
void *malloc();
...
foo = malloc(TWELVE_MILLION);

Or even (if your employer docks your pay for explicitly declaring
library functions) :

if (0) malloc(); /* tell gcc that malloc is a function */
foo = ((void *(*)())malloc)(TWELVE_MILLION);

(Please forgive the peculiar way to declare that malloc is a function!
It seemed like an approach for "minimum compilable example"!)

Please don't accuse me of encouraging perversion (!), but some
prior discussions *were* misleading.

(3)
While playing around, I discovered more bizarre behavior by
gcc (GNU C version 3.2) concerning malloc().

Compile the following code, e.g. with
gcc -c

=== Begin sample code
struct foo { int x; };

int main(int argc, char **argv)
{
if (0) malloc(); /* tell gcc malloc() is a function */
return (((struct foo(*)())malloc)(sizeof(struct foo))).x;
}
=== End sample code

When I compile it, I get "Segmentation fault"!
Before flaming, please note:
It never occurred to me to *run* this bizarre faulty program;
the "Segmentation fault" occurs during compilation.

Disclaimers:

(a) No one is advocating faulty type-casting, or faulty
replacements for malloc().

(b) No one is claiming 'gcc' is defective. To the contrary,
sample (1) demonstrates a clever efficiency that gcc is able to
apply due to malloc()'s semantics. (The fault in (3), I imagine,
would be easy to avoid and FSF probably would if it knew of it.)

(c) This post may be off-topic because gcc isn't standard C,
and some readers use other compilers. But this is a general forum
for issues relating to C.

(BTW, why *doesn't* everyone use the gcc compiler?)

(d) Standard C specifies what a compiler *may* do and what it
*must* do, not what it *might* do. Still, I wonder if the Standard
specifically condones gcc's optimization in (1).

(e) Both code samples above *violate* the rules of Standard C,
and instead of the "faulty" optimization or Segmentation fault,
]> ... it would be proper for a compliant Standard C compiler
]> to trash [my] hard disk, or launch a nuclear strike....
Such comments are amusing, but do become repetitious.
Frankly, anyone who has no interest in the above C fragments
beyond observing that they are non-compliant, strikes me
as lacking imagination.

For code sample (1) it would be easy to produce Standard C
code where the interesting difference emerges from a timing
measurement or examining object code, but it seemed more
straightforward to demonstrate gcc's clever optimization
with a substituted malloc() that invalidates the optimization.
James Dow Allen

- - - - - - - - - - -

Tell us, Senator, what is your opinion of alcohol?

``Well, if by alcohol you mean that hearty spirit which brings laughter
and
livelihood to social gatherings, which lubricates the gears of
business
transactions, which makes life's hardships easier to bear, then I say
hurrah for it, and I support its legal sale and consumption.

``However, if by alcohol you mean that evil essence which robs the
family
budget, which is a toxin to the body and the mind, which causes men
to
assault one another and to fail in their professions, then I say fie
on
it, and I will work with all my power to prevent its sale and
consumption.''

- - - - - - - - - - -

Jun 12 '06 #1
68 15028
jjf

James Dow Allen wrote:

Speaking of malloc(), a misleading answer is sometimes given in
this ng, when coders neglect to include <stdlib.h>, and then
attempt to type-cast malloc().

While omitting the system header file is a mistake, strictly
speaking the resultant problems aren't caused by casting the
malloc() function incorrectly, but by failing to cast it at all!
No, the resultant problems are caused by failing to include the header.
In an expression such as
foo = (void *)malloc(TWELVE_MILLION);
it is malloc's *return value* that is cast, not malloc() itself.
No. Without a prior correct declaration, it's the location in which
integer return values get placed which gets cast. This may or may not
be where malloc() returns its value. It's obviously "not malloc()
itself" though - what does that even mean?
This is another case where C syntax has different effect in
expression vs declaration.
What do you mean?
To avoid the problem (assuming the <stdlib.h> file has been
accidentally erased, but one knows malloc's correct declaration)
one could write:
void *malloc();
...
foo = malloc(TWELVE_MILLION);
If that happens to be the correct declaration in your implementation,
though it's unlikely to be the case in any modern implementation.
Or even (if your employer docks your pay for explicitly declaring
library functions) :

if (0) malloc(); /* tell gcc that malloc is a function */
foo = ((void *(*)())malloc)(TWELVE_MILLION);

(Please forgive the peculiar way to declare that malloc is a function!
You don't declare that malloc is a function anywhere here.
It seemed like an approach for "minimum compilable example"!)
The correct approach would be to include the appropriate header.
Please don't accuse me of encouraging perversion (!), but some
prior discussions *were* misleading.


Possibly, but nowhere near as misleading as the remarkable stuff you
wrote above.

Jun 12 '06 #2
On 12 Jun 2006 00:57:37 -0700, in comp.lang.c , "James Dow Allen"
<jd*********@yahoo.com> wrote:
Speaking of malloc(), a misleading answer is sometimes given in
this ng, when coders neglect to include <stdlib.h>, and then
attempt to type-cast malloc().

While omitting the system header file is a mistake, strictly
speaking the resultant problems aren't caused by casting the
malloc() function incorrectly, but by failing to cast it at all!
No. If you omit the header, the compiler must assume malloc returns an
int. It is a common mistake to think that casting the int to a pointer
"fixes" this. Not so. As a trivial counterexample, some real-world
operating systems use different registers for pointers and integers,
and casting the garbage in the integer register won't help.
In an expression such as
foo = (void *)malloc(TWELVE_MILLION);
it is malloc's *return value* that is cast, not malloc() itself.
Of course, but then once compiled, malloc() is a bunch of machine
language instructions, it would be fairly meaningless to cast that !
This is another case where C syntax has different effect in
expression vs declaration.
This doesn't make any sense - what do you mean?
To avoid the problem (assuming the <stdlib.h> file has been
accidentally erased, but one knows malloc's correct declaration)
one could write:
void *malloc();
This is guaranteed by the Standard, section 7.1.4
if (0) malloc(); /* tell gcc that malloc is a function */
foo = ((void *(*)())malloc)(TWELVE_MILLION);
this is NOT guaranteed, is exceptionally bad practice, and leads to
undefined behaviour. On many implementations, such code would lead to
unexplained crashes at runtime.
Please don't accuse me of encouraging perversion (!), but some
prior discussions *were* misleading.
I think you have some misunderstandings, probably based on only
experience on 'friendly' hardware.
While playing around, I discovered more bizarre behavior by
gcc (GNU C version 3.2) concerning malloc().
This is offtopic here, you need to take C bugs to the gcc newsgroups
and fora.
When I compile it, I get "Segmentation fault"!
so you found a compiler bug - please report it to gcc via their usual
method.
(c) This post may be off-topic because gcc isn't standard C,
and some readers use other compilers. But this is a general forum
for issues relating to C.
Please read the Welcome message, and about a zillion messages
discussion topicality
(BTW, why *doesn't* everyone use the gcc compiler?)


Because fish don't have feet.

--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
Jun 12 '06 #3
Mark McIntyre <ma**********@spamcop.net> wrote:
On 12 Jun 2006 00:57:37 -0700, in comp.lang.c , "James Dow Allen"
<jd*********@yahoo.com> wrote:
if (0) malloc(); /* tell gcc that malloc is a function */
foo = ((void *(*)())malloc)(TWELVE_MILLION);


this is NOT guaranteed, is exceptionally bad practice, and leads to
undefined behaviour. On many implementations, such code would lead to
unexplained crashes at runtime.


Where's the undefinedd behavior? Code that's not executed and contains
no syntax errors doesn't cause undefined behavior. All function pointer
types are supposed to have the same representation. It's not clear
what's undefined here.
Jun 12 '06 #4

Jordan Abel wrote:
Mark McIntyre <ma**********@spamcop.net> wrote:
On 12 Jun 2006 00:57:37 -0700, in comp.lang.c , "James Dow Allen"
<jd*********@yahoo.com> wrote:
if (0) malloc(); /* tell gcc that malloc is a function */
foo = ((void *(*)())malloc)(TWELVE_MILLION);
this is NOT guaranteed, is exceptionally bad practice, and leads to
undefined behaviour. On many implementations, such code would lead to
unexplained crashes at runtime.


Where's the undefinedd behavior?

When the pointer-to-function is used to call the function.

In the C99 standard (the C89 standard has similar statments, see
below):

6.3.2.3-8 says
"
A pointer to a function of one type may be converted to a pointer to a
function of another
type and back again; the result shall compare equal to the original
pointer. If a converted
pointer is used to call a function whose type is not compatible with
the pointed-to type,
the behavior is undefined.
"
void* and int are not compatible.
Code that's not executed and contains
no syntax errors doesn't cause undefined behavior. AFAIK "foo = ((void *(*)())malloc)(TWELVE_MILLION)" will be evaluated.
All function pointer
types are supposed to have the same representation. Could you quote a paragraph from the standard?

I have only found:
6.7.5.3-15
"
For two function types to be compatible, both shall specify compatible
return types.
Moreover, the parameter type lists, if both are present, shall agree in
the number of
parameters and in use of the ellipsis terminator; corresponding
parameters shall have
compatible types. [...]
"
Since void* and int are not compatible types, int() and void*() are
incompatible function types.

6.7.5.1-2
"
For two pointer types to be compatible, both shall be identically
qualified and both shall
be pointers to compatible types.
"
Thus int(*)() and void*(*)() are not compatible pointer types.

Note also that "same representation" doesn't mean "compatible types".
For instance:
6.7.3-9
"
For two qualified types to be compatible, both shall have the
identically qualified version
of a compatible type; the order of type qualifiers within a list of
specifiers or qualifiers
does not affect the specified type.
"
Thus const int and int are not compatible types while they have the
same representation.
6.2.5-25
"
Any type so far mentioned is an unqualified type. Each unqualified type
has several
qualified versions of its type,38) corresponding to the combinations of
one, two, or all
three of the const, volatile, and restrict qualifiers. The qualified or
unqualified
versions of a type are distinct types that belong to the same type
category and have the
same representation and alignment requirements.39) A derived type is
not qualified by the
qualifiers (if any) of the type from which it is derived.
"
Note also that, since int() and void*() are not compatible types,
pointers to these function types can have a different representation:
6.5.5-26
"
A pointer to void shall have the same representation and alignment
requirements as a
pointer to a character type.39) Similarly, pointers to qualified or
unqualified versions of
compatible types shall have the same representation and alignment
requirements. All
pointers to structure types shall have the same representation and
alignment requirements
as each other. All pointers to union types shall have the same
representation and
alignment requirements as each other. Pointers to other types need not
have the same representation or alignment requirements.
"

Now, I see that your code uses C89, not C99 (implicit declaration have
been banned in C99):
Anyway, I can quote equivalent statements from the C89 standard:
3.3.4 (Cast operators)
"
A pointer to a function of one type may be converted to a pointer to a
function of another type and back again; the result shall compare equal
to the original pointer. If a converted pointer is used to call a
function that has a type that is not compatible with the type of the
called function, the behavior is undefined.
"

3.5.4.3 Function declarators (including prototypes)
"
For two function types to be compatible, both shall specify compatible
return types.63 Moreover, the parameter type lists, if both are
present, shall agree in the number of parameters and in use of the
ellipsis terminator; corresponding parameters shall have compatible
types.
"
3.5.4.1 Pointer declarators
"
For two pointer types to be compatible, both shall be identically
qualified and both shall be pointers to compatible types.
"

Furthermore, even the non-evaluated expression "malloc()" will give
another undefined behavior (which might give a link-time error for
instance).
3.3.2.2 Function calls
"
If the expression that precedes the parenthesized argument list in a
function call consists solely of an identifier, and if no declaration
is visible for this identifier, the identifier is implicitly declared
exactly as if, in the innermost block containing the function call, the
declaration

extern int identifier();

appeared
"

3.5 Declarations (or 6.7-4 in the C99 standard)
"
All declarations in the same scope that refer to the same object or
function shall specify compatible types.
"

3.1.2.6 Compatible type and composite type (6.2.7-2 in the C99
standard)
"
All declarations that refer to the same object or function shall have
compatible type; otherwise the behavior is undefined.
"
It's not clear
what's undefined here.


There are two undefined behaviors : one for each line.

Jun 12 '06 #5
James Dow Allen (in
11*********************@i40g2000cwc.googlegroups.c om) said:

| The gcc compiler treats malloc() specially! I have no
| particular question, but it might be fun to hear from
| anyone who knows about gcc's special behavior.

You seem surprised. Gcc implementations can sometimes be enhanced to
take advantage of target processor architectural features. Consider it
an advantage (and/or disadvantage) of open-source software.

--
Morris Dovey
DeSoto Solar
DeSoto, Iowa USA
http://www.iedu.com/DeSoto
Jun 12 '06 #6
SuperKoko schrieb:
Jordan Abel wrote:
Mark McIntyre <ma**********@spamcop.net> wrote:
On 12 Jun 2006 00:57:37 -0700, in comp.lang.c , "James Dow Allen"
<jd*********@yahoo.com> wrote:

if (0) malloc(); /* tell gcc that malloc is a function */
foo = ((void *(*)())malloc)(TWELVE_MILLION);
this is NOT guaranteed, is exceptionally bad practice, and leads to
undefined behaviour. On many implementations, such code would lead to
unexplained crashes at runtime.

Where's the undefinedd behavior?

When the pointer-to-function is used to call the function.

In the C99 standard (the C89 standard has similar statments, see
below):

6.3.2.3-8 says
"
A pointer to a function of one type may be converted to a pointer to a
function of another
type and back again; the result shall compare equal to the original
pointer. If a converted
pointer is used to call a function whose type is not compatible with
the pointed-to type,
the behavior is undefined.
"
void* and int are not compatible.


[snip]

But the converted pointer type is void*(*)() and the pointed-to type is
not int(*)() but the type malloc is implemented in the library, and
malloc does not return int but void*. So where is the undefined behaviour?

Thomas
Jun 12 '06 #7
"James Dow Allen" <jd*********@yahoo.com> writes:
[...]
Speaking of malloc(), a misleading answer is sometimes given in
this ng, when coders neglect to include <stdlib.h>, and then
attempt to type-cast malloc().

While omitting the system header file is a mistake, strictly
speaking the resultant problems aren't caused by casting the
malloc() function incorrectly, but by failing to cast it at all!
No, the undefined behavior is caused by calling malloc() without a
visible prototype. Casting the result may change the way that
undefined behavior is manifested, and may prevent the compiler from
warning you about it.
In an expression such as
foo = (void *)malloc(TWELVE_MILLION);
it is malloc's *return value* that is cast, not malloc() itself.
Of course.
This is another case where C syntax has different effect in
expression vs declaration.
I'll join several other people in not understanding what you mean by
this. If you're drawing a parallel between
(void*)malloc(TWELVE_MILLION)
and
void *malloc(size_t size);
that's not the same syntax.

This is a case where one piece of C syntax in an expression has a
different effect than a different piece of C syntax in a declaration.
To avoid the problem (assuming the <stdlib.h> file has been
accidentally erased, but one knows malloc's correct declaration)
one could write:
void *malloc();
...
foo = malloc(TWELVE_MILLION);
One could, but one would very likely invoke undefined behavior.
Given
void *malloc();
the compiler doesn't know the type, or even number, of malloc()'s
argument(s). Unless TWELVE_MILLION happens to be promoted to
a type compatible with size_t, you're lying to the compiler.
Or even (if your employer docks your pay for explicitly declaring
library functions) :

if (0) malloc(); /* tell gcc that malloc is a function */
foo = ((void *(*)())malloc)(TWELVE_MILLION);
Undefined behavior.

By converting malloc (a function name which has been implicitly
converted to a pointer-to-function) to type (void *(*)(), you're
trying to avoid the problem with malloc()'s return type, but you're
still lying to the compiler. The compiler assumes that malloc() is a
function returning int. You're *assuming* that this is the case (it
isn't), and converting the bogus pointer to a pointer to function
returning void*. It's the same problem you get with the simpler
(void*)malloc(TWELVE_MILLION)
where you take something that's really a void*, forcing the compiler
to assume it's an int, and converting it from int to void*.

You're probably assuming that all pointer-to-function types have the
same representation. That's probably true in most or all
implementations, but it's not guaranteed. The standard guarantees
that any pointer-to-function type can be converted to any other
pointer-to-function type and back again, yielding the original pointer
value -- but the conversions don't necessarily just reinterpret the
bits of the pointer. C99 6.2.5 specifically guarantees that all
pointers to structure types have the same representation and alignment
requirements; likewise for pointers to union types. It makes no such
guarantess for pointers to function types.

In all your function declarations and casts, you're omitting the
parameter type. If you must provide your own declaration of malloc()
for some reason, the correct declaration is
void *malloc(size_t);
or
void *malloc(size_t size);

There is, of course, no good reason to use any method other than
#include <stdlib.h>
(unless your compilation system is broken and you're unable to repair
or replace it -- but if it's that badly broken it's not clear that
even a correct declaration will be an effective workaround).

[snip]
Please don't accuse me of encouraging perversion (!), but some
prior discussions *were* misleading.
Some of them undoubtedly were, but not all of them. I think you've
just misunderstood them.

[...] When I compile it, I get "Segmentation fault"!
Before flaming, please note:
It never occurred to me to *run* this bizarre faulty program;
the "Segmentation fault" occurs during compilation. [...] (b) No one is claiming 'gcc' is defective. To the contrary,
sample (1) demonstrates a clever efficiency that gcc is able to
apply due to malloc()'s semantics. (The fault in (3), I imagine,
would be easy to avoid and FSF probably would if it knew of it.)
<OT>
If gcc gets a segmentation fault during compilation, regardless of how
bad the compiled code might be, of course it's defective. In other
words, you've found a bug. You should report it to the gcc
maintainers. See <http://gcc.gnu.org/> for information on how to do
this.
</OT>
(BTW, why *doesn't* everyone use the gcc compiler?)


On some platforms, gcc doesn't exist. On others, another compiler
generates better code. For those who need C99, there are compilers
that support it better than gcc does. Many C compilers have features
that gcc lacks. I'm sure there are other reasons.

Why *should* everyone use the same compiler? The point of a standard
is that it's a contract between the implementer and the programmer; if
I write conforming code, I can use any conforming compiler I like.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Jun 12 '06 #8
James Dow Allen wrote:
Speaking of malloc(), a misleading answer is sometimes given in
this ng, when coders neglect to include <stdlib.h>, and then
attempt to type-cast malloc().

While omitting the system header file is a mistake, strictly
speaking the resultant problems aren't caused by casting the
malloc() function incorrectly, but by failing to cast it at all!
This is completely nuts. Without a suitable declaration of malloc() in
scope, the return value is assumed to be an int. Casting an int to a
pointer is not guaranteed to be meaningful. Your last sentence has no
intelligible meaning, since there is no way that castint that int to a
pointer helps at all.

In an expression such as
foo = (void *)malloc(TWELVE_MILLION);
it is malloc's *return value* that is cast, not malloc() itself.


Yeah. And who doesn't know that? Apparently, it's you, to judge from
your own nonsensical use of "casting the malloc() function" above.
Jun 12 '06 #9
Martin Ambuhl <ma*****@earthlink.net> writes:
James Dow Allen wrote:
Speaking of malloc(), a misleading answer is sometimes given in
this ng, when coders neglect to include <stdlib.h>, and then
attempt to type-cast malloc().
While omitting the system header file is a mistake, strictly
speaking the resultant problems aren't caused by casting the
malloc() function incorrectly, but by failing to cast it at all!


This is completely nuts. Without a suitable declaration of malloc() in
scope, the return value is assumed to be an int. Casting an int to a
pointer is not guaranteed to be meaningful.


You understate the problem. The int being converted is not guaranteed
to be meaningful, whether you convert it or not. The call itself
invokes undefined behavior before you even look at the result.

(On some systems, where int and void* happen to be the same size, and
function results of those types happen to be returned by the same
mechanism, casting the phony int result to a pointer type might happen
to "work". This is merely one of the infinitely many possible
consequences of undefined behavior. In fact, it's the worst one,
since the compiler is failing to tell you about your error.)

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Jun 12 '06 #10
"Morris Dovey" <mr*****@iedu.com> writes:
James Dow Allen (in
11*********************@i40g2000cwc.googlegroups. com) said: | The gcc compiler treats malloc() specially! I have no
| particular question, but it might be fun to hear from
| anyone who knows about gcc's special behavior. You seem surprised. Gcc implementations can sometimes be enhanced to
take advantage of target processor architectural features. Consider it
an advantage (and/or disadvantage) of open-source software.


<honest question> Why do you see targetting specific architectures as a
property of open-source software? You seem to be suggesting that this
would/could/does not happen with proprietary software, where you may
even be expected to pay more for specific architectures.

--
Chris.
Jun 12 '06 #11

Thomas J. Gritzan wrote:
SuperKoko schrieb:
Jordan Abel wrote:
Mark McIntyre <ma**********@spamcop.net> wrote:
On 12 Jun 2006 00:57:37 -0700, in comp.lang.c , "James Dow Allen"
<jd*********@yahoo.com> wrote:

> if (0) malloc(); /* tell gcc that malloc is a function */
> foo = ((void *(*)())malloc)(TWELVE_MILLION);
this is NOT guaranteed, is exceptionally bad practice, and leads to
undefined behaviour. On many implementations, such code would lead to
unexplained crashes at runtime.
Where's the undefinedd behavior?

When the pointer-to-function is used to call the function.

In the C99 standard (the C89 standard has similar statments, see
below):

6.3.2.3-8 says
"
A pointer to a function of one type may be converted to a pointer to a
function of another
type and back again; the result shall compare equal to the original
pointer. If a converted
pointer is used to call a function whose type is not compatible with
the pointed-to type,
the behavior is undefined.
"
void* and int are not compatible.


[snip]

But the converted pointer type is void*(*)() and the pointed-to type is
not int(*)() but the type malloc is implemented in the library, and
malloc does not return int but void*. So where is the undefined behaviour?


There are two successive UB which might seems to compensate each
other... But it might be false.

In mathematics you can't say : "I divided by zero, but just after that,
I multiplied by zero to compensate".

That's the same with the C language.

You lied a first time, saying that malloc returns int... This is the
first UB. It might do a link-time error, or simply yield an invalid,
meaningless, pointer when you take the address of malloc. This pointer
is theorically meaningless since the representation of an int(*)()
pointer can be totally different from a void*(*)().
If you're lucky, your compiler will have a single representation for
all function pointers.
If you're even more lucky, taking the address of malloc, after having
lied to the compiler, saying that it returns an int, may yield a
pointer which effectively points to malloc.

Then, you convert your (probably invalid) pointer to a void*(*)() which
would be the correct pointer type for the address of the *true* malloc.

But, a conversion is a conversion... Especially if representations of
different pointer types are different, it might change the pointer, and
then, the pointer will become invalid (it was already invalid)...
If you're lucky, it might give the correct pointer... But don't assume
that it will be always the case!

It is as if you said to a blind person with magical powers :
Take pepper on the upper rack (actually, there is salt on the upper
rack, and the pepper is on the lower rack).
The blind person excepts to get pepper, and might feel that it is the
salt, and complain...
Now, assuming that he doesn't complain (because you're lucky).
Now you say : With your magical power, transform this pepper into salt.
He says "adracadabra : pepper -to-salt transformation! abdradacabra!"
What do you except?
Perhaps will it explodes.
Perhaps will it transform the salt in pepper.
Perhaps will it transform the salt in ketchup.
If you're lucky, it will remain salt.

Note also that TWELVE_MILLION is expected to be of type size_t...
Otherwise bad things could happen (long live to prototypes).

And, of course many other undefined things could happen when doing:
foo = (void *)malloc(TWELVE_MILLION);

Because even if you're lucky and that it does no link-time error, and
that it invokes correctly malloc... The return value might be in the
wrong place.
For instance, the computer might use a different register to return int
values and to return void* values (perhaps the 68000 CPU). In that
case, the int value would be random.
Then, even if it is returned in the same register (if you're lucky and
that sizeof(int)==sizeof(void*)), it assumes that converting int to
void* doesn't change the representation... Which might be false too.

Jun 12 '06 #12
On 12 Jun 2006 11:00:51 GMT, in comp.lang.c , Jordan Abel
<ra*******@gmail.com> wrote:
Mark McIntyre <ma**********@spamcop.net> wrote:
On 12 Jun 2006 00:57:37 -0700, in comp.lang.c , "James Dow Allen"
<jd*********@yahoo.com> wrote:
if (0) malloc(); /* tell gcc that malloc is a function */
foo = ((void *(*)())malloc)(TWELVE_MILLION);


this is NOT guaranteed, is exceptionally bad practice, and leads to
undefined behaviour. On many implementations, such code would lead to
unexplained crashes at runtime.


Where's the undefinedd behavior?


Using malloc without a prototype in scope.
--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
Jun 12 '06 #13
Chris McDonald (in e6**********@enyo.uwa.edu.au) said:

| "Morris Dovey" <mr*****@iedu.com> writes:
|
|| James Dow Allen (in
|| 11*********************@i40g2000cwc.googlegroups.c om) said:
|
||| The gcc compiler treats malloc() specially! I have no
||| particular question, but it might be fun to hear from
||| anyone who knows about gcc's special behavior.
|
|| You seem surprised. Gcc implementations can sometimes be enhanced
|| to take advantage of target processor architectural features.
|| Consider it an advantage (and/or disadvantage) of open-source
|| software.
|
| <honest question> Why do you see targetting specific architectures
| as a property of open-source software? You seem to be suggesting
| that this would/could/does not happen with proprietary software,
| where you may even be expected to pay more for specific
| architectures.

I'm not suggesting anything at all about proprietary compilers. What
does happen is that you might find multiple (all different)
implementations of gcc for a single target processor - each of which
might emit, for example, differently optimized code - depending on the
inclinations and abilities of the particular individuals constructing
the toolsets.

I'm disinclined to be judgemental - but I do find the differences
fascinating.

--
Morris Dovey
DeSoto Solar
DeSoto, Iowa USA
http://www.iedu.com/DeSoto
Jun 12 '06 #14
Mark McIntyre wrote:
On 12 Jun 2006 11:00:51 GMT, in comp.lang.c , Jordan Abel
<ra*******@gmail.com> wrote:

Mark McIntyre <ma**********@spamcop.net> wrote:
On 12 Jun 2006 00:57:37 -0700, in comp.lang.c , "James Dow Allen"
<jd*********@yahoo.com> wrote:
if (0) malloc(); /* tell gcc that malloc is a function */
foo = ((void *(*)())malloc)(TWELVE_MILLION);

this is NOT guaranteed, is exceptionally bad practice, and leads to
undefined behaviour. On many implementations, such code would lead to
unexplained crashes at runtime.


Where's the undefinedd behavior?

Using malloc without a prototype in scope.


AFAICT that's an error only if `size_t' is a type that
is subject to the "integer promotions." And even that unlikely
problem could be corrected with a small change:

if (0) malloc(); /* tell gcc that malloc is a function */
foo = ((void *(*)(size_t))malloc)(TWELVE_MILLION);

--
Eric Sosman
es*****@acm-dot-org.invalid

Jun 13 '06 #15
>>>Where's the undefinedd behavior?


Using malloc without a prototype in scope.
AFAICT that's an error only if `size_t' is a type that
is subject to the "integer promotions."


It's also an error if the return type of malloc() is not int, which
it shouldn't be.

It is possible (even likely on some architectures, like 680X0) that
a pointer value is returned in the Pointer Return Register (e.g.
a0) and that integers are returned in the Integer Return Register
(e.g. d0) or the Integer Pair Return Registers (e.g. (d1,d0)).

If you use malloc() without a prototype in scope, the compiler will
pick up the uninitialized garbage in the Integer Return Register,
and perhaps move it to the Pointer Return Register when you cast
it, thereby losing the valid result and replacing it with garbage.
And even that unlikely
problem could be corrected with a small change:

if (0) malloc(); /* tell gcc that malloc is a function */
foo = ((void *(*)(size_t))malloc)(TWELVE_MILLION);


Actually, I think this approach would also fix the "uninitialized
garbage from wrong return register" issue, since when the call is
made, the compiler has the correct return type.

Gordon L. Burditt
Jun 13 '06 #16
SuperKoko <ta*******@yahoo.fr> wrote:

Thomas J. Gritzan wrote:
SuperKoko schrieb:
> Jordan Abel wrote:
>> Mark McIntyre <ma**********@spamcop.net> wrote:
>>> On 12 Jun 2006 00:57:37 -0700, in comp.lang.c , "James Dow Allen"
>>> <jd*********@yahoo.com> wrote:
>>>
>>>> if (0) malloc(); /* tell gcc that malloc is a function */
>>>> foo = ((void *(*)())malloc)(TWELVE_MILLION);
>>> this is NOT guaranteed, is exceptionally bad practice, and leads to
>>> undefined behaviour. On many implementations, such code would lead to
>>> unexplained crashes at runtime.
>> Where's the undefinedd behavior?
> When the pointer-to-function is used to call the function.
>
> In the C99 standard (the C89 standard has similar statments, see
> below):
>
> 6.3.2.3-8 says
> "
> A pointer to a function of one type may be converted to a pointer to a
> function of another
> type and back again; the result shall compare equal to the original
> pointer. If a converted
> pointer is used to call a function whose type is not compatible with
> the pointed-to type,
> the behavior is undefined.
> "
> void* and int are not compatible.
[snip]

But the converted pointer type is void*(*)() and the pointed-to type is
not int(*)() but the type malloc is implemented in the library, and
malloc does not return int but void*. So where is the undefined behaviour?


There are two successive UB which might seems to compensate each
other... But it might be false.

In mathematics you can't say : "I divided by zero, but just after that,
I multiplied by zero to compensate".

That's the same with the C language.


But he's NOT making the call until it's the right type.
You lied a first time, saying that malloc returns int... This is the
first UB. It might do a link-time error, or simply yield an invalid,
meaningless, pointer when you take the address of malloc.


The part you quoted doesn't support that. I'm not saying you're wrong,
i'm saying the quote doesn't support it. It says you can't _call_
a function through a pointer of the wrong type.
Jun 13 '06 #17
Gordon Burditt <go***********@burditt.org> wrote:
Where's the undefinedd behavior?
Using malloc without a prototype in scope.


AFAICT that's an error only if `size_t' is a type that
is subject to the "integer promotions."


It's also an error if the return type of malloc() is not int, which
it shouldn't be.

It is possible (even likely on some architectures, like 680X0) that
a pointer value is returned in the Pointer Return Register (e.g.
a0) and that integers are returned in the Integer Return Register
(e.g. d0) or the Integer Pair Return Registers (e.g. (d1,d0)).

If you use malloc() without a prototype in scope, the compiler will
pick up the uninitialized garbage in the Integer Return Register,
and perhaps move it to the Pointer Return Register when you cast
it, thereby losing the valid result and replacing it with garbage.


Except he's calling it as a function returning void *. that is what the
cast is for.
And even that unlikely
problem could be corrected with a small change:

if (0) malloc(); /* tell gcc that malloc is a function */
foo = ((void *(*)(size_t))malloc)(TWELVE_MILLION);


Actually, I think this approach would also fix the "uninitialized
garbage from wrong return register" issue, since when the call is
made, the compiler has the correct return type.


So did the original example. He didn't change the return type of the
cast.
Jun 13 '06 #18
Keith Thompson <ks***@mib.org> wrote:
"James Dow Allen" <jd*********@yahoo.com> writes:
[...]
Speaking of malloc(), a misleading answer is sometimes given in
this ng, when coders neglect to include <stdlib.h>, and then
attempt to type-cast malloc().

While omitting the system header file is a mistake, strictly
speaking the resultant problems aren't caused by casting the
malloc() function incorrectly, but by failing to cast it at all!


No, the undefined behavior is caused by calling malloc() without a
visible prototype. Casting the result may change the way that
undefined behavior is manifested, and may prevent the compiler from
warning you about it.


READ AGAIN. He didn't cast the result of the call. He cast the word
"malloc" and then called the result of the cast. Only one person here
even has a coherent argument. He's not talking about casting the
expression malloc(whatever), he's talking about casting the bare word
"malloc".
Jun 13 '06 #19
On 12 Jun 2006 00:57:37 -0700
"James Dow Allen" <jd*********@yahoo.com> wrote:
(3)
While playing around, I discovered more bizarre behavior by
gcc (GNU C version 3.2) concerning malloc().

Compile the following code, e.g. with
gcc -c

=== Begin sample code
struct foo { int x; };

int main(int argc, char **argv)
{
if (0) malloc(); /* tell gcc malloc() is a function */
return (((struct foo(*)())malloc)(sizeof(struct foo))).x;
}
=== End sample code

When I compile it, I get "Segmentation fault"!
Before flaming, please note:
It never occurred to me to *run* this bizarre faulty program;
the "Segmentation fault" occurs during compilation.


Note that the bug you just described was corrected on gcc 3.4.
Which gives the following output:
foo.c: In function `main':
foo.c:5: error: too few arguments to function `malloc'
foo.c:6: warning: function called through a non-compatible type
foo.c:6: note: if this code is reached, the program will abort
Jun 13 '06 #20
On 12 Jun 2006 14:17:09 -0700
"SuperKoko" <ta*******@yahoo.fr> wrote:
There are two successive UB which might seems to compensate each
other... But it might be false.

In mathematics you can't say : "I divided by zero, but just after that,
I multiplied by zero to compensate".


Is multiplying by zero a ub in math?
Jun 13 '06 #21
Rafael Almeida said:
On 12 Jun 2006 14:17:09 -0700
"SuperKoko" <ta*******@yahoo.fr> wrote:
There are two successive UB which might seems to compensate each
other... But it might be false.

In mathematics you can't say : "I divided by zero, but just after that,
I multiplied by zero to compensate".


Is multiplying by zero a ub in math?


We might reasonably replace "exhibits undefined behaviour" with "is
meaningless" for the purposes of a mathematical analogy.

So: if X is the (meaningless) result of a division by zero, multiplying X by
anything, including zero, is meaningless.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Jun 13 '06 #22
James Dow Allen wrote:
The gcc compiler treats malloc() specially!

gcc assumes that malloc()'s return value is not an
alias for another pointer. This can be confirmed by compiling
and running the following code twice, once with a
-Dgetnode=malloc
option.
void *getnode(int x)
{
return &A[x];
}
Defining an external symbol with the same name as a
standard library function (whether or not you have included
the relevant header), causes undefined behaviour.

Since your code has undefined behaviour, any bizarre
results should not be considered a compiler bug.
To avoid the problem (assuming the <stdlib.h> file has been
accidentally erased, but one knows malloc's correct declaration)
one could write:
void *malloc();
...
foo = malloc(TWELVE_MILLION);

Or even (if your employer docks your pay for explicitly declaring
library functions) :

if (0) malloc(); /* tell gcc that malloc is a function */
foo = ((void *(*)())malloc)(TWELVE_MILLION);


I can't imagine an employer rejecting the first example
but accepting the second!

In either case it is hard to see why you would do anything
other than:
#include <stdlib.h>

If your compiler does not have that file then it is non-conforming
so this whole discussion is moot!

Jun 13 '06 #23
SOMEBODY TOO BLOODY RUDE TO LEAVE ATTRIBUTION LINES IN PLACE wrote:
[Eric Sosman wrote]:
And even that unlikely
problem could be corrected with a small change:

if (0) malloc(); /* tell gcc that malloc is a function */
foo = ((void *(*)(size_t))malloc)(TWELVE_MILLION);


Actually, I think this approach would also fix the "uninitialized
garbage from wrong return register" issue, since when the call is
made, the compiler has the correct return type.


It might happen to work, but it still invokes undefined behavior,
because you're still lying to the compiler. Without a prototype in
scope, there's no guarantee that the compiler knows how to convert
malloc (which it will assume is a pointer to function returning int)
to type void *(*)(size_t).

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Jun 13 '06 #24
Jordan Abel <ra*******@gmail.com> writes:
Keith Thompson <ks***@mib.org> wrote:
"James Dow Allen" <jd*********@yahoo.com> writes:
[...]
Speaking of malloc(), a misleading answer is sometimes given in
this ng, when coders neglect to include <stdlib.h>, and then
attempt to type-cast malloc().

While omitting the system header file is a mistake, strictly
speaking the resultant problems aren't caused by casting the
malloc() function incorrectly, but by failing to cast it at all!


No, the undefined behavior is caused by calling malloc() without a
visible prototype. Casting the result may change the way that
undefined behavior is manifested, and may prevent the compiler from
warning you about it.


READ AGAIN. He didn't cast the result of the call. He cast the word
"malloc" and then called the result of the cast. Only one person here
even has a coherent argument. He's not talking about casting the
expression malloc(whatever), he's talking about casting the bare word
"malloc".


Ok, I read again. I was responding to the material that I quoted, not
(at that time) to James's proposed solution.

And as I said elsethread, casting malloc itself, as opposed to casting
the result of calling malloc(), *still* doesn't avoid undefined
behavior.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Jun 13 '06 #25
On Mon, 12 Jun 2006 21:04:26 GMT, Keith Thompson <ks***@mib.org>
wrote:
Martin Ambuhl <ma*****@earthlink.net> writes:
James Dow Allen wrote:
Speaking of malloc(), a misleading answer is sometimes given in
this ng, when coders neglect to include <stdlib.h>, and then
attempt to type-cast malloc().
While omitting the system header file is a mistake, strictly
speaking the resultant problems aren't caused by casting the
malloc() function incorrectly, but by failing to cast it at all!


This is completely nuts. Without a suitable declaration of malloc() in
scope, the return value is assumed to be an int. Casting an int to a
pointer is not guaranteed to be meaningful.


You understate the problem. The int being converted is not guaranteed
to be meaningful, whether you convert it or not. The call itself
invokes undefined behavior before you even look at the result.

(On some systems, where int and void* happen to be the same size, and

^^^^^^^^^^^^
that should be "nearly all systems", by my accounts.

--
jay
Jun 13 '06 #26
jaysome wrote:
On Mon, 12 Jun 2006 21:04:26 GMT, Keith Thompson <ks***@mib.org>
wrote:

Martin Ambuhl <ma*****@earthlink.net> writes:
James Dow Allen wrote:
Speaking of malloc(), a misleading answer is sometimes given in
this ng, when coders neglect to include <stdlib.h>, and then
attempt to type-cast malloc().
While omitting the system header file is a mistake, strictly
speaking the resultant problems aren't caused by casting the
malloc() function incorrectly, but by failing to cast it at all!

This is completely nuts. Without a suitable declaration of malloc() in
scope, the return value is assumed to be an int. Casting an int to a
pointer is not guaranteed to be meaningful.


You understate the problem. The int being converted is not guaranteed
to be meaningful, whether you convert it or not. The call itself
invokes undefined behavior before you even look at the result.

(On some systems, where int and void* happen to be the same size, and


^^^^^^^^^^^^
that should be "nearly all systems", by my accounts.

Not realy, less than half of mine (most being 64 bit).

--
Ian Collins.
Jun 13 '06 #27
jaysome said:
On Mon, 12 Jun 2006 21:04:26 GMT, Keith Thompson <ks***@mib.org>
wrote:

<snip>

(On some systems, where int and void* happen to be the same size, and

^^^^^^^^^^^^
that should be "nearly all systems", by my accounts.


I have at least two C compilers right here which use 16-bit int and 32-bit
void *. Both are installed and in reasonably frequent use.

Of course, you might think that Richard's an awkward git who keeps these
things around just to win arguments with, but in fact the real reason is
that some of the people who use my code *require* it to work on such
architectures, and I do so hate to disappoint people.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Jun 13 '06 #28
On Tue, 13 Jun 2006 07:27:33 +0000, Richard Heathfield
<in*****@invalid.invalid> wrote:
jaysome said:
On Mon, 12 Jun 2006 21:04:26 GMT, Keith Thompson <ks***@mib.org>
wrote:

<snip>

(On some systems, where int and void* happen to be the same size, and

^^^^^^^^^^^^
that should be "nearly all systems", by my accounts.


I have at least two C compilers right here which use 16-bit int and 32-bit
void *. Both are installed and in reasonably frequent use.

Of course, you might think that Richard's an awkward git who keeps these
things around just to win arguments with, but in fact the real reason is
that some of the people who use my code *require* it to work on such
architectures, and I do so hate to disappoint people.


I did say "nearly all systems", and not "all systems". I think you
helped to confirm my assertion.

--
jay
Jun 13 '06 #29
On Tue, 13 Jun 2006 19:22:15 +1200, Ian Collins <ia******@hotmail.com>
wrote:
jaysome wrote:
On Mon, 12 Jun 2006 21:04:26 GMT, Keith Thompson <ks***@mib.org>
wrote:

Martin Ambuhl <ma*****@earthlink.net> writes:

James Dow Allen wrote:
>Speaking of malloc(), a misleading answer is sometimes given in
>this ng, when coders neglect to include <stdlib.h>, and then
>attempt to type-cast malloc().
>While omitting the system header file is a mistake, strictly
>speaking the resultant problems aren't caused by casting the
>malloc() function incorrectly, but by failing to cast it at all!

This is completely nuts. Without a suitable declaration of malloc() in
scope, the return value is assumed to be an int. Casting an int to a
pointer is not guaranteed to be meaningful.

You understate the problem. The int being converted is not guaranteed
to be meaningful, whether you convert it or not. The call itself
invokes undefined behavior before you even look at the result.

(On some systems, where int and void* happen to be the same size, and


^^^^^^^^^^^^
that should be "nearly all systems", by my accounts.

Not realy, less than half of mine (most being 64 bit).


I don't doubt you, but you're only one in a thousand, if not a
million. What do you think about that assertion?

--
jay
Jun 13 '06 #30
jaysome said:
On Tue, 13 Jun 2006 07:27:33 +0000, Richard Heathfield
<in*****@invalid.invalid> wrote:
jaysome said:
On Mon, 12 Jun 2006 21:04:26 GMT, Keith Thompson <ks***@mib.org>
wrote:

<snip>

(On some systems, where int and void* happen to be the same size, and
^^^^^^^^^^^^
that should be "nearly all systems", by my accounts.


I have at least two C compilers right here which use 16-bit int and 32-bit
void *. Both are installed and in reasonably frequent use.

Of course, you might think that Richard's an awkward git who keeps these
things around just to win arguments with, but in fact the real reason is
that some of the people who use my code *require* it to work on such
architectures, and I do so hate to disappoint people.


I did say "nearly all systems", and not "all systems". I think you
helped to confirm my assertion.


Not at all. In fact, 100% of all systems that have been mentioned in
response to your assertion have been counter-examples. 0% is not "nearly
all" by any stretch of the imagination.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Jun 13 '06 #31
jaysome said:
On Tue, 13 Jun 2006 19:22:15 +1200, Ian Collins <ia******@hotmail.com>
wrote:
jaysome wrote:

(On some systems, where int and void* happen to be the same size, and

^^^^^^^^^^^^
that should be "nearly all systems", by my accounts.
Not realy, less than half of mine (most being 64 bit).


I don't doubt you, but you're only one in a thousand, if not a
million.


No, he's 1 in 2, so far. And his "less than half", coupled with my own
reply, still means that less than half the systems mentioned so far are in
line with your assertion.
What do you think about that assertion?


I think you need to learn that 2 != 1000, let alone 1000000.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Jun 13 '06 #32

Rafael Almeida wrote:
On 12 Jun 2006 14:17:09 -0700
"SuperKoko" <ta*******@yahoo.fr> wrote:
There are two successive UB which might seems to compensate each
other... But it might be false.

In mathematics you can't say : "I divided by zero, but just after that,
I multiplied by zero to compensate".

Is multiplying by zero a ub in math?

No. Multiplying by zero is correct in math (so my analogy is not
perfect).
Here, in math, there is a single "undefined thing" : division by zero.
But, the idea is that you can't compensate UB, in any way...
Trying to compensate with well-defined behavior doesn't help (the
multiplication by zero), and trying to compensate with undefined
behavior is not better.

Jordan Abel: The part you quoted doesn't support that. I'm not saying you're wrong,
i'm saying the quote doesn't support it. It says you can't _call_
a function through a pointer of the wrong type.

No, the standard doesn't say that.
It says that you can convert (with an explicit C cast) a function to a
pointer to function of an incompatible type, and cast it back to a
compatible type... Otherwise, using a pointer converted (with an
explicit C cast... Not any mean of buggy reintepretation of the bytes
of the representation of the pointer) from an original int(*)() pointer
to a void*(*)() pointer and calling a function on this new pointer
creates UB.
Strictly speaking, your program contains a single pointer conversion :
from int(*)() to void*(*)().

The original pointer is int(*)()... Nowhere in your code does appear an
explicit conversion from void*(*)() to int(*)().... You just have a
buggy mean to get a int(*)() pointer from nowhere sensible (from a
symbol which should refer to an int() function but that you interpret
as a void*() function).

For instance, if the compiler documents that the first cast yields a
valid pointer to function. For instance, the compiler (with an
extension) has two different "overloaded" functions : an "int
malloc(void)" and a "void* malloc(size_t)", and the compiler documents
that the explicit declaration of "int malloc()" is valid and refers to
a function which is quite different from "void* malloc()". For
instance, this "int malloc()" function could be an "atom allocator",
yielding a different int at each call (until they are released with
"void free(int)").

Such compiler extension doesn't affect any strictly conforming program
(and thus, the compiler remains ISO-compliant) and seems even quite
sensible (except that overloading malloc is not a good idea).
In that case, the declaration has not UB... But the conversion
int(*)()->void*(*)() yields a pointer that you can't use. It would be
ok, if converted back to int(*)()

Combining UB is not a good idea.

Jun 13 '06 #33
jaysome wrote:
On Tue, 13 Jun 2006 19:22:15 +1200, Ian Collins <ia******@hotmail.com>
wrote:

jaysome wrote:
On Mon, 12 Jun 2006 21:04:26 GMT, Keith Thompson <ks***@mib.org>
wrote: (On some systems, where int and void* happen to be the same size, and

^^^^^^^^^^^^
that should be "nearly all systems", by my accounts.


Not realy, less than half of mine (most being 64 bit).

I don't doubt you, but you're only one in a thousand, if not a
million. What do you think about that assertion?

There's at least as many 64bit desktop system shipping these days as
there are 32bit, not including the server space which has even greater
bias to 64bit.

--
Ian Collins.
Jun 13 '06 #34

Martin Ambuhl wrote:
James Dow Allen wrote:
While omitting the system header file is a mistake, strictly
speaking the resultant problems aren't caused by casting the
malloc() function incorrectly, but by failing to cast it at all!
[A poor choice of words: I used "cast" to mean "declare."
But I did show a specific code sample to clarify my intent.
And in one sample, I did "cast" malloc() rather than its
return value.]
This is completely nuts. Without a suitable declaration of malloc() in
scope, the return value is assumed to be an int.
Not by gcc, in sample code depicted.
In an expression such as
foo = (void *)malloc(TWELVE_MILLION);
it is malloc's *return value* that is cast, not malloc() itself.


Yeah. And who doesn't know that? Apparently, it's you,...


I didn't mean to start a flame-war.

I'll respond to some other comments:

Morris Dovey wrote: James Dow Allen wrote:
| The gcc compiler treats malloc() specially!

You seem surprised. Gcc implementations can sometimes be enhanced to
take advantage of target processor architectural features.
Somewhat surprised, and also impressed. I wondered whether it was
documented and whether there was similar special handling of
any other functions. Since it's a clever type of "noalias"
optimization.
it seemed like it might be of general interest even for those not
using gcc.

Only Morris actually responded to this, the major intent of my post.
(And he just used it as a plug for open-source :-) )
Some may find this post ...
off-topic or confusing.
This was prescient! For example:

Mark McIntyre wrote: No. If you omit the header, the compiler must assume malloc returns an
int. It is a common mistake to think that casting the int to a pointer
"fixes" this. Not so.
Mark makes two points, one wrong, the other precisely a point I was
making.

The wrong point is "If you omit the header, the compiler must
assume malloc returns an int." The header provides a declaration
of what malloc does return. Omit the header and declare malloc
yourself for the same effect. This is *wrong* as a matter of
style and maintainability, but not as a matter of C syntax
or semantics.

Mark's correct point is that "casting the int" doesn't in general
work. I explained this, and showed how (at least for gcc)
to fix it.

Jordan Abel wrote: SuperKoko <ta*******@yahoo.fr> wrote: > void* and int are not compatible.

That's the same with the C language.


But he's NOT making the call until it's the right type.


I'm glad someone at least acknowledges that my peculiar
construction "works"! I'm sure the formula is non-conformant
(I think it's sad the beautiful "portable assembly language"
has been taken over by lawyer-like thinking: frankly I stick
to K&R C), but *with the gcc compiler* it works just like I
(and Jordan) imply.

James Dow Allen

Jun 13 '06 #35

jaysome wrote:
On Mon, 12 Jun 2006 21:04:26 GMT, Keith Thompson <ks***@mib.org>
wrote:
(On some systems, where int and void* happen to be the same size, and

^^^^^^^^^^^^
that should be "nearly all systems", by my accounts.

I don't know how you "count" systems.
Must it be balanced by the number of CPU sold on the market for
personal computers for each system?
If the answer is no, then, your statement is false.

If the answer is yes (since there are mostly IA-32 CPU sold on the
market for personal computers), then you might be correct. But not for
a long time.
In a very near future, most personal computers will effectively use 64
bits architectures.
And the LP64 model (32 bits int, 64 bits long, 64 bits pointers) seems
to be the first choice of x86-64 based platforms (I'm quite sure that
PowerPC/OS-X and x86-64 Linux are using LP64 right now; correct me if
I'm wrong).
It looks like the next Microsoft Windows, will use LLP64 (32 bits int,
32 bits long, and 64 bits pointer), mainly for backward compatibility
with programs which assumed that long were 32 bits.
Here too, sizeof(int)!=sizeof(void*)

In that case nearly all systems will have sizeof(int)!=sizeof(void*)
(in number of CPU for personal computers).

That's funny to see how many programmers assumed
sizeof(int)==sizeof(void*) on the x86-32 architecture while it was
often false on x86-16 and will be often false on x86-64.

Jun 13 '06 #36
jaysome <ja*****@spamcop.net> writes:
On Mon, 12 Jun 2006 21:04:26 GMT, Keith Thompson <ks***@mib.org>
wrote:

[...]
(On some systems, where int and void* happen to be the same size, and

^^^^^^^^^^^^
that should be "nearly all systems", by my accounts.


And all the world's a VAX.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Jun 13 '06 #37
"James Dow Allen" <jd*********@yahoo.com> writes:
[...]
I'm glad someone at least acknowledges that my peculiar
construction "works"! I'm sure the formula is non-conformant
(I think it's sad the beautiful "portable assembly language"
has been taken over by lawyer-like thinking: frankly I stick
to K&R C), but *with the gcc compiler* it works just like I
(and Jordan) imply.


Ok, it happens to work with gcc, but frankly that's no more
interesting than the fact that
int *ptr = (int*)malloc(sizeof int);
(with no visible prototype for malloc()) happens to work on many
systems. Both invoke undefined behavior.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Jun 13 '06 #38
Gordon Burditt wrote:
Where's the undefinedd behavior?
Using malloc without a prototype in scope.
AFAICT that's an error only if `size_t' is a type that
is subject to the "integer promotions."

It's also an error if the return type of malloc() is not int, which
it shouldn't be.


It depends on what you think "it" is. If "it" is the
original code fragment
if (0) malloc(); /* tell gcc that malloc is a function */
foo = ((void *(*)())malloc)(TWELVE_MILLION);
.... then there's no "implicit int" in play at the point of
the call. The call uses a function pointer whose type almost
matches that of malloc() -- the only discrepancy being the
omission of the prototype -- so all is well unless, as I
mentioned, size_t is promotable. (I overlooked something:
there's also trouble if, for example, TWELVE_MILLION is an
int or double or some such, because there's no prototype to
force its conversion to size_t.)

Let's try a variation, shall we? In one file we'll have

#include <stdlib.h>
typedef void * (*fptr)(size_t); /* for readability */
fptr get_malloc_ptr(void) {
return malloc;
}

This function just returns a pointer to malloc(), which in turn
is properly declared by the header. Now in a separately-compiled
file we'll have

#include <stddef.h> /* for size_t */
void *my_malloc(size_t bytes) {
void * (*f)(size_t) = get_malloc_ptr();
return f(bytes);
}

This function acquires a pointer to malloc(), calls via the pointer,
and returns the result. Note carefully that no malloc() prototype
is visible at the point of the call -- there isn't a declaration
of any kind visible, with or without a prototype. Yet there is no
error here, no undefined behavior, no contravention of anything
except good sense.

The original code does pretty much the same thing, except
that it uses a different way of acquiring the pointer to malloc().
Both examples make their actual call to malloc() via a function
pointer expression of the proper type.
It is possible (even likely on some architectures, like 680X0) that
a pointer value is returned in the Pointer Return Register (e.g.
a0) and that integers are returned in the Integer Return Register
(e.g. d0) or the Integer Pair Return Registers (e.g. (d1,d0)).

If you use malloc() without a prototype in scope, the compiler will
pick up the uninitialized garbage in the Integer Return Register,
and perhaps move it to the Pointer Return Register when you cast
it, thereby losing the valid result and replacing it with garbage.


I think you have misread "it:" The operand of the cast
is not what you seem to believe it is.
And even that unlikely
problem could be corrected with a small change:

if (0) malloc(); /* tell gcc that malloc is a function */
foo = ((void *(*)(size_t))malloc)(TWELVE_MILLION);

Actually, I think this approach would also fix the "uninitialized
garbage from wrong return register" issue, since when the call is
made, the compiler has the correct return type.


The only difference between this and the original is the
insertion of the prototype. There is no UGFWRR issue in either
the original or the modified version.

--
Eric Sosman
es*****@acm-dot-org.invalid

Jun 13 '06 #39
James Dow Allen <jd*********@yahoo.com> wrote:

Morris Dovey wrote:
James Dow Allen wrote:
| The gcc compiler treats malloc() specially!

You seem surprised. Gcc implementations can sometimes be enhanced to
take advantage of target processor architectural features.


Somewhat surprised, and also impressed. I wondered whether it was
documented and whether there was similar special handling of
any other functions. Since it's a clever type of "noalias"
optimization.
it seemed like it might be of general interest even for those not
using gcc.


GCC knows about and has special handling for a large number of standard C functions
(as well as a smaller number of functions that are not part of Standard C.)

Since GCC knows how these functions are supposed to behave it can do
extra optimizations as well as give extra warnings for them.

Which functions it knows about is documented in the GCC manual (search for
'built-in functions'.) (GCC manuals can be found at http://gcc.gnu.org/onlinedocs/ )
Some other compilers do similar things. This is one reason it can be a very bad
idea to try to define your own versions of standard functions, since the compiler
might generate inlined code for the standard function and never generate a function call
at all, or might do some other optimizations.


--
<Insert your favourite quote here.>
Erik Trulsson
er******@student.uu.se
Jun 13 '06 #40
SuperKoko <ta*******@yahoo.fr> wrote:

Rafael Almeida wrote:
On 12 Jun 2006 14:17:09 -0700
"SuperKoko" <ta*******@yahoo.fr> wrote:
> There are two successive UB which might seems to compensate each
> other... But it might be false.
>
> In mathematics you can't say : "I divided by zero, but just after that,
> I multiplied by zero to compensate".
>


Is multiplying by zero a ub in math?

No. Multiplying by zero is correct in math (so my analogy is not
perfect).
Here, in math, there is a single "undefined thing" : division by zero.
But, the idea is that you can't compensate UB, in any way...
Trying to compensate with well-defined behavior doesn't help (the
multiplication by zero), and trying to compensate with undefined
behavior is not better.

Jordan Abel:
The part you quoted doesn't support that. I'm not saying you're wrong,
i'm saying the quote doesn't support it. It says you can't _call_
a function through a pointer of the wrong type.

No, the standard doesn't say that.
It says that you can convert (with an explicit C cast) a function to a
pointer to function of an incompatible type, and cast it back to a
compatible type... Otherwise, using a pointer converted (with an
explicit C cast... Not any mean of buggy reintepretation of the bytes
of the representation of the pointer) from an original int(*)() pointer
to a void*(*)() pointer and calling a function on this new pointer
creates UB.
Strictly speaking, your program contains a single pointer conversion :
from int(*)() to void*(*)().

The original pointer is int(*)()... Nowhere in your code does appear an
explicit conversion from void*(*)() to int(*)().... You just have a
buggy mean to get a int(*)() pointer from nowhere sensible (from a
symbol which should refer to an int() function but that you interpret
as a void*() function).


But that's not what your cite says. The standard may say so elsewhere,
but it doesn't say it in what you quoted.
Jun 13 '06 #41
Ian Collins <ia******@hotmail.com> wrote:
There's at least as many 64bit desktop system shipping these days as
there are 32bit,


That, i'd doubt.
Jun 13 '06 #42
Jordan Abel wrote:
Ian Collins <ia******@hotmail.com> wrote:
There's at least as many 64bit desktop system shipping these days as
there are 32bit,

That, i'd doubt.


Well it must be close, AMD have over 20% of the market and Intel are
shipping 64bit CPUs as well.

--
Ian Collins.
Jun 13 '06 #43
Eric Sosman <es*****@acm-dot-org.invalid> writes:
Gordon Burditt wrote:
> Where's the undefinedd behavior?

Using malloc without a prototype in scope.

AFAICT that's an error only if `size_t' is a type that
is subject to the "integer promotions." It's also an error if the return type of malloc() is not int, which
it shouldn't be.


It depends on what you think "it" is. If "it" is the
original code fragment
> if (0) malloc(); /* tell gcc that malloc is a function */
> foo = ((void *(*)())malloc)(TWELVE_MILLION);


... then there's no "implicit int" in play at the point of
the call. The call uses a function pointer whose type almost
matches that of malloc() -- the only discrepancy being the
omission of the prototype -- so all is well unless, as I
mentioned, size_t is promotable. (I overlooked something:
there's also trouble if, for example, TWELVE_MILLION is an
int or double or some such, because there's no prototype to
force its conversion to size_t.)


In the following, assume that there's no visible prototype for
malloc().

Suppose different pointer-to-function types are represented
differently. The standard guarantees that any pointer-to-function
type can be converted to any other pointer-to-function type, but the
compiler has to know the type of the original pointer in order to
generate correct code for the conversion.

In the above code fragment, the compiler doesn't necessarily know that
the malloc() function returns void*. (It's allowed to know this,
since it's in the standard library, but it's not required to.) The
compiler sees the identifier "malloc", and it knows that it's the name
of a function, but it doesn't know anything else about it; in
particular, it doesn't know the function's return type. One
reasonable thing for the compiler to do is to assume that the function
returns int (I'm not sure whether this is required, but it's certainly
allowed). So it generates code to convert something of type
"int (*)()" to type "void *(*)()", and applies this conversion to the
value of malloc. But malloc is really of type ""void *(*)()" already
(we've just hidden this fact from the compiler), so the conversion
yields garbage, and the attempt to do a function call using this
garbage pointer-to-function value invokes undefined behavior.

This is the same problem we see with the more common
int *ptr = (int*)malloc(sizeof int);
except that we're lying to the compiler about the type of the malloc
function rather than lying to the compiler about the type of the
result returned by the malloc function. In either case, we're
covering up the lie by doing a conversion, but we're not telling the
compiler what to convert from.

Ignoring the problem with the TWELVE_MILLION argument, this is all
likely to "work" either if all pointer-to-function types have the same
representation (which is probably true for all actual implementations)
*or* if the compiler uses its knowledge of the actual type of
malloc(). That just means that the undefined behavior will rarely, if
ever, cause any visible problems.

You can't meaningfully use malloc unless the compiler knows its type,
and the only way to tell the compiler what malloc's type is is to
declare it (preferably via "#include <stdlib.h>", but an explicit
declaration will also work).
Let's try a variation, shall we? In one file we'll have

#include <stdlib.h>
typedef void * (*fptr)(size_t); /* for readability */
fptr get_malloc_ptr(void) {
return malloc;
}

This function just returns a pointer to malloc(), which in turn
is properly declared by the header. Now in a separately-compiled
file we'll have

#include <stddef.h> /* for size_t */
void *my_malloc(size_t bytes) {
void * (*f)(size_t) = get_malloc_ptr();
return f(bytes);
}

This function acquires a pointer to malloc(), calls via the pointer,
and returns the result. Note carefully that no malloc() prototype
is visible at the point of the call -- there isn't a declaration
of any kind visible, with or without a prototype. Yet there is no
error here, no undefined behavior, no contravention of anything
except good sense.
If the second code fragment has a visible prototype for
get_malloc_ptr(), this should work correctly without invoking
undefined behavior. The compiler knows the type of the result of
get_malloc_ptr() because you've declared it properly. You haven't
lied to the compiler.

(If there is no visible prototype for get_malloc_ptr(), the compiler
will assume it returns int; the attempt to initialize f, which is of
type void *(*)(size_t), with an expression of type int is a constraint
violation.)
The original code does pretty much the same thing, except
that it uses a different way of acquiring the pointer to malloc().
Both examples make their actual call to malloc() via a function
pointer expression of the proper type.


No, the original code does a conversion of a pointer-to-function value
without knowing the actual type of the value being converted.

[...]

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Jun 13 '06 #44
Ian Collins <ia******@hotmail.com> wrote:
Jordan Abel wrote:
Ian Collins <ia******@hotmail.com> wrote:
There's at least as many 64bit desktop system shipping these days as
there are 32bit,

That, i'd doubt.


Well it must be close, AMD have over 20% of the market and Intel are
shipping 64bit CPUs as well.


What about the SPARC processors, aren't they 64 bit? SUN servers
were popular at some point (now they're switching to AMD-64).
Are PPC 64 bits?

--
Ioan - Ciprian Tandau
tandau _at_ freeshell _dot_ org (hope it's not too late)
(... and that it still works...)
Jun 13 '06 #45
Nelu wrote:
Ian Collins <ia******@hotmail.com> wrote:
Jordan Abel wrote:
Ian Collins <ia******@hotmail.com> wrote:
There's at least as many 64bit desktop system shipping these days as
there are 32bit,
That, i'd doubt.


Well it must be close, AMD have over 20% of the market and Intel are
shipping 64bit CPUs as well.

What about the SPARC processors, aren't they 64 bit? SUN servers
were popular at some point (now they're switching to AMD-64).
Are PPC 64 bits?

Yes and yes.

--
Ian Collins.
Jun 13 '06 #46
Keith Thompson wrote:

In the following, assume that there's no visible prototype for
malloc().

Suppose different pointer-to-function types are represented
differently. The standard guarantees that any pointer-to-function
type can be converted to any other pointer-to-function type, but the
compiler has to know the type of the original pointer in order to
generate correct code for the conversion. [...]


That's something I hadn't considered, and I think
you may be right.

--
Eric Sosman
es*****@acm-dot-org.invalid
Jun 13 '06 #47
On Mon, 12 Jun 2006 21:32:10 -0400, in comp.lang.c , Eric Sosman
<es*****@acm-dot-org.invalid> wrote:
Mark McIntyre wrote:

Using malloc without a prototype in scope.


AFAICT that's an error only if `size_t' is a type that
is subject to the "integer promotions."


Its an error if malloc returns anything other than int, which it does.
Irrespective of how you fool with the return type to cast it to a
pointer type, the compiler has been lied to and told to fetch an int
from some memory location which may (or may not) be a bucket of fetid
dingos kidneys.
--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
Jun 13 '06 #48
Mark McIntyre wrote:
Eric Sosman wrote:
foo = ((void *(*)(size_t))malloc)(TWELVE_MILLION);

AFAICT that's an error only if `size_t' is a type that
is subject to the "integer promotions."


Its an error if malloc returns anything other than int, which it does.
Irrespective of how you fool with the return type to cast it to a
pointer type, the compiler has been lied to and told to fetch an int
from some memory location which may (or may not) be a bucket of fetid
dingos kidneys.


The above code doesn't tell the compiler to fetch an int from
anywhere. The cast of the function pointer expression 'malloc'
results in a value of type 'pointer to function returning void* and
taking one argument of type size_t' and there is nothing
undefined about this conversion.

The actual definition of malloc() matches this type so there is
no undefined behaviour when malloc is called through this
function pointer.

It is quite different to casting the return value of
malloc. The above code is equivalent to:

void * (*fptr)(size_t) = malloc;
fptr(TWELVE_MILLION);

which is quite legal.

Jun 13 '06 #49
"Old Wolf" <ol*****@inspire.net.nz> writes:
Mark McIntyre wrote:
Eric Sosman wrote:
foo = ((void *(*)(size_t))malloc)(TWELVE_MILLION);

AFAICT that's an error only if `size_t' is a type that
is subject to the "integer promotions."
Its an error if malloc returns anything other than int, which it does.
Irrespective of how you fool with the return type to cast it to a
pointer type, the compiler has been lied to and told to fetch an int
from some memory location which may (or may not) be a bucket of fetid
dingos kidneys.


The above code doesn't tell the compiler to fetch an int from
anywhere.


True.
The cast of the function pointer expression 'malloc'
results in a value of type 'pointer to function returning void* and
taking one argument of type size_t'
True (more or less).
and there is nothing
undefined about this conversion.
False, I believe.

Consider the expression
malloc
If a correct prototype is visible, this expression (after the implicit
conversion that's done to function names in most contexts) is
void *(*)(size_t)
Casting this expression to the same type, of course, performs no
actual conversion and simply yields the same pointer-to-function
value.

But in the absence of a prototype, the compiler doesn't know the type
of the expression
malloc
so it cannot, in the general case, generate correct code to convert
this value to type void *(*)(size_t). It's likely to generate code
that would convert a value of type int (*)() to type
void *(*)(size_t); applying this code to a value that's *actually* of
type void *(*)(size_t) unleashes the nasal demons.

(The nasal demons are likely to be invisible and harmless if all
pointer-to-function types have the same representation, which is very
likely since it's the most obvious and straightforward way to satisfy
the standard's guarantee that any pointer-to-function type can be
meaningfully converted to any other pointer-to-function type and back
again.)
The actual definition of malloc() matches this type so there is
no undefined behaviour when malloc is called through this
function pointer.
Yes, but the actual definition of malloc() is not visible to the
compiler.
It is quite different to casting the return value of
malloc. The above code is equivalent to:

void * (*fptr)(size_t) = malloc;
fptr(TWELVE_MILLION);
Yes.
which is quite legal.


but invokes undefined behavior.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Jun 14 '06 #50

This discussion thread is closed

Replies have been disabled for this discussion.

By using this site, you agree to our Privacy Policy and Terms of Use.