C语言之Strict Aliasing(严格别名)

C语言之Strict Aliasing(严格别名)

alias(别名)

C语言的左值(lvalue)和右值(rvalue)的含义

以赋值符号 = 为界,= 左边的就是左值,= 右边就是右值。

比如:(1) int b = 3;(2) int a = b;第(2)行代码,a为左值,b为右值。

可以将 L-value 的 L, 理解成 Location,表示定位、地址。将 R-value 的 R 理解成 Read,表示读取数据,数据放在内存中,内存有两个基本的属性:内存地址和里面的数据。变量名编译之后,会映射成内存地址。看看a = b的含义。其实就是 将 b地址内存里面的数据,放到a地址内存中。

在 C 中,当多个左值 lvalue 指向同一个内存区域时,就会出现别名(alias),一个内存区域可以有很多别名。

strict aliasing(严格别名)

编译时,编译器会在不影响程序功能的情况下进行优化,比如指令重排等,所以执行的程序可能完全不是我们所写的程序,只是功能一样。当别名不受限制时,比如int指针也可以指向保存float的内存,每块内存的内容可能会被任何先前的内存存储所改变。导致编译器在优化时考虑每个指针都可能指向同一个内存,不敢轻易优化深怕影响程序功能,严重限制优化空间。比如

void add(long *a, int *b)
{
    *a += *b;
    *a += *b;
}
void add(long *a, int *b)
{
    *a += 2 * *b;
}

如果a和b不会指向同一块内存,那么两个函数功能一样,都是a=a+2b,但是如果a和b有可能指向同一块内存那么两个函数功能就不一样了,第一个变成a=4a,第二个还是a=a+2b。

所以为了更加方便编译优化引入严格别名规则和相关的编译选项

通过 -fstrict-aliasing选项(O2以上默认开启)让编译器认为代码中都是符合严格别名规则的,可以大胆的去优化, -fno-strict-aliasing反之,同时可以可以通过-Wstrict-aliasing选项告警不符合严格别名规则帮助开发者排查,对应的-Wno-strict-aliasing屏蔽告警

当开始-fstrict-aliasing编译选项时,如果代码违反严格别名规则,那么将是一种不可控的行为(undefined behavior),可能会编译出各种未知的问题。

strict aliasing rule(严格别名规则)

在 C11 标准(N1570)中的定义如下

  • a type compatible with the effective type of the object(相互兼容类型)
int a = 1;
int *b = &a;
  • a qualified version of a type compatible with the effective type of the object(相互兼容类型的限定版本)
int a = 1;
const int *b = &a;
  • a type that is the signed or unsigned type corresponding to the effective type of the object(对应的有无符号类型)
int a = 1;
unsigned int *b = (unsigned int *)&a;
  • a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object(对应的有无符号类型的限定版本)
int a = 1;
const unsigned int *b = (const unsigned int *)&a;
  • an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union)(聚合或union类型,其成员中包括上述类型之一)
struct {
    int a;
} aa = {0};
int *b = &aa;
  • a character type(字符类型)
int x = 65;
char *p = (char *)&x;//万能的字符指针

restrict 关键字

有时即使两个变量可能符合strict aliasing rule(严格别名规则),但我们保证他们不会指向同一片内存空间,可以通过restrict 关键字告诉编译器,变量独占内存块,不会有其他变量指向改内存块,以让编译器放手去优化。如下

void add(int *a, int *b)
{
    *a += *b;
    *a += *b;
}

如果不加restrict关键字,那么a和b可能指向同一块地址,所以编译器不会去优化,只会老实按顺序执行,但是如果我们通过关键字restrict告诉编译器,a和b不会指向同一块内存,那么编译可能会将其优化成如下

void add(int * restrict a, int * restrict b)
{
    *a += *b;
    *a += *b;
}
void add(int *a, int *b)
{
    *a += 2 * *b;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值