0

私は現在、未定義の動作に焦点を当てたc99で数週間働いています。ルールを尊重しながら、奇妙なコードをテストしたかったのです。結果は次のコードでした。

(変数名を許してください、私は道化師を食べました)

int main(int arg, char** argv)
{

    unsigned int uiDiffOfVars;
    int LegalPointerCast1, LegalPointerCast2, signedIntToRespectTheRules;
    char StartVar;//Only use to have an adress from where we can move on
    char *TheAccesingPointer;
    int iTargetOfPointeracces;

    iTargetOfPointeracces= 0x55555555;

    TheAccesingPointer = (char *) &StartVar;
    LegalPointerCast2 = (int) &StartVar;
    LegalPointerCast1 = (int) &iTargetOfPointeracces;

    if ((0x80000000 & LegalPointerCast2) != (0x80000000 & LegalPointerCast1))
    {
        //as im not sure in how far 
        //"— Apointer is converted to other than an integer or pointer type (6.5.4)." is treating unsigned integers,
        //im checking this way.
        printf ("try it on next machine!\r\n");
        return 1;
    }


    if ((abs (LegalPointerCast1) > abs (LegalPointerCast2)))
        uiDiffOfVars = abs (LegalPointerCast1) - abs (LegalPointerCast2);
    else
        uiDiffOfVars = abs (LegalPointerCast2) - abs (LegalPointerCast1);

    LegalPointerCast2 = (int) TheAccesingPointer;
    signedIntToRespectTheRules = abs ((int) uiDiffOfVars);

    if ((abs (LegalPointerCast1) > abs (LegalPointerCast2)))
        TheAccesingPointer = (char *)(LegalPointerCast2 + signedIntToRespectTheRules);
    else
        TheAccesingPointer = (char *)(LegalPointerCast2 - signedIntToRespectTheRules);

     printf ("%c\r\n", *TheAccesingPointer);//Will the output be an 'U' ?

    return 0;
}

したがって、このコードは未定義の動作です。私が所有していないメモリ領域にアクセスしていないか、初期化されていないメモリにアクセスしているかにかかわらず、異なる結果が得られます。(私の知る限り)

最初の重要なルールは、ポインターを追加または減算することは許可されていないため、配列の境界を離れることができませんでした。しかし、ポインターを整数にキャストすることは許可されています。そこで、必要に応じて計算できますね。

私の2番目の仮定は、有効なアドレスをポインターに割り当てることが許可されているため、この計算されたアドレスをポインターに割り当てるための有効な操作でした。私は char ポインターで動作しているので、char* は何でもエイリアスできるため、厳密なエイリアス規則が破られることもありません。

それで、これがUBを引き起こすという、どのルールが破られていますか?

単一の変数も「配列」として理解されるべきであり、私はこの規則を破っていますか?

— 配列オブジェクトまたは整数型へのポインタの加算または減算は、同じ配列オブジェクトまたはその直後を指していない結果を生成します (6.5.6)。

もしそうなら、私もこれをしてもいいですか?

int var;
int *ptr;
ptr = &var;
ptr = ptr + 1;

結果はほぼ確実に未定義の動作になるためです。MSVC2010でコンパイルすると、予想される「U」が出力されますが、clangとgccを使用するfreeBSDでは、最適化レベルに応じてかなり面白く、毎回異なる結果が得られます。(行動が定義されている限り、私の目にはあるべきではありません)。

では、この鼻のドラゴンの原因は何か考えはありますか?

4

2 に答える 2