ビット演算と戻り値

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

ビット演算と戻り値

#1

投稿記事 by » 7年前

コード:

unsigned short get2 (unsigned char *s){
----略------
return s[0] << 8 | s[1];
}
戻り値の部分では一体どのような手順で、どのような演算が行われているのでしょうか?

また、sは1バイトの変数であるのにどうしてこの関数は2バイトの変数であるunsigned short型の値を返すことができるのでしょうか?

box
記事: 1745
登録日時: 9年前

Re: ビット演算と戻り値

#2

投稿記事 by box » 7年前

白 さんが書きました: また、sは1バイトの変数であるのにどうしてこの関数は2バイトの変数であるunsigned short型の値を返すことができるのでしょうか?
sは1バイト、というのは勘違いでありましょう。
sの型はunsigned char *です。
unsigned charではありません。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: ビット演算と戻り値

#3

投稿記事 by softya(ソフト屋) » 7年前

sはunsigned charポインタですので、s[0]なら確かに1バイトですがsは1バイトの変数ではありませんね。
さて、<<と言う演算子の意味はご存知でしょうか?上のポインタであると言うことと<<演算子が重要ポイントなのですが。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

記事: 26
登録日時: 9年前
住所: 新潟

Re: ビット演算と戻り値

#4

投稿記事 by » 7年前

s[0],s[1]・・・がそれぞれ1バイトのunsigned char型であるということを書きたかったのですが、あきらかに言葉不足でした。もうしわけありません。

<<演算子は 
aを変数,xを定数としたとき 
a << x はaをxビット分だけ左にずらす演算子だと思っています。
例・1バイトの変数
10011011(二進数表記)にたいして << 3とすると 11011000を返す

 ポインタであることをどのように利用されているのかは、分かりません。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: ビット演算と戻り値

#5

投稿記事 by softya(ソフト屋) » 7年前

白 さんが書きました:aを変数,xを定数としたとき 
a << x はaをxビット分だけ左にずらす演算子だと思っています。
例・1バイトの変数
10011011(二進数表記)にたいして << 3とすると 11011000を返す
いえ、1バイトの変数に対して<<3演算してもこうはなりません。
実際にprintfして確認してみて下さい。
白 さんが書きました:ポインタであることをどのように利用されているのかは、分かりません。
この場合のポインタは配列として処理しているだけなのですが、s[0]とs[1]は何処の値をそれぞれ何バイト取り出すことになるでしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

box
記事: 1745
登録日時: 9年前

Re: ビット演算と戻り値

#6

投稿記事 by box » 7年前

何となく、質問者さんが行ないたいことは↓こういうことではないかなぁ、などと勝手に想像しています。

コード:

#include <stdio.h>

int main(void)
{
    unsigned char c = 0x9b, d = c << 3;

    printf("%x %x\n", c, d);
    return 0;
}
だとすると、質問者さんが
白 さんが書きました: 例・1バイトの変数
10011011(二進数表記)にたいして << 3とすると 11011000を返す
こんな風に書かれたのは至極まっとうなことでありまして、
softya(ソフト屋) さんが書きました: いえ、1バイトの変数に対して<<3演算してもこうはなりません。
実際にprintfして確認してみて下さい。
これは筋が違うのではないかなぁ、などと勝手に想像したりしています。
まあ、想像がはずれているだけかもしれないですけれど。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

記事: 26
登録日時: 9年前
住所: 新潟

Re: ビット演算と戻り値

#7

投稿記事 by » 7年前

>>この場合のポインタは配列として処理しているだけなのですが、s[0]とs[1]は何処の値をそれぞれ何バイト取り出すことになるでしょうか?

メモリ:1|2|3|4|5|6|7|8|8|7|6|5|4|3|2|9 (右に向かってメモリ上のアドレスが大きくなっていく)
ポインタsが指し示すのが上記の1のアドレスだとすると、
s[0] = 1|2|3|4|5|6|7|8
s[1] = 8|7|6|5|4|3|2|9
のように、s[0],s[1]ともに1バイトずつ取り出す
 こういうことでしょうか?


>>いえ、1バイトの変数に対して<<3演算してもこうはなりません。
実際にprintfして確認してみて下さい。

確認用にプログラムを作ってはみたのですが、
・1バイトの変数
10011011(二進数表記)にたいして << 3とすると 11011000を返す
となってしまっています。
以下に確認用に作ったプログラムのソースを載せるので、問題点を教えてください。お願いします。

コード:

#include <stdio.h>

void put_2bit(unsigned char x);

int main(void){
	unsigned char a = 155;
	int i;

	int b = 0x1f3d;

	printf("%d\n",b);
	printf("%d\n\n",(b << 3));

	printf("%d\n",(a&0x00ff));
	printf("%d\n\n",((a << 3)&0x00ff));

	put_2bit(a);
	put_2bit(a << 3);

	return 0;
}

void put_2bit(unsigned char x){
	int i;

	for(i = 0;i < 8;i++){
		if(((x >> (7 - i))&1) == 1){
			putchar('1');
		}else{
			putchar('0');
		}
	}

	printf("\n");

	return ;
}

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: ビット演算と戻り値

#8

投稿記事 by softya(ソフト屋) » 7年前

白 さんが書きました:メモリ:1|2|3|4|5|6|7|8|8|7|6|5|4|3|2|9 (右に向かってメモリ上のアドレスが大きくなっていく)
ポインタsが指し示すのが上記の1のアドレスだとすると、
s[0] = 1|2|3|4|5|6|7|8
s[1] = 8|7|6|5|4|3|2|9
のように、s[0],s[1]ともに1バイトずつ取り出す
 こういうことでしょうか?
たしかに1バイトづつ取り出しますが、
1|2|3|4|5|6|7|8|8|7|6|5|4|3|2|9
はビット?バイト?のどちらを表しているんでしょうか?|区切り1つが1バイトですか?|区切り1つが1ビットだと0か1しか格納できないはずです。
バイト区切りなら、s[0]とs[1]の値を16進数で書いてみて下さい。
白 さんが書きました:10011011(二進数表記)にたいして << 3とすると 11011000を返す
となってしまっています。
以下に確認用に作ったプログラムのソースを載せるので、問題点を教えてください。お願いします。
わざわざ上位ビットをと0x00ffで削っているのは何故でしょう。

コード:

unsigned short get2 (unsigned char *s){
----略------
return s[0] << 8 | s[1];
}
この関数の何処の部分が該当するんでしょうか?
私には0x00ffに該当する部分は無いと思いますが。
戻り型はunsigned shortですので1バイトでは有りません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2645
登録日時: 9年前
連絡を取る:

Re: ビット演算と戻り値

#9

投稿記事 by ISLe » 7年前

質問にあるコードの左ビットシフト演算子の左オペランドはunsigned charですが、演算時にはint型に昇格します。
なのでunsigned charなのに上位にあふれたビットが残ります。
汎整数拡張とか整数拡張とか汎整数昇格で検索してみてください。

コード:

return s[0] << 8 | s[1];
の部分では
  1. s[0]がビットシフト演算子のオペランドとしてint型に昇格→ビットシフト演算子の結果はint型
  2. 上の結果を元に、ビット論理和演算子の右オペランドs[1]もint型に昇格→ビット論理和演算子の結果はint型
  3. 式全体ではint型の結果をunsigned short型に丸めて戻り値
という流れになります。

記事: 26
登録日時: 9年前
住所: 新潟

Re: ビット演算と戻り値

#10

投稿記事 by » 7年前

>> softya(ソフト屋) さん

>>メモリ:1|2|3|4|5|6|7|8|8|7|6|5|4|3|2|9 (右に向かってメモリ上のアドレスが大きくなっていく)
は、1ビット区切りです。16個の区切りをつけるのに番号を付れば楽に区切れたので本来記録できないはずの値を載せてしまいました。すみません。

メモリ:1|1|1|1|1|0|0|1|1|1|1|0|1|0|0|0(1bit単位で計16bit、右に向かってアドレスが大きくなる)
ポインタsが指し示すのが上記メモリの最左端とすると
2進数 16進数
s[0] = 1|1|1|1|1|0|0|1 0xf9
s[1] = 1|1|1|0|1|0|0|0 0xe8

これでいいのでしょうか?

>>わざわざ上位ビットをと0x00ffで削っているのは何故でしょう。
自分でもなぜ削ったのか分からなくなりました。昨夜の時点ではなにか目的があって削ったのだとは思うのですが・・・
 ここから先は推測でしかないのですが、

コード:

put_2bit(a);
put_2bit(a << 3);
で出力される値と

コード:

printf("%d\n",a);
printf("%d\n\n",a << 3);
で出力される値が違ったので無理やり合わせたのだと思います。


>>ISLeさん
上記と同じようにメモリ:1|1|1|1|1|0|0|1|1|1|1|0|1|0|0|0(1bit単位で計16bit、右に向かってアドレスが大きくなる)
ポインタsが指し示すのが上記メモリの最左端とすると
2進数 16進数
s[0] = 1|1|1|1|1|0|0|1 0xf9
s[1] = 1|1|1|0|1|0|0|0 0xe8
と設定すると

1. s[0](1|1|1|1|1|0|0|1) << 8 → 1|1|1|1|1|0|0|1|0|0|0|0|0|0|0|0 というように2バイトに昇格

2. 1で2バイトに昇格したので自動的にs[1]も2バイトのint型に昇格する
s[1]の昇格後= 0|0|0|0|0|0|0|0|1|1|1|0|1|0|0|0

3. 1|1|1|1|1|0|0|1|0|0|0|0|0|0|0|0 | 0|0|0|0|0|0|0|0|1|1|1|0|1|0|0|0
を計算することになり、最終的な結果は
1|1|1|1|1|0|0|1|1|1|1|0|1|0|0|0
となり、戻り値はunsigned short型で定義されているために、1|1|1|1|1|0|0|1|1|1|1|0|1|0|0|0を丸める(”x型に丸める”というのはx型が記録できる形にするということですか?)

コード:

return s[0] << 8 | s[1];
上記の処理はつまり、ポインタsからの連続する16ビットを2バイトの変数(unsigned short)に格納できるようにするということでしょうか?

beatle
記事: 1280
登録日時: 8年前
住所: 埼玉
連絡を取る:

Re: ビット演算と戻り値

#11

投稿記事 by beatle » 7年前

ISLe さんが書きました:質問にあるコードの左ビットシフト演算子の左オペランドはunsigned charですが、演算時にはint型に昇格します。
白 さんが書きました:1. s[0](1|1|1|1|1|0|0|1) << 8 → 1|1|1|1|1|0|0|1|0|0|0|0|0|0|0|0 というように2バイトに昇格

2. 1で2バイトに昇格したので自動的にs[1]も2バイトのint型に昇格する
ISLeさんは「int型に昇格」と言っています。2バイトになるとは言っていません。もちろん、int型が2バイトの機械では2バイトに昇格することになります。

それから、ビットごとにアドレスを振る考え方は良くないと思います。C言語で考えられる最小の単位はバイトですから、バイト単位でアドレスを考えて下さい。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: ビット演算と戻り値

#12

投稿記事 by softya(ソフト屋) » 7年前

書き忘れです。ややこしい話ですがunsigned short型とunsigned int型のバイト数は実は処理系依存なのですが、それを言っていると複雑なので一般的なPCの環境(x86系Linux/WindowsでVc++やgccを利用)と想定して話を進めます。
この場合unsigned short型は2バイトでunsigned int型は4バイトです。

int型に昇格(暗黙の型変換)については、char,short,intの計算時は無条件にint型に昇格します。この場合unsigned なのでunsigned ing型に昇格します。
白 さんが書きました:2. 1で2バイトに昇格したので自動的にs[1]も2バイトのint型に昇格する
なので中途半端に2バイトに昇格することはありません。それと昇格時にunsigned の場合は上位ビットは0と想定されます(これも処理系依存)。
白 さんが書きました:上記の処理はつまり、ポインタsからの連続する16ビットを2バイトの変数(unsigned short)に格納できるようにするということでしょうか?
2バイトの情報を得るという機能以外に無条件にビックエンディアンの形で値を得ることが出来ると言う点が重要です。
x86系CPUはリトルエンディアン系なので、こういう事をしないとビックエンディアンの値を得ることが出来ません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2645
登録日時: 9年前
連絡を取る:

Re: ビット演算と戻り値

#13

投稿記事 by ISLe » 7年前

暗黙の型変換に含まれる汎整数拡張(整数拡張,汎整数昇格とも)は、元がunsignedでもsigned int型で表現できる場合はsigned int型に変換します。

記事: 26
登録日時: 9年前
住所: 新潟

Re: ビット演算と戻り値

#14

投稿記事 by » 7年前

 環境を書き忘れていました。すみませんでした。
OS winXp 32bit
コンパイラ VC++2008
以下上記の環境を前提条件としてお願いします。

>>beatleさん
>>softya(ソフト屋)さん
>>ISLeさん
計算結果が
・signed int型で表せるならばsigned int型に昇格
・signed int型で表せずにunsigned int型で表せるならばunsigned int型に昇格
ということでしょうか?
もし上記のように昇格するとすると signed int型でもunsigned int型でも表せないものはどのような扱いになりますか?

コード:

unsigned int promote = 0xffff;

	printf("%d\n",promote);
	promote++;
	printf("%d\n",promote);
コードを書いて試してみたのですが、
65535
65536
と表示されました。unsigned int型は上記の環境では0~65535の範囲しか表せないはずなのに、どうして65536と表示されてしまうのですか?

コード:

	printf("%d\n",promote);
	printf("%d\n",promote+1);
ならば、unsigned int型では表せない結果がさらに昇格したのかとも思えるのですが・・・

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: ビット演算と戻り値

#15

投稿記事 by softya(ソフト屋) » 7年前

VC++のunsigned int型は4バイトです。4バイトですので数値の上限は2の32乗-1で4294967295です。
2バイトで試したいならunsigned short型ですね。

[補足]
C/C++は動的に変数の扱いが変わることはありません。なので変数が自動的に昇格することはありません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

記事: 26
登録日時: 9年前
住所: 新潟

Re: ビット演算と戻り値

#16

投稿記事 by » 7年前

printf("%d\n",sizeof(unsigned int));
で確認してみましたが4バイトでした。
また、読み返してみるとprintfのフォーマット指定しもunsigned intを表示させるなら%uでした。

コード:

	unsigned int promote = 0xffffffff;

	printf("%u\n",promote);
	promote += 1;
	printf("%u\n",promote);
	printf("size:%d\n",sizeof(unsigned int));
上記のようにして試してみたところ
4294967295
0
4
と出力されたので、unsigned int型以上に昇格することはないと受け取ってもいいのでしょうか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: ビット演算と戻り値

#17

投稿記事 by softya(ソフト屋) » 7年前

白 さんが書きました:上記のようにして試してみたところ
4294967295
0
4
と出力されたので、unsigned int型以上に昇格することはないと受け取ってもいいのでしょうか?
受け取って良いと思います。

調度良い記事があるので読んでみて下さい。

「汎整数拡張 - Wikipedia」
http://ja.wikipedia.org/wiki/%E6%B1%8E% ... 1%E5%BC%B5
「汎整数拡張 ‐ 通信用語の基礎知識」
http://www.wdic.org/w/TECH/%E6%B1%8E%E6 ... 1%E5%BC%B5
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

beatle
記事: 1280
登録日時: 8年前
住所: 埼玉
連絡を取る:

Re: ビット演算と戻り値

#18

投稿記事 by beatle » 7年前

白さんは、多分「ANSI C言語辞典」を一冊持っていると役立つだろうと思います。
汎整数拡張の話とか、ビット演算の話(符号あり整数にビット演算すると処理系定義になる話とか)も乗っています。

とオススメしようと思ったのですがもう絶版なのかな・・・?
新ANSI C言語辞典

ISLe
記事: 2645
登録日時: 9年前
連絡を取る:

Re: ビット演算と戻り値

#19

投稿記事 by ISLe » 7年前

わたしの回答は最初の質問のコードに対してであり、汎整数拡張もビットシフト演算子に付随して言及したものです。
暗黙の型変換のルールについて興味があるなら規格をあたってください。

便利とはいえないですけど日本工業標準調査会のサイトのJIS検索でC/C++プログラミング言語の規格を読むことができます。
JIS規格番号はCがX3010、C++がX3014です。

printfは可変個数引数を取るので汎整数拡張の対象になります。
暗黙の型変換については大ざっぱに言って自動的にサイズの大きい型に昇格する仕組みです。

コード:

	unsigned int u = 0xffffffff;
	printf("size:%d\n",(int)sizeof(u + 1LL));

記事: 26
登録日時: 9年前
住所: 新潟

Re: ビット演算と戻り値

#20

投稿記事 by » 7年前

>>softya(ソフト屋)さん
さっそく参考記事の方読んでみます。

>>beatleさん
「新ANSI C言語辞典」を次に書店を訪れたときに探してみます。

最初の質問内容からだんだんとずれていってしまい、もうしわけありませんでした。

 全くとっかかりのつかめなかった

コード:

unsigned short get2 (unsigned char *s){
----略------
return s[0] << 8 | s[1];
}
をどうにかこうにか迫って行けそうになってきたので、ちゃんと理解できているかを実際にコードを書いて確かめてみたいと思います。
 boxさん、softya(ソフト屋)さん、ISLeさん、beatleさん今回は本当にありがとうございました。自分一人では何を調べれば理解できるようになるのかすら分からず途方に暮れていました。ありがとうございまいした.

閉鎖

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