Deferencing type-punned pointers, and how to stop gcc giving you a warning
- From: Chris Sherlock <csherlock optusnet com au>
- To: Gnome-bugsquad gnome org
- Subject: Deferencing type-punned pointers, and how to stop gcc giving you a warning
- Date: Sun, 25 Apr 2004 18:59:35 +1000
Hi all,
I've been looking at a few warnings that gcc have been spitting out at
me lately about "Derefencing type-punned pointer will break
strict-aliasing rules".
Here's what the gcc manual has to say about it:
-fstrict-aliasing Allows the compiler to assume the strictest
aliasing rules applicable to the language being compiled. For C (and
C++), this activates optimizations based on the type of expressions.
In particular, an object of one type is assumed never to reside at
the same address as an object of a different type, unless the types
are almost the same. For example, an "unsigned int" can alias an
"int", but not a "void*" or a "double". A character type may alias
any other type.
Pay special attention to code like this:
union a_union { int i; double d; };
int f() { a_union t; t.d = 3.0; return t.i; }
The practice of reading from a different union member than the one
most recently written to (called ‘‘type-punning’’) is common. Even
with -fstrict-aliasing, type-punning is allowed, provided the memory
is accessed through the union type. So, the code above will work as
expected. However, this code might not:
int f() { a_union t; int* ip; t.d = 3.0; ip = &t.i; return *ip; }
Every language that wishes to perform language-specific alias
analysis should define a function that computes, given an "tree"
node, an alias set for the node. Nodes in different alias sets are
not allowed to alias. For an example, see the C front-end function
"c_get_alias_set".
Enabled at levels -O2, -O3, -Os."
and
-Wstrict-aliasing This option is only active when -fstrict-aliasing
is active. It warns about code which might break the strict aliasing
rules that the compiler is using for optimization. The warning does
not catch all cases, but does attempt to catch the more common
pitfalls. It is included in -Wall.
When I compiled gtk+2.4 (and gtk+2.2) I found that I was getting quite a
few of these warnings. During my discovery of the problem - see
http://bugzilla.gnome.org/show_bug.cgi?id=140722 - I've found some
general rules to try to prevent these warnings:
* if you use g_module_symbol, and you need to use a void* function
pointer with multiple parameters to get the module's symbol pointer,
then *also* declare a gpointer pfunc_name, and declare the function
pointer itself to be "void (*func_name) (int x, int y)=NULL;" Then, call
g_module_symbol and pass the third parameter as &pfunc_name. As soon as
it's been called, then assign func_name the address of pfunc_name.
Example (from gdk-pixbuf/queryloaders.c):
static void
query_module (const char *dir, const char *file)
{
char *path;
GModule *module;
// needed or gcc will give a warning that dereferencing
// type-punned pointer will break strict-aliasing rules
gpointer pfill_info;
gpointer pfill_vtable;
void (*fill_info) (GdkPixbufFormat *info)=NULL;
void (*fill_vtable) (GdkPixbufModule *module)=NULL;
if (g_path_is_absolute (file))
path = g_strdup (file);
else
path = g_build_filename (dir, file, NULL);
module = g_module_open (path, 0);
if (module &&
g_module_symbol (module, "fill_info", &pfill_info) &&
g_module_symbol (module, "fill_vtable", &pfill_vtable)) {
GdkPixbufFormat *info;
GdkPixbufModule *vtable;
fill_info = pfill_info;
fill_vtable = pfill_vtable;
.
.
.
etc
As far as I can tell, gcc doesn't like casting a function pointer with a
specific parameter signature to a regular gpointer. So you have to pass
an actual gpointer variable's address to g_module_symbol and then copy
over the the gpointer variable's address to the actual function pointer
and THEN call the function pointer.
* if you use gdk_window_get_user_data, parameter 2 of this function is a
gpointer*, so again you can't just do something like:
> GtkWidget *widget;
> GdkWindow *window;
>
> gdk_window_get_user_data(window, (gpointer*)&widget);
This is because a gpointer * is really void **, and GtkWidget is a
typedef to the structure _GtkWidget. As types, these are obviously
incompatible, however as pointers they're the "same" type (ie and
address to an area of memory)... however gcc it breaks gcc's
strict-aliasing rules. You can't cast a struct pointer to a gpointer*
without the warning (though your program will compile OK), but you can
cast a gpointer* to a GtkWidget*. Therefore, you need to do the following:
> GtkWidget *widget;
> GdkWindow *window;
>
> gpointer pwidget;
>
> gdk_window_get_user_data(window, &pwidget);
> widget = (GtkWidget*) &pwidget;
Obviously, these examples can be applied to other areas where you get
this warning.
Anyway, I hope this helps somewhat.
Chris
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]