ビット演算と戻り値
Re: ビット演算と戻り値
sは1バイト、というのは勘違いでありましょう。白 さんが書きました: また、sは1バイトの変数であるのにどうしてこの関数は2バイトの変数であるunsigned short型の値を返すことができるのでしょうか?
sの型はunsigned char *です。
unsigned charではありません。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。
プログラムは思ったとおりには動かない。書いたとおりに動く。
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 13年前
- 住所: 東海地方
- 連絡を取る:
Re: ビット演算と戻り値
sはunsigned charポインタですので、s[0]なら確かに1バイトですがsは1バイトの変数ではありませんね。
さて、<<と言う演算子の意味はご存知でしょうか?上のポインタであると言うことと<<演算子が重要ポイントなのですが。
さて、<<と言う演算子の意味はご存知でしょうか?上のポインタであると言うことと<<演算子が重要ポイントなのですが。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: ビット演算と戻り値
s[0],s[1]・・・がそれぞれ1バイトのunsigned char型であるということを書きたかったのですが、あきらかに言葉不足でした。もうしわけありません。
<<演算子は
aを変数,xを定数としたとき
a << x はaをxビット分だけ左にずらす演算子だと思っています。
例・1バイトの変数
10011011(二進数表記)にたいして << 3とすると 11011000を返す
ポインタであることをどのように利用されているのかは、分かりません。
<<演算子は
aを変数,xを定数としたとき
a << x はaをxビット分だけ左にずらす演算子だと思っています。
例・1バイトの変数
10011011(二進数表記)にたいして << 3とすると 11011000を返す
ポインタであることをどのように利用されているのかは、分かりません。
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 13年前
- 住所: 東海地方
- 連絡を取る:
Re: ビット演算と戻り値
いえ、1バイトの変数に対して<<3演算してもこうはなりません。白 さんが書きました:aを変数,xを定数としたとき
a << x はaをxビット分だけ左にずらす演算子だと思っています。
例・1バイトの変数
10011011(二進数表記)にたいして << 3とすると 11011000を返す
実際にprintfして確認してみて下さい。
この場合のポインタは配列として処理しているだけなのですが、s[0]とs[1]は何処の値をそれぞれ何バイト取り出すことになるでしょうか?白 さんが書きました:ポインタであることをどのように利用されているのかは、分かりません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: ビット演算と戻り値
何となく、質問者さんが行ないたいことは↓こういうことではないかなぁ、などと勝手に想像しています。
だとすると、質問者さんが
まあ、想像がはずれているだけかもしれないですけれど。
#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して確認してみて下さい。
まあ、想像がはずれているだけかもしれないですけれど。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。
プログラムは思ったとおりには動かない。書いたとおりに動く。
Re: ビット演算と戻り値
>>この場合のポインタは配列として処理しているだけなのですが、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を返す
となってしまっています。
以下に確認用に作ったプログラムのソースを載せるので、問題点を教えてください。お願いします。
メモリ: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
- 登録日時: 13年前
- 住所: 東海地方
- 連絡を取る:
Re: ビット演算と戻り値
たしかに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|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進数で書いてみて下さい。
わざわざ上位ビットをと0x00ffで削っているのは何故でしょう。 この関数の何処の部分が該当するんでしょうか?白 さんが書きました:10011011(二進数表記)にたいして << 3とすると 11011000を返す
となってしまっています。
以下に確認用に作ったプログラムのソースを載せるので、問題点を教えてください。お願いします。
私には0x00ffに該当する部分は無いと思いますが。
戻り型はunsigned shortですので1バイトでは有りません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: ビット演算と戻り値
質問にあるコードの左ビットシフト演算子の左オペランドはunsigned charですが、演算時にはint型に昇格します。
なのでunsigned charなのに上位にあふれたビットが残ります。
汎整数拡張とか整数拡張とか汎整数昇格で検索してみてください。
の部分では
なのでunsigned charなのに上位にあふれたビットが残ります。
汎整数拡張とか整数拡張とか汎整数昇格で検索してみてください。
の部分では
- s[0]がビットシフト演算子のオペランドとしてint型に昇格→ビットシフト演算子の結果はint型
- 上の結果を元に、ビット論理和演算子の右オペランドs[1]もint型に昇格→ビット論理和演算子の結果はint型
- 式全体ではint型の結果をunsigned short型に丸めて戻り値
Re: ビット演算と戻り値
>> 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で削っているのは何故でしょう。
自分でもなぜ削ったのか分からなくなりました。昨夜の時点ではなにか目的があって削ったのだとは思うのですが・・・
ここから先は推測でしかないのですが、 で出力される値と で出力される値が違ったので無理やり合わせたのだと思います。
>>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型が記録できる形にするということですか?)
上記の処理はつまり、ポインタsからの連続する16ビットを2バイトの変数(unsigned short)に格納できるようにするということでしょうか?
>>メモリ: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で削っているのは何故でしょう。
自分でもなぜ削ったのか分からなくなりました。昨夜の時点ではなにか目的があって削ったのだとは思うのですが・・・
ここから先は推測でしかないのですが、 で出力される値と で出力される値が違ったので無理やり合わせたのだと思います。
>>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型が記録できる形にするということですか?)
上記の処理はつまり、ポインタsからの連続する16ビットを2バイトの変数(unsigned short)に格納できるようにするということでしょうか?
Re: ビット演算と戻り値
ISLe さんが書きました:質問にあるコードの左ビットシフト演算子の左オペランドはunsigned charですが、演算時にはint型に昇格します。
ISLeさんは「int型に昇格」と言っています。2バイトになるとは言っていません。もちろん、int型が2バイトの機械では2バイトに昇格することになります。白 さんが書きました: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型に昇格する
それから、ビットごとにアドレスを振る考え方は良くないと思います。C言語で考えられる最小の単位はバイトですから、バイト単位でアドレスを考えて下さい。
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 13年前
- 住所: 東海地方
- 連絡を取る:
Re: ビット演算と戻り値
書き忘れです。ややこしい話ですがunsigned short型とunsigned int型のバイト数は実は処理系依存なのですが、それを言っていると複雑なので一般的なPCの環境(x86系Linux/WindowsでVc++やgccを利用)と想定して話を進めます。
この場合unsigned short型は2バイトでunsigned int型は4バイトです。
int型に昇格(暗黙の型変換)については、char,short,intの計算時は無条件にint型に昇格します。この場合unsigned なのでunsigned ing型に昇格します。
x86系CPUはリトルエンディアン系なので、こういう事をしないとビックエンディアンの値を得ることが出来ません。
この場合unsigned short型は2バイトでunsigned int型は4バイトです。
int型に昇格(暗黙の型変換)については、char,short,intの計算時は無条件にint型に昇格します。この場合unsigned なのでunsigned ing型に昇格します。
なので中途半端に2バイトに昇格することはありません。それと昇格時にunsigned の場合は上位ビットは0と想定されます(これも処理系依存)。白 さんが書きました:2. 1で2バイトに昇格したので自動的にs[1]も2バイトのint型に昇格する
2バイトの情報を得るという機能以外に無条件にビックエンディアンの形で値を得ることが出来ると言う点が重要です。白 さんが書きました:上記の処理はつまり、ポインタsからの連続する16ビットを2バイトの変数(unsigned short)に格納できるようにするということでしょうか?
x86系CPUはリトルエンディアン系なので、こういう事をしないとビックエンディアンの値を得ることが出来ません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: ビット演算と戻り値
暗黙の型変換に含まれる汎整数拡張(整数拡張,汎整数昇格とも)は、元がunsignedでもsigned int型で表現できる場合はsigned int型に変換します。
Re: ビット演算と戻り値
環境を書き忘れていました。すみませんでした。
OS winXp 32bit
コンパイラ VC++2008
以下上記の環境を前提条件としてお願いします。
>>beatleさん
>>softya(ソフト屋)さん
>>ISLeさん
計算結果が
・signed int型で表せるならばsigned int型に昇格
・signed int型で表せずにunsigned int型で表せるならばunsigned int型に昇格
ということでしょうか?
もし上記のように昇格するとすると signed int型でもunsigned int型でも表せないものはどのような扱いになりますか?
コードを書いて試してみたのですが、
65535
65536
と表示されました。unsigned int型は上記の環境では0~65535の範囲しか表せないはずなのに、どうして65536と表示されてしまうのですか? ならば、unsigned int型では表せない結果がさらに昇格したのかとも思えるのですが・・・
OS winXp 32bit
コンパイラ VC++2008
以下上記の環境を前提条件としてお願いします。
>>beatleさん
>>softya(ソフト屋)さん
>>ISLeさん
計算結果が
・signed int型で表せるならばsigned int型に昇格
・signed int型で表せずにunsigned int型で表せるならばunsigned int型に昇格
ということでしょうか?
もし上記のように昇格するとすると signed int型でもunsigned int型でも表せないものはどのような扱いになりますか?
コードを書いて試してみたのですが、
65535
65536
と表示されました。unsigned int型は上記の環境では0~65535の範囲しか表せないはずなのに、どうして65536と表示されてしまうのですか? ならば、unsigned int型では表せない結果がさらに昇格したのかとも思えるのですが・・・
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 13年前
- 住所: 東海地方
- 連絡を取る:
Re: ビット演算と戻り値
VC++のunsigned int型は4バイトです。4バイトですので数値の上限は2の32乗-1で4294967295です。
2バイトで試したいならunsigned short型ですね。
[補足]
C/C++は動的に変数の扱いが変わることはありません。なので変数が自動的に昇格することはありません。
2バイトで試したいならunsigned short型ですね。
[補足]
C/C++は動的に変数の扱いが変わることはありません。なので変数が自動的に昇格することはありません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: ビット演算と戻り値
printf("%d\n",sizeof(unsigned int));
で確認してみましたが4バイトでした。
また、読み返してみるとprintfのフォーマット指定しもunsigned intを表示させるなら%uでした。
上記のようにして試してみたところ
4294967295
0
4
と出力されたので、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
- 登録日時: 13年前
- 住所: 東海地方
- 連絡を取る:
Re: ビット演算と戻り値
受け取って良いと思います。白 さんが書きました:上記のようにして試してみたところ
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(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: ビット演算と戻り値
白さんは、多分「ANSI C言語辞典」を一冊持っていると役立つだろうと思います。
汎整数拡張の話とか、ビット演算の話(符号あり整数にビット演算すると処理系定義になる話とか)も乗っています。
とオススメしようと思ったのですがもう絶版なのかな・・・?
新ANSI C言語辞典
汎整数拡張の話とか、ビット演算の話(符号あり整数にビット演算すると処理系定義になる話とか)も乗っています。
とオススメしようと思ったのですがもう絶版なのかな・・・?
新ANSI C言語辞典
Re: ビット演算と戻り値
わたしの回答は最初の質問のコードに対してであり、汎整数拡張もビットシフト演算子に付随して言及したものです。
暗黙の型変換のルールについて興味があるなら規格をあたってください。
便利とはいえないですけど日本工業標準調査会のサイトのJIS検索でC/C++プログラミング言語の規格を読むことができます。
JIS規格番号はCがX3010、C++がX3014です。
printfは可変個数引数を取るので汎整数拡張の対象になります。
暗黙の型変換については大ざっぱに言って自動的にサイズの大きい型に昇格する仕組みです。
暗黙の型変換のルールについて興味があるなら規格をあたってください。
便利とはいえないですけど日本工業標準調査会のサイトのJIS検索でC/C++プログラミング言語の規格を読むことができます。
JIS規格番号はCがX3010、C++がX3014です。
printfは可変個数引数を取るので汎整数拡張の対象になります。
暗黙の型変換については大ざっぱに言って自動的にサイズの大きい型に昇格する仕組みです。
Re: ビット演算と戻り値
>>softya(ソフト屋)さん
さっそく参考記事の方読んでみます。
>>beatleさん
「新ANSI C言語辞典」を次に書店を訪れたときに探してみます。
最初の質問内容からだんだんとずれていってしまい、もうしわけありませんでした。
全くとっかかりのつかめなかった をどうにかこうにか迫って行けそうになってきたので、ちゃんと理解できているかを実際にコードを書いて確かめてみたいと思います。
boxさん、softya(ソフト屋)さん、ISLeさん、beatleさん今回は本当にありがとうございました。自分一人では何を調べれば理解できるようになるのかすら分からず途方に暮れていました。ありがとうございまいした.
さっそく参考記事の方読んでみます。
>>beatleさん
「新ANSI C言語辞典」を次に書店を訪れたときに探してみます。
最初の質問内容からだんだんとずれていってしまい、もうしわけありませんでした。
全くとっかかりのつかめなかった をどうにかこうにか迫って行けそうになってきたので、ちゃんと理解できているかを実際にコードを書いて確かめてみたいと思います。
boxさん、softya(ソフト屋)さん、ISLeさん、beatleさん今回は本当にありがとうございました。自分一人では何を調べれば理解できるようになるのかすら分からず途方に暮れていました。ありがとうございまいした.