7921


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

[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として扱いたいマシンはリトルエンディアンマシン。。でしたが、この質問を書き、回答を読んで将来ビッグエンディアンで使う可能性もあると思い直しました。  
なので、ビットシフトなどは今回の用途では使わない方向でいきます。


CC BY-SA 3.0
2015-03-13T13:21:32.157
7884
2015-08-21T10:17:50.423
2015-03-13T15:55:05.570
3496
3496
question
c++ c
char配列から指定バイトだけ切り出したい(memcpyなど使わずに)


たぶんこれでいけると思います。

[code]

    int i[3];
    i[0] = c[0];
    i[1] = c[1];
    i[2] = c[2];
    
[/code]


CC BY-SA 3.0
2015-03-13T13:58:04.553
7885
2015-03-20T00:01:01.520
2015-03-20T00:01:01.520
440
8266
7884
answer


3バイト`memcpy`するのと同等のコードです。

[code]

    int n = 0;
    char cc[4] = { 0xe8, 0x03, 0x00, 0x00 };
    
    char *p = (char*)&n;
    p[0] = cc[0];
    p[1] = cc[1];
    p[2] = cc[2];
    
[/code]


CC BY-SA 3.0
2015-03-13T14:15:30.147
7886
2015-03-13T14:15:30.147
3639
7884
answer

[code]

    int n = (*(int *)&c[i]) & 0xffffff;
    
[/code]

で、任意のiの位置から3バイトをintで取得できますが、どうでしょう？

但し、最後の3バイトを取り出す場合があると、これはオーバーランしてしまって使えませんが。


CC BY-SA 3.0
2015-03-13T14:27:02.823
7888
2015-03-13T14:33:38.910
2015-03-13T14:33:38.910
7900
7900
7884
answer


memcpy()を避ける意義はなぜでしょう？  
というのも今どきのコンパイラはmemcpy()の意味を知っていて直接インライン展開してしまいます。複雑な記述をするよりはmemcpy()でシンプルに記述した方がいいように思います。


CC BY-SA 3.0
2015-03-13T14:41:47.987
7889
2015-03-13T14:41:47.987
4236
7884
answer


設計の都合上、後で必ずバイトオーダー変換関数を通さなければならない場合は 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]


CC BY-SA 3.0
2015-03-13T14:56:54.780
7890
2015-03-13T15:47:36.037
2015-03-13T15:47:36.037
7831
7831
7884
answer


一旦都合の良い形にコピーしてから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]

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


CC BY-SA 3.0
2015-03-14T19:45:16.457
7921
2015-03-14T19:45:16.457
3783
7884
answer


過去に似たような事をした人がPRJにいて奇々怪々なセグメンテーション違反が発生して2日間不具合に悩まされたことがありました。  
ほかの方が書いているようにint境界にないと動作は未定義ですから`ntohl()`したうえでビットシフトとビットマスクすることがビッグエンディアン等プラットフォーム依存しないコードの記述だと思います。


CC BY-SA 3.0
2015-03-19T17:05:35.257
8152
2015-03-19T17:05:35.257
7966
7884
answer


面白そうな課題「mem～系の関数を使わずに文字列リテラルのビット列が示す整数値を表示せよ」として書いてみました。自己満足ですが何かのお役に立てればと思います。

[code]

    #include<stdio.h>
    #include<stdint.h>
    /*////////////////////////////////////////////////////
     * ミドルエンディアンまで対応する場合は
     * uint16_tを使って判定しリトルエンディアンと似た処理になるかと。
     * どこまで正常に動くかは未確認です。
     * ベストアンサーが決まったあとの回答を見てからの回答なのでちょっとせこいかもしれない
     * 質問の意図と大きく異っていたら申し訳ない
    /*////////////////////////////////////////////////////
    
    int main(int argc, char** argv) {
        char c[]   = "Hello world!";
        int num[2] = {0}; // 
        int x = 1, i;
    
        //ここでバイト数が変更可能
        int isize = sizeof (int) / sizeof (char);
    
        int nsize = sizeof (num) / sizeof (num[0]);
        int csize = sizeof (c) / sizeof (c[0]);
    
        if (csize > nsize*isize) {
            if (*(char*) &x) {
                puts("little?");
                int j = csize;
                for (i = 0; i < nsize; i++)
                    for (j = 0; j < isize; j++)
                        *(((unsigned char*) &num[i])+isize-j-1) = c[i*isize+j];
            } else {
                // これについてはデバッグできない。
                puts("big?");
                for (i = 0; i < nsize; i++) {
                    num[i] = *(int*) &c[isize * i];
                }
            }
    
            for (i = 0; i < nsize; i++)printf("%d\n", num[i]);
            for (i = 0; i < csize; i++)printf("%c", c[i]);
        }
        return 0;
    }
    
[/code]

純粋なコメントとして  
/*////////////////////////////////////////////////////  
* チェックサムプログラムでエンディアンを気にしないとの記述を見たので調べたが  
* 全く関係がない内容だった。  
* こういったデータ構造まで気にしてプログラムを書くのは  
* 通信、OS、マイコン,ドライバ?関連のプログラマーだとおもう  
* (もしくは、コンパイラを作っているのかもしれない?)  
* いつもよりレベルが高くとても勉強になった。  
/*////////////////////////////////////////////////////


CC BY-SA 3.0
2015-08-21T10:17:50.423
14702
2015-08-21T10:17:50.423
10455
7884
answer