2016-12-26 2 views
0

関数を使用すると、ある一定の長さのchar *パラメータを別の整数型のポインタとして解釈し、その変換されたポインタにアクセスする正当な方法がありますか?次の関数のプロトタイプ与え例えばchar *を読み取り専用操作のための別のプリミティブ型の配列として再解釈する正当な方法はありますか?

...それを行うには違法(UB)の方法の多くがあるように思える:

int32_t sum_32(char *a, int len); 
私はの方法があるかどうかを知りたいのですが

合法的に、次のコードと機能的に同等なものを書いて:もちろん

int32_t sum_32(char *a, int len) { 
    assert(len % 4 == 0); 
    int32_t total = 0; 
    for (int i = 0; i < len/4; i++) { 
     total += ((int32_t *)a)[i]; 
    } 
    return total; 
} 

を、ただ大きい値に再結合するためにシフトして文字サイズのアクセスにアクセスを打破するためにそれを行うための一つの方法は、(約いくつかの前提でe

int32_t sum_32(char *a, int len) { 
    assert(len % 4 == 0); 
    int32_t total = 0; 
    for (int i = 0; i < len; i += 4) { 
     int32_t val = (int32_t) 
         (a[i+0] << 0) + 
         (a[i+1] << 8) + 
         (a[i+2] << 16) + 
         (a[i+3] << 24) ; 
     total += val; 
    } 
    return total; 
} 

...しかし、ここで私は一度に基になる配列1 int32_tにアクセスするソリューションを探しています:ndianness、ここLE)を仮定。

char *aのソースが割り当て機能であることを知っている場合、答えは「変更できません」、またはより広義にはaに追加できる制限がありますより大きなタイプは正当なものですか?

+0

最初の問題はおそらく整列です。割り当て関数から来た場合は、少なくとも、それが何かのために適切に整列されていることを知っています。 – melpomene

+0

stdlibのGNU実装を見てみることをお勧めします。文字列関数には、この種の単語単位の処理が多数含まれています。 (整列が行われた後はもちろん) – wildplasser

+1

すでに書いたように、整列とエンディアンに注意する必要があります。たとえば、[この回答](http://stackoverflow.com/a/4840428/69809)には、4または8を法とするポインタ・アドレスを四捨五入するための2つの関数があります。異なるエンディアンを扱う必要がある場合は、同様に個々のバイトを扱うこれは「パフォーマンス」というタグが付けられているので、あまりにも早く最適化していないと確信していますか?後者の関数は、シフト、btwを持っていません。 – Groo

答えて

2

最後にメモリがint32_tまたは互換性のあるタイプの場合、有効なタイプはint32_tとなり、簡単なキャストで読み取ることができます。それ以外の場合、エイリアシング規則を破らなければ不可能です。コンパイラは、実際にmemcpyライブラリ関数を呼び出さないために最適化されます

int32_t temp; 
memcpy(&temp, a+i*4, sizeof temp); 
total += temp; 

:厳格なエイリアシングの問題を回避するために

+0

メモリ領域への最後の書き込みがファイルやソケットなどの 'fread'のようなものであった場合はどうでしょうか?そして、私は "最後の書き込み"について何も仮定することはできません。 – BeeOnRope

+0

また、 'char *'のエイリアシング規則にエスケープハッチはありませんか? – BeeOnRope

+3

はい、すべての型を 'char * 'でエイリアスできますが、その逆はできません。 – alain

2

は、total += ((int32_t *)a)[i];を置き換えることができます。もちろん、意図したエンディアンの意味が必要な場合にのみ、これを使用してください。それ以外の場合はビットシフトバージョンを使用してください。

(注:質問に書かれているように、charが署名されている可能性があるため、ビットシフトバージョンは間違っています - 機能をunsigned char *に変更するか、同等のキャストを使用する必要があります)。

compiler explorerを使用し、このコードではaがアライメントされているかどうかをテストし、そうであればXMM命令を使用し、そうでない場合は古い命令を使用します。

+0

そして*もう一度*はlibc実装の力です。インライン展開?問題ない! – wildplasser

+0

@wildplasser libcの実装は何ですか? –

+0

なぜ '* 4'ですか? 'memcpy(&temp、a + i * 4、4);と矛盾しないようにしてください。 – chux

関連する問題