- 競技プログラミングでは、std::cin で値を入力するのが一般的である
- 問題の制約上、小さな値 (例: 0 < N < 100 とか)は、uint8_t で扱えそう、と思ったりする人がいるかもしれないが、ここに罠がある
- uint8_t な変数に std::cin すると、ビットパターンは char になる。少なくとも gcc (libstdc++) の環境では。
- 他の処理系だと異なる動きをするかもしれない
- なので、数値と比較すると思ったように動作しない
- uint8_t な変数に std::cin すると、ビットパターンは char になる。少なくとも gcc (libstdc++) の環境では。
以下のコードを g++ -std=c++11 test.cxx
としてコンパイルし
$ echo "1" | ./a.out
としてみると良い。尚、 abi::__cxa_demangle
は gcc 限定のAPIである。
#include <bitset>
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
void print_typename(const std::type_info &id) {
int stat;
char *name = abi::__cxa_demangle(id.name(), 0, 0, &stat);
if(name != NULL) {
if (stat == 0) {
std::cout << name << std::endl;
}
free(name);
}
}
int main(int argc, char *argv[]) {
//
// 通常の uint8_t (unsigned char) だと、ビット列は数字として扱われる
//
uint8_t x = 1;
print_typename(typeid(x)); // 自分の環境だと unsigned char
std::cout << static_cast<std::bitset<8>>(x) << std::endl; // 00000001
//
// だが、std::cin から uint8_t に入力されると、unsigned char に
// 型変換されるので、ビット列は数値のものにならないので注意
//
// https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/istream#L754-L757
// https://en.cppreference.com/w/cpp/io/basic_istream/operator_gtgt2
//
std::cin >> x; // 標準入力から1を入力する想定
print_typename(typeid(x)); // unsigned char
std::cout << static_cast<std::bitset<8>>(x) << std::endl; // 00110001
// 自分の環境だと、標準入力から x に 1を入力すると、以下は "false!" と出力される
// なぜなら、x は unsigned char での 1、つまり int の 49 として扱われるからだ
if (x == 1) {
std::cout << "true!" << std::endl;
} else {
std::cout << "false!" << std::endl;
}
return 0;
}
競技プログラミングでは、普通に int を使え。カッコつけんなハゲ > 自分
そもそも、unsigned な整数型は、使うなとは言わないが、以下の理由で、自分なりの理由を持って使ったほうが良い。
詳細は Google C++ Style Guide を参照
- 桁溢れした時にバグを生みやすい
- unsigned の時は挙動が明確に規定されている分、バグった時の挙動が一定
- signed と unsigned を混ぜた時に、安全かどうかを考えるのが大変
- unsigned が本当に必要な時は bitfield くらい
- 上記の考慮なしに、ドキュメント化しやすい、という理由だけで unsigned を使うのはやめた方がいい