char配列から指定バイトだけ切り出したい(memcpyなど使わずに)


この配列に何かバイナリデータが入っているとします。

[code]

    char c[9]
    
[/code]

これの先頭３バイトをintの変数に入れたいのですが、mem～系の関数を使わずに実現することは可能ですか？  
ちなみに４バイト目以降は無傷で残しておきたいです。

【追記】  
意味の分かりづらい質問ですいません。  
このような感じです。エンディアンに関してはバイトオーダーの関数は後で使って調整できるものとします。なのでとにかくintに入れれば大丈夫です。

[code]

    int n = 0;
    char cc[4] = { 0xe8, 0x03, 0x00, 0x00 }; //数字の1000
    memcpy(&n, cc, 3); // 3バイト取り出して1000を復元する
    
[/code]

回答していただいた方々、ありがとうございます。  
sanadanさんのコードをベースに下記の方法で数値を復元することができました。

[code]

    char c[4] = {0x00,0x00,0x06,0x00};  // int6のネットワークバイトオーダー(3バイト目まで)
    int x = (*(int *)c) & 0xffffff;  // この時点で0x00,0x06,0x00,0x00になる
    x = x << 8;  // 3桁しか使わない
    x = ntohl(x); // オーダー解決
    
[/code]

yoh2さんの方法を使えばバイトオーダーも同時に解決できそうですね。

ご回答していただいた通り、このようなことをするよりも素直にmemcpyを利用しようかとも思っています。

【さらに追記】  
よく考えたら任意の場所から取り出して最後の3バイトを取り出す場合もありそうなので、やはり上記コードは使えなさそうです。

最初の意図を明確にしておくと、  
インプットデータはネットワークバイトオーダーのバイト配列でした。  
それの3バイト目までをintとして扱いたいマシンはリトルエンディアンマシン。。でしたが、この質問を書き、回答を読んで将来ビッグエンディアンで使う可能性もあると思い直しました。  
なので、ビットシフトなどは今回の用途では使わない方向でいきます。




一旦都合の良い形にコピーしてからntohlするのはいかがでしょうか。

[code]

    char c[4] = {0x00,0x00,0x06,0x00};
    char d[4] = {0x00};
    memcpy(d + 1, c, 3);               // => {0x00,0x00,0x00,0x06}
    x = ntohl(*(uint32_t *)d);         // => 6
    
[/code]

すでに検討済みであったり、そもそもコピー先のメモリが惜しいという可能性がありますが、念のため投稿します。




設計の都合上、後で必ずバイトオーダー変換関数を通さなければならない場合は h2so5 さんのコード。  
その必要がなければ、最初から望みの値を得られるよう、以下のようにすればいいと思います。

[code]

    /* CHAR_BIT = 8 と仮定してます */
    char cc[] = ...;
    
    int n = ((unsigned char)cc[0] <<  0) | ((unsigned char)cc[1] << 8) |
            ((unsigned char)cc[2] << 16);
    
[/code]

3バイトのみのパターンだけではなく、`0` 〜 `sizeof(int)` バイトまでのパターンが有り得るならこんな感じで。

[code]

    char cc[] = ...;
    int m = ...; /* 先頭から何バイト取得したいか */
    
    int n = 0;
    for(size_t i = 0; i < m; i++)
    {
        n |= (unsigned char)cc[i] << (i * 8)
    }
    
[/code]

[追記]  
`0xe8, 0x03, 0x00, 0x00` の列から `1000` を取り出したいとのことでしたので、 `char`
配列に入っている値はリトルエンディアンであると思っていましたが、追記された

> `char c[4] = {0x00,0x00,0x06,0x00}; // int6のネットワークバイトオーダー(3バイト目まで)`

を見ると、ビッグエンディアン (= ネットワークバイトオーダー) のパターンもありそうです。  
また、それ以降のコードからは `0x11, 0x22, 0x33, 0x44` からネットワークバイトオーダーで3バイトとは、 `0x11223300`
(`0x11223344` の 下位1バイトをマスク) ではなく、 `0x00112233` (`0x112233` のみ取ってくる)
を意図しているように見えます。  
その場合ならこうですね。

バイト数が3固定:

[code]

    /* CHAR_BIT = 8 と仮定してます */
    char cc[] = ...;
    
    int n = ((unsigned char)cc[0] << 16) | ((unsigned char)cc[1] << 8) |
            ((unsigned char)cc[2] <<  0);
    
[/code]

バイト数が可変:

[code]

    char cc[] = ...;
    int m = ...; /* 先頭から何バイト取得したいか */
    
    int n = 0;
    for(size_t i = 0; i < m; i++)
    {
        n |= (unsigned char)cc[i] << ((m - i - 1) * 8)
    }
    
[/code]

