473,386 Members | 1,741 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,386 software developers and data experts.

function called through a non-compatible type

When I typecast a function and call it trough the casted pointer (se
code below) I get the warnings:
warning: function called through a non-compatible type
if this code is reached, the program will abort
and the gcc generates abort code for the call. This happens when I use
gcc 3.4 but not when I use 3.3. Is there any flag that can change this
behavior?
I am aware that ANSI-99 says (in 6.3.2.3-8): "If a converted pointer is
used to call a function whose type is not compatible with the
pointed-to type, the behaviour is undefined". But I can't see how the
pointers are incompatible.
int foo(void *x) {
return *(int*)x;
}
int main(void) {
int x=0;
return ((int(*)(int*))foo)(&x);
}
This kind of typecasting can sometimes be very useful in macros to
ensure correct types of call-back functions (e.g such a macro can be
used to match the data-pointer to qsort with the arguments to the
parameter types of the call-back-function).
--
Christer

Nov 14 '05 #1
9 3437


christer-sandberg wrote:
When I typecast a function and call it trough the casted pointer (se
code below) I get the warnings:
warning: function called through a non-compatible type
if this code is reached, the program will abort
and the gcc generates abort code for the call. This happens when I use
gcc 3.4 but not when I use 3.3. Is there any flag that can change this
behavior?
Ask on a gcc newsgroup. Suggested subject: "How can
I disable my airbags, seat belts, and brakes?"
I am aware that ANSI-99 says (in 6.3.2.3-8): "If a converted pointer is
used to call a function whose type is not compatible with the
pointed-to type, the behaviour is undefined". But I can't see how the
pointers are incompatible.
int foo(void *x) {
return *(int*)x;
}
int main(void) {
int x=0;
return ((int(*)(int*))foo)(&x);
}
The argument to foo() is a `void*'. The cast specifies
an `int*' argument. `void*' and `int*' are incompatible,
hence foo() and the converted pointer are incompatible.
This kind of typecasting can sometimes be very useful in macros to
ensure correct types of call-back functions (e.g such a macro can be
used to match the data-pointer to qsort with the arguments to the
parameter types of the call-back-function).


I'm not entirely certain what you mean here, but if
you mean what I think you mean then I think you're wrong.

What I think you mean is that you would like to write
casts to convert a pointer to J. Random Function to the type
expected by, say, qsort():

int f(int *x, int *y) { ... }
...
qsort(..., (int (*)(const void*, const void*))f);

This does not "ensure correct types of call-back functions,"
not at all. It merely attempts to disguise a function of
the Wrong type as a function of the Right type so qsort()
will accept it. If the disguise succeeds qsort() may accept
the function -- but there's just no telling what may happen
when qsort() tries to call it.

Here's another one you might try as a thought experiment:

qsort(..., (int (*)(const void*, const void*))free);

Do you think that the cast will somehow magically transform
free() into a well-behaved comparison function? If not, why
would you expect it to do so with f(), above?

--
Er*********@sun.com

Nov 14 '05 #2
"christer-sandberg" <ch***************@mdh.se> writes:
When I typecast a function and call it trough the casted pointer (se
code below) I get the warnings:
warning: function called through a non-compatible type
if this code is reached, the program will abort
and the gcc generates abort code for the call. This happens when I use
gcc 3.4 but not when I use 3.3. Is there any flag that can change this
behavior?
I am aware that ANSI-99 says (in 6.3.2.3-8): "If a converted pointer is
used to call a function whose type is not compatible with the
pointed-to type, the behaviour is undefined". But I can't see how the
pointers are incompatible.
int foo(void *x) {
return *(int*)x;
}
int main(void) {
int x=0;
return ((int(*)(int*))foo)(&x);
}
The pointers are incompatible because they point to two different
function types. In particular, one function takes a void* argument,
and the other takes an int* argument. On some implementations, it's
entirely possible that void* and int* have different representations.
Calling a function that expects a void* through a pointer to a
function that expects an int* invokes undefined behavior.
This kind of typecasting can sometimes be very useful in macros to
ensure correct types of call-back functions (e.g such a macro can be
used to match the data-pointer to qsort with the arguments to the
parameter types of the call-back-function).


That's a bad idea; it can blow up in your face when the code is ported
to another implementation.

On the other hand, gcc's behavior of deliberately inserting code to
abort the program does seem a little odd. As far as I know, void* and
int* have the same representation on all platforms that gcc supports
(though I could be mistaken), so the call would probably work if gcc
allowed it. But your best bet is to avoid calling functions through
incompatible pointer types.

--
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.
Nov 14 '05 #3

Thanks for your answer.
Eric Sosman wrote:
The argument to foo() is a `void*'. The cast specifies
an `int*' argument. `void*' and `int*' are incompatible,

If I don't remember wrong, according to ANSI-C from 99 a void pointer
can be assigned the value of a pointer to an object and they should
compare equal. In what sense do you mean they should be incompatible?

This kind of typecasting can sometimes be very useful in macros to
ensure correct types of call-back functions (e.g such a macro can be used to match the data-pointer to qsort with the arguments to the
parameter types of the call-back-function).


I'm not entirely certain what you mean here, but if
you mean what I think you mean then I think you're wrong.

What I think you mean is that you would like to write
casts to convert a pointer to J. Random Function to the type
expected by, say, qsort():

int f(int *x, int *y) { ... }
...
qsort(..., (int (*)(const void*, const void*))f);

This does not "ensure correct types of call-back functions,"
not at all. It merely attempts to disguise a function of
the Wrong type as a function of the Right type so qsort()
will accept it. If the disguise succeeds qsort() may accept
the function -- but there's just no telling what may happen
when qsort() tries to call it.

Here's another one you might try as a thought experiment:

qsort(..., (int (*)(const void*, const void*))free);

Do you think that the cast will somehow magically transform
free() into a well-behaved comparison function? If not, why
would you expect it to do so with f(), above?

I meant niether of them. Rather something like what I wrote (sorry for
my bad English)
a macro ensuring the correct type of parameters to the callback
(otherwise the the parameters needs to be casted in the function
definition in which case there is nothing ensuring that the type of the
array is used) e.g.:
#define QSORT(d, n, t, f) (void(*)(t*, size_t, size_t, int(*)(const t*,
const t*))) \
qsort(d, n, sizeof(t), f);
I can be done some ather ways too, but as I see it, it is an advantage
to be able to force the type of array, the size of the array elements
and the types of the paramaters to the call.back be the same.
--
Christer

Nov 14 '05 #4
"christer-sandberg" <ch***************@mdh.se> writes:
Thanks for your answer.
Eric Sosman wrote:
The argument to foo() is a `void*'. The cast specifies
an `int*' argument. `void*' and `int*' are incompatible,

If I don't remember wrong, according to ANSI-C from 99 a void pointer
can be assigned the value of a pointer to an object and they should
compare equal. In what sense do you mean they should be incompatible?


Yes, they can be assigned. In an assignment, the compiler knows
whether it needs allow for a change of representation to implement the
conversion. If you use a function pointer of the wrong type, the
called function has no way of knowing that the parameter isn't of the
expected type, so it won't allow for any change of representation.
You're not converting the pointer, you're pretending that it's of a
different type.

--
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.
Nov 14 '05 #5
Thanks for your answer
Keith Thompson wrote:
On some implementations, it's
entirely possible that void* and int* have different representations. That seems to be a reason (actually I suspected something like that). I
guess it could be something like different pointer types can be of
different sizes and a void pointer must be big enough to contain any
othe pointer type, and in case int* is smaller there may be undefined
data in the gap. This requires howeveer the difference of the sizes to
be at least so big that the compiler can save som space (rather that
just need to 0-extend the small ones). Do you happen to know if there
are such cases?
Calling a function that expects a void* through a pointer to a
function that expects an int* invokes undefined behavior. According the rest of what you write, shouldn't it be ".. may on some
platforms invoke..." here?
On the other hand, gcc's behavior of deliberately inserting code to
abort the program does seem a little odd. As far as I know, void* and int* have the same representation on all platforms that gcc supports
(though I could be mistaken), so the call would probably work if gcc
allowed it.

Yes, e.g. the below code is both legal and executable (on my target):
static int foo(void*x) {
return *(int*)x;
}
int main(void) {
int x=0;
return ((int (*)(int*))((void*)foo))(&x);
}
--
Christer

Nov 14 '05 #6


christer-sandberg wrote:
Thanks for your answer.
Eric Sosman wrote:
The argument to foo() is a `void*'. The cast specifies
an `int*' argument. `void*' and `int*' are incompatible,
If I don't remember wrong, according to ANSI-C from 99 a void pointer
can be assigned the value of a pointer to an object and they should
compare equal. In what sense do you mean they should be incompatible?


The Standardese answer is "In the sense of 6.2.7, where
type compatibility is defined."

A (possibly) more helpful answer is by example. You can
convert any `int*' to a `void*' and back again, but it need
not be true that you can do the reverse: there may be `void*'
values that cannot be converted to `int*' successfully. On
many or even most present-day systems, such conversions can
produce values that are not usable as `int*'. So it's clear
that `int*' and `void*' are not the same type, even though
there are some pointer values that both can represent.

Or here's another: On every system I have ever encountered
(though I don't believe the Standard actually requires it),
every `char' value can be converted to `double' and back, and
the result compares equal to the original. Does this mean that
`char' and `double' are compatible types? Of course not.
[...]
I meant niether of them. Rather something like what I wrote (sorry for
my bad English)
a macro ensuring the correct type of parameters to the callback
(otherwise the the parameters needs to be casted in the function
definition in which case there is nothing ensuring that the type of the
array is used) e.g.:
#define QSORT(d, n, t, f) (void(*)(t*, size_t, size_t, int(*)(const t*,
const t*))) \
qsort(d, n, sizeof(t), f);
I can be done some ather ways too, but as I see it, it is an advantage
to be able to force the type of array, the size of the array elements
and the types of the paramaters to the call.back be the same.


Ingenious -- but even worse than what I thought you were
attempting! I'd supposed you were trying to disguise `f' so
the compiler wouldn't complain when you passed it to qsort().
Instead, you're lying about the nature of qsort() itself:

- You're telling the compiler to pass the first argument
as a `t*', but qsort() expects to receive a `void*'.
If any conversion from `t*' to `void*' is needed, your
recipe will prevent it from happening; if `t*' and `void*'
are passed to functions differently (e.g. in different
registers), your recipe will put the first argument in
the wrong place.

- You're telling the compiler that the fourth argument is
a pointer to a function taking two `const t*' arguments,
and your recipe will cause the compiler to complain if
the actual `f' is different. But the real qsort() doesn't
want such an `f'; it wants an `f' that takes a pair of
`const void*'. If there's any difference between these
(e.g., if a function pointer isn't a mere address but
also carries something like a "dope vector"), you cannot
expect this to work.

- And despite all these shenanigans, qsort() is still going
to pass `void*' arguments -- not `t*' arguments -- when it
calls `f'. Since that's not what `f' expects to receive,
there's no telling what might happen.

I agree that gcc's insertion of abort() is a bit draconian,
but it's understandable: gcc is trying to catch "really bad
incompatibilities," but it can only discern "incompatibility;"
it seems to have no notion of "probably venial incompatibility."
And you have to admit that it's had at least one good effect:
it's brought you here to learn the error of your (ingenious
albeit misguided) ways!

Enforcement of matching types is a weak point of C: there
are a number of places where you must rely on vigilance and can
expect little if any help from the language or the compiler.
"Type-blind" functions like qsort() and bsearch() -- memcpy(),
for that matter -- use `void*' precisely because they want to
be able to operate on any kind of data object, but blindness
has its drawbacks. Type-checking is defeated (that's the
purpose, after all), but that also means you lose the benefits
of type-checking. Your effort to restore those benefits is
laudable, but I think it's doomed. You might go as far as

#define QSORT(a, n, f) \
qsort((a), (n), sizeof *(a), (f))

.... to ensure that the third argument is correct, but I can
think of no way to ensure that `f' is the right function to use
with the type of data that happens to be in `a'. (There's even
less hope that you could ensure that `f' imposes a total ordering
on the values of that type!) Some constraints of a function's
documentation are simply not expressible in the language; C
shares this problem with other languages, too.

"If you lie to the compiler [even with the best of
intentions], it will get its revenge."

--
Er*********@sun.com

Nov 14 '05 #7
"christer-sandberg" <ch***************@mdh.se> writes:
Thanks for your answer
Keith Thompson wrote:
On some implementations, it's
entirely possible that void* and int* have different representations.

That seems to be a reason (actually I suspected something like that). I
guess it could be something like different pointer types can be of
different sizes and a void pointer must be big enough to contain any
othe pointer type, and in case int* is smaller there may be undefined
data in the gap. This requires howeveer the difference of the sizes to
be at least so big that the compiler can save som space (rather that
just need to 0-extend the small ones). Do you happen to know if there
are such cases?


I suspect the AS/400 is such a case; it seems to be the canonical
example of an exotic platform that has a conforming C implementation
while violating everyone's naive expectations.

Cray vector machines use a different representation for char* and
void* pointers than for int* pointers. A native machine address
points to a 64-bit word; byte pointers (implemented in software) store
a 3-bit offset in the unused high-order bits. The representation is
such that an arbitrary int* pointer happens to be a valid byte pointer
to the first byte of the word (the offset happens to be zero).

In any case, differences in size aren't the only way that two pointer
types can have a different representation.
Calling a function that expects a void* through a pointer to a
function that expects an int* invokes undefined behavior.

According the rest of what you write, shouldn't it be ".. may on some
platforms invoke..." here?


No, it invokes undefined behavior on all systems. On some systems,
the UB shows up as the program doing exactly what you expect it to do
(until you upgrade your compiler, or demonstrate your program to an
important customer, or maybe forever). That's what "undefined" means.
On the other hand, gcc's behavior of deliberately inserting code to
abort the program does seem a little odd. As far as I know, void*

and
int* have the same representation on all platforms that gcc supports
(though I could be mistaken), so the call would probably work if gcc
allowed it.

Yes, e.g. the below code is both legal and executable (on my target):
static int foo(void*x) {
return *(int*)x;
}
int main(void) {
int x=0;
return ((int (*)(int*))((void*)foo))(&x);
}


What do you mean by "legal"? It invokes undefined behavior. In fact,
I just noticed that you're casting the value of foo (a
pointer-to-function) to void*. That's not just undefined behavior,
it's a constraint violation; a conforming compiler is required to
diagnose it.

If your compiler happens to allow it, and happens to do what you
expect, that doesn't make it legal.

--
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.
Nov 14 '05 #8
Although I don't know how gcc dereferences a void *, on some
implementations I think dereferencing a void * gives you one byte
rather than a 32-bit word. This would cause irregular behaviour if you
made such a function argument cast, as functions tend to have code
specific to the range of types in the arguments. (e.g. a case that
overflows a char does not overflow an int) I think this comes down to
overloading, and thats also why overloaded functions would have
distinct code.

I think insertion of abort code comes from the distinction between
overloading (which would generate different code) and the simple case
of casting between two data types. the compiler can handle the latter
by simply dereferencing a different portion of memory.

Bahadir

Nov 14 '05 #9
Bi*************@gmail.com writes:
Although I don't know how gcc dereferences a void *, on some
implementations I think dereferencing a void * gives you one byte
rather than a 32-bit word.
Deferencing a void* is illegal. You have to convert it to a pointer
to some object type before you can dereference it.
This would cause irregular behaviour if you
made such a function argument cast, as functions tend to have code
specific to the range of types in the arguments. (e.g. a case that
overflows a char does not overflow an int) I think this comes down to
overloading, and thats also why overloaded functions would have
distinct code.


C doesn't have overloaded functions (except for the new type-generic
math stuff in C99, but that's a special case).

--
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.
Nov 14 '05 #10

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

Similar topics

19
by: Christian Engström | last post by:
If you have a function that returns something by value, the gcc compiler (version 3.2.3 on Windows XP with MinGW) converts the returned value from the type you specify in the code, to the const...
10
by: Michael | last post by:
Guys, I'm interested in how the compiler implements function calls, can anyone correct my understanding/point me towards some good articles. When a function is called, is the stack pointer...
5
by: klaus triendl | last post by:
hi, recently i discovered a memory leak in our code; after some investigation i could reduce it to the following problem: return objects of functions are handled as temporary objects, hence...
6
by: gustav04 | last post by:
hi all i have a question: what is the difference between a c-function and an c++ class method (both do exactly the same thing). lets say, i have a function called print2std() and a class...
6
by: Querejeto | last post by:
Hello: Is it possible to detect programmatically the constness of a member function when it is called? That is, I would like to see a generic implementation (i.e. it does not depend on the...
89
by: Cuthbert | last post by:
After compiling the source code with gcc v.4.1.1, I got a warning message: "/tmp/ccixzSIL.o: In function 'main';ex.c: (.text+0x9a): warning: the 'gets' function is dangerous and should not be...
11
by: ypjofficial | last post by:
Hello All, So far I have been reading that in case of a polymorphic class ( having at least one virtual function in it), the virtual function call get resolved at run time and during that the...
7
by: asdf | last post by:
They looks so similar. Anybody would like to tell me their differences? Thanks a lot.
6
by: Fabian Wein | last post by:
Hi, is there a way to call a const function from non const function? I have a non-const List GetList(); and want my
14
by: jg | last post by:
Does C++ standard require an inline function be generated all the time ? For example, #include <iostream> using namespace std; inline int foo() {
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

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

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