Deferencing type-punned pointers, and how to stop gcc giving you a warning



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]