嚴格別名規則“-fstrict-aliasing”和“-fno-strict-aliasing”及類型雙關


 

-fstrict-aliasing表示啟用嚴格別名規則,“-fno-strict-aliasing表示禁用嚴格別名規則,當gcc的編譯優化參數為“-O2”、“-O3”和“-Os”時,默認會打開-fstrict-aliasing

 

什么是嚴格別名規則?gcc對嚴格別名的定義:

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.

即,編譯器假定相同的內存地址絕不會存放不同類型的數據,否則即破壞了嚴格別名規則。

 

別名的定義可理解為:同一內存地址有不同的名稱,比如:

int m = 0x20190101;

int* p1 = &m;

int *p2 = &m;

int *p3 = p2;

int n = m;

 

這里“&m”、“p1”、“p2”和“p3”均是同一內存地址的別名,但n不是,因此涉及嚴格別名,是和指針相關的。

 

下列代碼,如果使用“-O2”、“-O3”或“-Os”編譯,並且加不-fno-strict-aliasing,則“*s”的結果是未定義的,不同的編譯器可能產生不同的結果,即使同一編譯器也可能運行時結果不盡相同:

#include <stdio.h>

int main() {

    int m = 0x12345678;

    short* s = (short*)&m; // 使用C++的方式也不可:short* s = reinterpret_cast<short*>(&m);

    printf("%x\n", *s);

    return 0;

}

 

gcc-4.1.2上運行情況,可以看到每次結果都不相同:

> g++ --version

> g++ -g -o e e.cpp -O2

> ./e

6590

> ./e

590

> ./e

ffffb590

 

怎么解決嚴格別名問題?采用類型相關(type-punning),手段是采用聯合體union,比如下面這種類型相關的用法是安全的:

#include <stdio.h>

union X {

    int m;

    short s;

};

int main() {

    X x;

    x.m = 0x12345678;

    short s = x.s;

    printf("%x\n", s);

    return 0;

}

 

然而,下列用法仍然是不安全的(多版本gcc實測正常,也未有“dereferencing type-punned pointer will break strict-aliasing rules”編譯告警,但gcc手冊指出結果可能不符合預期):

#include <stdio.h>

union X {

    int m;

    short s;

};

int main() {

    X x;

    x.m = 0x12345678;

    short* s = &x.s;

    printf("%x\n", *s);

    return 0;

}

 

下列代碼的結果也是未定義的(多版本gcc實測也正常,同樣未有編譯告警,但gcc手冊指出結果是未定義的):

#include <stdio.h>

union X {

    int m;

    short s;

};

int main() {

    int m = 0x12345678;

    short s = ((X*)&m)->s;

    printf("%x\n", s);

    return 0;

}

 

如果擔心風險,可加上編譯參數“-fno-strict-aliasing,但這會阻止gcc相關的優化。不過下列別名總是安全的:

1) “unsigned int”別名“int”,其它類似

2) “char”別名其它任何類型

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM