GCC编译参数strict aliasing详解

一. 看下面的代码,目的是用short指针交换一个int数据的高低两个short
$ cat tt.c
#include 
int main()
{
     int a = 0x12345678;
     short *p = (short *)&a;
     short temp;
     temp = *p;
     *p = *(p+1);
     *(p+1) = temp;
     printf("%x\n", a);
}
$ gcc   -O2   tt.c
$ ./a.out 
12345678
$ gcc -v
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.3.1-2' --with-bugurl=file:///usr/share/doc/gcc-4.3/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib 
--without-included-gettext --enable-threads=posix --enable-nls --with-gxx-include-dir=/usr/include/c++/4.3 --program-suffix=-4.3 --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-mpfr --enable-targets=all --enable-cld 
--enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu
Thread model: posix
gcc version 4.3.1 (Debian 4.3.1-2) 

上面的指令把环境啥的都揭示的很清楚。这段代码主要是交换一个整数的前一个字和后一个字,结果很奇怪是不,竟然没交换:D,是不是我土了代码写错?好,再看
$ gcc tt.c
$ ./a.out 
56781234

awesome!结果完全正确

二。分析
表面上立马看出结果不同的原因在于gcc编译时的参数不同,gcc -O2优化开启了很多优化选项,可以到gnu网站查的,其中有一项就是-fstrict-aliasing,这开启了aliasing规则
1.那么什么是aliasing规则呢?先看gcc对-fstrict-aliasing的解释
Allows the compiler to assume the strictest aliasing rulesapplicable to the language being compiled. For C (and C++), this activates optimizations based on the type of expressions. Inparticular, an object of one type is assumed never to reside at the sameaddress as an object of a different type, unless the types are almostthe same. For example, an unsigned int can alias an int, but not avoid* or a double. A character type may alias any other type.

再看c99标准对aliasing的
7 An object shall have its stored value accessed only by an lvalue
    expression that has one of the following types: {footnote 73}
      a type compatible with the effective type of the object,
      a qualified version of a type compatible with the effective type ofthe object,
      a type that is the signed or unsigned type corresponding to theeffective type of the object,
      a type that is the signed or unsigned type corresponding to aqualified version of the effective type of the object,
      an aggregate or union type that includes one of the aforementionedtypes among its members (including, recursively, a member of asubaggregate or contained union), or
      a character type.

 {footnote 73} The intent of this list is to specify those circumstancesin which an object may or may not be aliased.
gcc的说明更直白点,更容易懂。再看代码
short *p = (short *)&a;该语句中p是指向short的指针,&a是指向int的指针,这破坏了aliasing规则。

2.破坏aliasing规则为什么会出错呢?
这要从c99为啥提了这么个东东说起,在我理解中,不同类型的指针一定不会指向同一个内存位置会有两个好处,1是减少可能的load和store;2是编译器可以对指令做些调整优化。
出错就是因为开启了strict aliasing规则后,gcc认为*p不会指向&a所在的内存位置所以*p的操作和最后打印a没关系,指令可以reorder。实际上汇编结果表明正是如此
gcc -O2 -S tt.c后tt.s部分内容:
     movl     $305419896, 4(%esp) //可以看出直接送数据,根本不管*p的操作的
     movl     $.LC0, (%esp)
     call     printf

3.c99的restrict关键字
这个就不多说了。它起的作用是表明其修饰的指针不会被其它指针aliased扩展了strict aliasing
 
  

关键字restrict仅对指针有用,修饰指针,表明要修改这个指针所指向的数据区的内容,仅能通过该指针来实现,此关键字的作用是使编译器优化代码,生成更高效的汇编代码,“由restrict修饰的指针是最初唯一对指针所指向的对象进行存取的办法,仅当第二个指针基于第一个时,才能对对象进行存取.”

其实restrict同const或valiate一样是一个修饰符而已,告诉编译器被 restrict修饰的指针所指向的对象,只能通过这个指针或基于这个指针的其他指针进行操作,即限制访问用restrict限制的指针指向的对象只能通过这个指针访问,这对编译器的优化很有好处。  但要注意:restrict是C99中新增的关键字,在C89和C++中都不支持,在gcc中可以通过-std=c99来得到对它的支持。

4.对代码作者的影响 对新写的代码,要确保不违反这个规则,那么确实需要让不同指针指向同一个 内存位置怎么办?解决方案参考文献中写得很好,可以看看。当然有不少情形 下不违反这个规则非常麻烦,比如os的内核等特殊地方,那么 加入fno-strict aliasing参数,大家可以发现bsd内核、linux内核编译参数都加了这个 对于已有的代码,违反的地方非常多,那么可以加gcc的-fno-strict-aliasing 参数。 三。问题发现过程 我有时会把linux内核的部分代码用在应用层,linux内核编译时是加了 -fno-strict-aliasing这个参数的(内核编译时加V=1可以看到) 可应用的代码不一定,猜到是和内核的编译参数有关,最后没办法了,对于 所有的参数看着谁像,就去google,最后找到的 四。启发 os内核的编译参数都是各个大牛呕心沥血精心挑选的,选这个参数有他们的用 意,体会这种用意的同时,我们的水平也会提升。这让我想起曾经一款处理器 平台,它的linux模块加载老是不成功,找来找去最后还是找到了编译参数。那 是我第一次和编译参数做斗争:),现在看来和编译参数斗其乐无穷 ps:代码实验必需确保你编译器支持c99标准,而同时有打开和关闭aliasing规则的 参数开关,gcc3.X以后的就可以,微软的不清楚 两个比较好的参考文献 1. http://en.wikipedia.org/wiki/Aliasing_(computing) 2. http://www.cellperformance.com/mike_acton/2006/06/understanding_strict_aliasing.html

你可能感兴趣的:(Linux编译)