6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【C++初心者】GetAsyncKeyState() の返り値をなぜ&1するのか

Last updated at Posted at 2020-05-21

前提

まず、僕はC++のクソ初心者です。
もちろん見る人が見れば「何言ってんだこのnoob」となるかと思いますが、自分の備忘録もかねて書きます。
今回GetAsyncKeyState()の返り値をなぜ&1するかがわからなかったため、自分でいろいろ試して判明した結果を書きます。

追記 !GetAsyncKeyState()と書くのも同様に動くみたいです。ていうかこっちのほうがよくみるカモ。

GetAsyncKeyStateってなんぞ

GetAsyncKeyState()とは<Windows.h>の中に定義されている関数で、引数にキーを渡すことで、そのキーが押されているかどうかを監視し、結果を戻り値で表してくれる便利な関数です。
渡せるキーにはいくつも種類があり、例えばF1キーなら引数にVK_F1を渡せばいいですし、insertならVK_INSERTを渡せばいいです。詳しくはMSDNを見てね

#何がわからなかったか
一般的には、この関数は以下のようにして使います。

while(true) { //括弧の中は別にtrueじゃなくてもいいです。
    if(GetAsyncKeyState(VK_F1) & 1) {
        //F1が押されたときの処理
        break;
    }
}

ifの中をよく見ると、GetAsyncKeyState()の戻り値に& 1して、その結果が0以外であればbreak、0であればループ続行という感じになってますよね。

&というのはビット演算でいうANDのことです。ビット演算は2進数に対して使う演算子ですが、ここでは説明を省くのでこのサイトとか見てみてください。

なぜこんなことになっているか。
GetAsyncKeyState()の戻り値についてMSDNで確認してみると、以下のように書いてあります。

If the function succeeds, the return value specifies whether the key was pressed since the last call to GetAsyncKeyState, and whether the key is currently up or down. If the most significant bit is set, the key is down, and if the least significant bit is set, the key was pressed after the previous call to GetAsyncKeyState.

これ曰く、GetAsyncKeyState()の戻り値は、二つのことを表すことができます。もし最上位のビットが1なら、そのキーが押されていることを表し、最下位のビットが1ならそのキーはGetAsyncKeyState()が呼ばれて初めて押されたことを表すようです。

最上位のビットと最下位のビットとは二進数で使われる用語で、

最上位のビット→00000000←最下位のビット

このように、一番右の数字を最上位のビット。一番左の数字を最下位のビットといいます。

よくわからなかったので、以下のコードで実際のGetAsyncKeyState()の戻り値を出力してみました。

#include <iostream>
#include <Windows.h>
#include <bitset>
int main()
{
    while (1) {
        short u = GetAsyncKeyState(VK_F1); //0x49はIキーをあらわす
        std::cout << std::bitset<32>(u) << std::endl; //uを二進数で表示するのだが、いろいろ試したところ、引数のビット幅は32っぽい
        if (u == 0xffff8000) { //11111111111111111000000000000000がきたら終了
            break;
        }
    }
    
}

このコードにたどり着く前に色々試した結果こうなったので、0xffff8000などは無視でいいです。
重要なのは戻り値を知ることです。
これを実行して、数秒してからF1を押すと、こんな結果になります

//上は無限に続く
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
11111111111111111000000000000001 
11111111111111111000000000000000

これを見ると、押した瞬間に11111111111111111000000000000001が戻り値として帰ってきて、その後11111111111111111000000000000000が帰ってきているのがわかります。

正直最上位ビットに関しては押されてる間ずっと1なので別にどうでもいいです。ていうか無視していい。
左のほうにある1の大群も、僕たちが知らなくていいものなので無視していい。注視すべきは最下位ビットです。

もちろん、このコードは1秒に何千、何万という回数押下を監視し続けるので、F1を押下している一瞬でも何千回と押下を検知し続けます。
そのなかで、一回目の検知時は最下位ビットを1、二回目以降の数千回は最下位を0にして戻り値を返すわけです。

##本題に戻ると

本題はなぜこの数値に & 1をするかです。
多分だけど、ここでいう1っていうのは最下位ビットが1ってのを意味します。なぜなら二進数で000000011のことだからね!
なので& 1ってのは戻り値の最下位ビットが1、つまり__一番初めに押下が検知された瞬間__を表すってこと。

なので、

if (GetAsyncKeyState(VK_F1) == 0xffff8001) { //長い二進数を条件にする方法がわからないので0xffff8001にしてますw
    break;
}

にしても別に同じ動きが想定できるはずです。ただこれだと、アップデートで戻り値の真ん中らへんの数値が変わった時に動かなくなっちゃうからみんな& 1を使うってわけですね!

10000000000000000000000000000001 
例えばこういう戻り値になったら詰む

#最後に

一気にぶわああああっと書いたので日本語が間違ってたりするかもしれません。
また、初心者なので何か間違ったことを言ってるかもしれません。もし致命的なミスをしていたらコメントいただけると幸いです。
32ビット幅などの長い二進数を==などの後につける方法も知ってる方いたら教えてくださいw!

6
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?