ページ 11

int型変数を構成する4バイトのそれぞれの数値を計算で扱いたい

Posted: 2014年9月16日(火) 20:08
by wasawasa
こんにちは、何度もお世話になっております。
バイナリデータのファイルへの入出力を模索する中で、int変数を構成する4バイト分の数値の中の1~4バイト目の数値をC言語において別々に取り扱って計算する方法が無いか気になったので質問させて頂きました。
例えば、char a[5]の1~5バイト目の数値が

コード:

 1 2 3 4 5
┌─┬─┬─┬─┬─┐
│α│β│γ│δ│ε│
└─┴─┴─┴─┴─┘
となっていて、int b の1~4バイト目の数値が

コード:

 1 2 3 4
┌─┬─┬─┬─┐
│W │X │Y │Z │
└─┴─┴─┴─┘
となっている場合に、char c[5]の1~5バイト目に

コード:

   1    2    3    4   5
┌─────┬───┬─────┬──┬─────┐
│(α+W)%16 │|β-X|│(γ+α)%16│δ/Y│(ε*Z)%16 │
└─────┴───┴─────┴──┴─────┘
という数値を格納したい場合はどういう記述を行えばよいのでしょうか?
初歩的な事かもしれませんが、どなたかよろしくお願いします。

Re: int型変数を構成する4バイトのそれぞれの数値を計算で扱いたい

Posted: 2014年9月16日(火) 20:19
by box
char型はともかくとして、int型の場合、エンディアンによって
どのバイトにどういう値が入るかが異なります。

例えば、int型の1234567890という10桁の数値が
16進の499602D2
という形でメモリーに入っているとします。

このとき、低位アドレスを左側に書くと、
ビッグエンディアン:49 96 02 D2
リトルエンディアン:D2 02 96 49

このように、int型の第1バイト、という表現だけでは、エンディアンによって
結果が異なります。

どちらを想定されているでしょうか。

Re: int型変数を構成する4バイトのそれぞれの数値を計算で扱いたい

Posted: 2014年9月16日(火) 21:09
by みけCAT
1. 共用体を用いる

コード:

#include <stdio.h>

typedef union {
	int i;
	char c[4];
} int_char;

int main(void) {
	char a[5] = {0x12,0x34,0x56,0x78,0x9A};
	int b = 0xDEADBEEF;
	unsigned char W,X,Y,Z;

	int_char ic;
	/* エンディアンは関係なく、メモリ上の最初にあるバイトを1バイト目とする */
	ic.i = b;
	W = ic.c[0];
	X = ic.c[1];
	Y = ic.c[2];
	Z = ic.c[3];

	/* 素直に計算する */
	a[0]=(a[0]+W)%16;
	a[1]=a[1] >= X ? a[1]-X : X-a[1];
	a[2]=(a[2]+a[0])%16;
	a[3]=a[3]/Y;
	a[4]=(char)(((int)a[4]*(int)Z)%16);

	printf("%d %d %d %d %d\n",(int)a[0],(int)a[1],(int)a[2],(int)a[3],(int)a[4]);
	return 0;
}
2. ビット演算を用いる

コード:

#include <stdio.h>

int main(void) {
	char a[5] = {0x12,0x34,0x56,0x78,0x9A};
	int b = 0xDEADBEEF;
	unsigned char W,X,Y,Z;

	/* エンディアンに従い、数値の下位8ビットを1バイト目とする */
	W=b&0xFF;
	X=(b>>8)&0xFF;
	Y=(b>>16)&0xFF;
	Z=(b>>24)&0xFF;

	/* 素直に計算する */
	a[0]=(a[0]+W)%16;
	a[1]=a[1] >= X ? a[1]-X : X-a[1];
	a[2]=(a[2]+a[0])%16;
	a[3]=a[3]/Y;
	a[4]=(char)(((int)a[4]*(int)Z)%16);

	printf("%d %d %d %d %d\n",(int)a[0],(int)a[1],(int)a[2],(int)a[3],(int)a[4]);
	return 0;
}
どっちの方法がいいかは、用途に応じて考えてください。
オフトピック
そもそも、C言語ではint型が4バイトであるとは限りません。
例えば、LSI C-86 試食版におけるint型は2バイトでした。

Re: int型変数を構成する4バイトのそれぞれの数値を計算で扱いたい

Posted: 2014年9月19日(金) 14:01
by wasawasa
返信ありがとうございます。

boxさんにエンディアンについてご指摘頂いたので調べてみましたが、使用するCPU等によってデータの入力順が違うのですね。
私のPCではリトルエンディアンだったのでこちらを基準に考えていました。
みけCATさんに提示して頂いたシフト演算による方法は両方のエンディアンに対応できるっぽいのでこちらの方法で取り扱いたいと思います。

また、みけCATさんにintについてご指摘頂いたのでこちらも調べてみましたが、intの容量は時代に応じて2バイトから4バイトに変化したという話が見つかりました。
容量が変化し得る変数型でファイルを出力するのは不安なので、似たような話が見つからなかったlong型にint型の変数をキャストしてからシフト演算で扱いたいと思うのですが、long型が4バイト以外の容量で扱われている事はありますか?

Re: int型変数を構成する4バイトのそれぞれの数値を計算で扱いたい

Posted: 2014年9月19日(金) 15:25
by みけCAT
wasawasa さんが書きました:容量が変化し得る変数型でファイルを出力するのは不安なので、似たような話が見つからなかったlong型にint型の変数をキャストしてからシフト演算で扱いたいと思うのですが、long型が4バイト以外の容量で扱われている事はありますか?
あります。
64ビットのLinux環境では、long型が8バイトのことがあります。
long_test.png
long型のサイズが8バイトである環境
long_test.png (22.86 KiB) 閲覧数: 3752 回
現代のほとんどのパソコンではunsigned char型のサイズは8ビットだと思う[要出典]ので、
素直にunsigned char型でファイルを入出力するのがいいと思います。
もしくは、環境によって変数型の容量が変化するのが嫌いなのであれば、C言語ではなくJavaを用いるのがいいと思います。

Re: int型変数を構成する4バイトのそれぞれの数値を計算で扱いたい

Posted: 2014年9月19日(金) 17:13
by ISLe()
stdint.hが使えるのではあればですが、int32_tといった型を利用したら良いのではないでしょうか。


64ビット環境をターゲットにするとgccのlong intが8バイトになるのはウィンドウズでも同じですよ。

Re: int型変数を構成する4バイトのそれぞれの数値を計算で扱いたい

Posted: 2014年9月21日(日) 09:36
by wasawasa
返信ありがとうございます。
変数型って難しいですねぇ・・・。何となくjavaが流行る理由が分かったような気がしました。
とりあえず今はint32_tを使う事も視野に入れつつchar型で扱う事にしたいと思います。
皆さんありがとうございました。