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

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
wasawasa
記事: 94
登録日時: 11年前

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

#1

投稿記事 by wasawasa » 11年前

こんにちは、何度もお世話になっております。
バイナリデータのファイルへの入出力を模索する中で、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 │
└─────┴───┴─────┴──┴─────┘
という数値を格納したい場合はどういう記述を行えばよいのでしょうか?
初歩的な事かもしれませんが、どなたかよろしくお願いします。

box
記事: 2002
登録日時: 14年前

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

#2

投稿記事 by box » 11年前

char型はともかくとして、int型の場合、エンディアンによって
どのバイトにどういう値が入るかが異なります。

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

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

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

どちらを想定されているでしょうか。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

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

#3

投稿記事 by みけCAT » 11年前

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バイトでした。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

wasawasa
記事: 94
登録日時: 11年前

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

#4

投稿記事 by wasawasa » 11年前

返信ありがとうございます。

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

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

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

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

#5

投稿記事 by みけCAT » 11年前

wasawasa さんが書きました:容量が変化し得る変数型でファイルを出力するのは不安なので、似たような話が見つからなかったlong型にint型の変数をキャストしてからシフト演算で扱いたいと思うのですが、long型が4バイト以外の容量で扱われている事はありますか?
あります。
64ビットのLinux環境では、long型が8バイトのことがあります。
long_test.png
long型のサイズが8バイトである環境
long_test.png (22.86 KiB) 閲覧数: 3750 回
現代のほとんどのパソコンではunsigned char型のサイズは8ビットだと思う[要出典]ので、
素直にunsigned char型でファイルを入出力するのがいいと思います。
もしくは、環境によって変数型の容量が変化するのが嫌いなのであれば、C言語ではなくJavaを用いるのがいいと思います。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

ISLe()

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

#6

投稿記事 by ISLe() » 11年前

stdint.hが使えるのではあればですが、int32_tといった型を利用したら良いのではないでしょうか。


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

wasawasa
記事: 94
登録日時: 11年前

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

#7

投稿記事 by wasawasa » 11年前

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

閉鎖

“C言語何でも質問掲示板” へ戻る