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]