Skip to content

Instantly share code, notes, and snippets.

@mumumu
Last active February 24, 2024 08:34
Show Gist options
  • Save mumumu/f2067b5091004384dabab86ca1c1d0bf to your computer and use it in GitHub Desktop.
Save mumumu/f2067b5091004384dabab86ca1c1d0bf to your computer and use it in GitHub Desktop.
uint8_t とかをカッコつけて競技プログラミングでは使わないほうがいい、という話

uint8_t とかをカッコつけて競技プログラミングでは使わないほうがいい、という話

  • 競技プログラミングでは、std::cin で値を入力するのが一般的である
  • 問題の制約上、小さな値 (例: 0 < N < 100 とか)は、uint8_t で扱えそう、と思ったりする人がいるかもしれないが、ここに罠がある
    • uint8_t な変数に std::cin すると、ビットパターンは char になる。少なくとも gcc (libstdc++) の環境では。
      • 他の処理系だと異なる動きをするかもしれない
    • なので、数値と比較すると思ったように動作しない

サンプルコード (gcc only)

以下のコードを 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 を使うのはやめた方がいい
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment