2012-01-19 11 views
0

配列から単一の算術演算型への単一項目のキャスト(またはより多くの参照解除)を行うと、奇妙な動作に直面しています。配列のキャストの内容をCの算術型にキャスト

相続人減少したテストケース:具体的には

void test1() 
{ 
    unsigned char test[10] = {0}; 
    unsigned long i=0xffffffff; 

    *((unsigned long *)(&test[3])) = i; 


    int it; 

    for (it = 0 ; it < 10 ; it++) 

    { 
     printf("%02x ", test[it]); 

    } 
} 
void test2() 
{ 
    unsigned char test[10] = {0}; 
    unsigned char test2[10] = {0}; 
    test[2]=0xFF; 
    test[3]=0xFF; 

    *((unsigned short *)(&test2[1])) = *((unsigned short *)(&test[2])); 


    int it; 

    for (it = 0 ; it < 10 ; it++) 

    { 
     printf("%02x ", test2[it]); 

    } 
} 

それは主にこの表現である:

*((unsigned short *)(&test2[1])) 

私はいくつかの他のプラットフォーム(PIC24のような主に組み込みプラットフォーム)上でアクセス違反を取得しています。

私の質問は次のとおりです。このC準拠ですか?私はC標準の中で何かを見つけることができませんが、おそらく私は目が見えません。

このようなキャストなしで(バイト単位のコピー/アンローリングのルーピングなどは意味しません)、この操作を実行する代替手段を知っていますか?プラットフォームの現在のバイトオーダーを知る必要はありません。

ありがとうございます!

答えて

5
*((unsigned short *)(&test2[1])) 

これは未定義の動作です。アラインメントとエイリアシングの規則に違反しています。それをしないでください。

test2オブジェクトはunsigned charの配列であり、キャストによってunsigned shortというオブジェクトにアクセスしています。 unsigned charアライメント要件がunsigned shortアラインメント要件と同じであるという保証はありません。

Cの標準では、6.3.2.3p7(C99)と6.5p7のエイリアシング規則の情報を見つけることができます。

=オペレータの左側にキャストがある場合は、大体の注意が必要です。

+0

わかりましたが、時には本当に奇妙なことをする必要があります.Cがあなたにそれをさせるのは良いことです。私は通常、そのようなことで 'union'を好むことはありますが、ポインタキャストよりはるかにクリーンな解決策です。 – cha0site

3

*((unsigned long *)(&test[3])) = i;には未定義の動作があります。それはあなたのマシンのsizeof(long)とendiannessに依存します。 一般的には、異なるポインタタイプ間でキャストしないでください(void*を除く)。

1

ここで問題となるのは、アライメントされていないアクセスを実行していることです。文字が1バイトで短絡が2(可能性が高い)の場合は、奇数でwrite-short演算を行います。これは常にサポートされているわけではないため、アクセス違反の可能性が最も高い理由です。本当にこれをやりたければ(おそらくそうではないかもしれませんが)、char配列を前面に1文字長くしてから、最初のcharを使いません(配列を0ではなく1のインデックスとして扱います)それが保証されていないプラットフォームでも動作するでしょう。

+0

はい、問題になる可能性があります。しかし、それを '#pragma pack'で修正することができます。コンパイラは' char'sを2バイトの境界に揃えます。 – cha0site

+0

@ cha0site:私は '#pragma pack'はstruct要素のパッキングにのみ影響すると考えています - この場合、配列がある場合は適用されません –

+0

@PaulR:あなたが正しいと思われます。しかし、[__attribute__ aligned](http://gcc.gnu.org/onlinedocs/gcc-4.2.1/gcc/Variable-Attributes.html)のGCC拡張では、配列を揃えることができます。 – cha0site