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."


> -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 - 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);
                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;

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.


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]