77

C++で符号なし8ビット変数を操作したいと思います。算術に関する限り、unsigned charまたはトリックを実行します( AFAIKはのエイリアスにすぎないため、これは予想されます。つまり、デバッガーがそれを提示します。uint8_tuint8_tunsigned char

問題は、C ++でostreamを使用して変数を出力すると、charとして扱われることです。私が持っている場合:

unsigned char a = 0;
unsigned char b = 0xff;
cout << "a is " << hex << a <<"; b is " << hex << b << endl;

その場合、出力は次のようになります。

a is ^@; b is 377

それ以外の

a is 0; b is ff

を使用してみuint8_tましたが、前に述べたように、unsigned charそれはtypedefされているので、同じように動作します。変数を正しく出力するにはどうすればよいですか?

編集:コード全体の多くの場所でこれを行います。印刷するたびにキャストせずにこれを行う方法はありますか?int

4

17 に答える 17

58

使用する:

cout << "a is " << hex << (int) a <<"; b is " << hex << (int) b << endl;

また、先行ゼロでパディングする場合は、次のようにします。

#include <iomanip>
...
cout << "a is " << setw(2) << setfill('0') << hex << (int) a ; 

Cスタイルのキャストを使用しているので、ターミナルC ++の悪さを完全に理解して、マクロを使用してみませんか。

#define HEX( x )
   setw(2) << setfill('0') << hex << (int)( x )

あなたはそれから言うことができます

cout << "a is " << HEX( a );

編集:そうは言っても、MartinStettnerのソリューションははるかに優れています!

于 2009-03-23T12:44:29.380 に答える
57

次の手法を使用することをお勧めします。

struct HexCharStruct
{
  unsigned char c;
  HexCharStruct(unsigned char _c) : c(_c) { }
};

inline std::ostream& operator<<(std::ostream& o, const HexCharStruct& hs)
{
  return (o << std::hex << (int)hs.c);
}

inline HexCharStruct hex(unsigned char _c)
{
  return HexCharStruct(_c);
}

int main()
{
  char a = 131;
  std::cout << hex(a) << std::endl;
}

書くのは短く、元のソリューションと同じ効率で、「元の」文字出力を使用することを選択できます。そしてそれはタイプセーフです(「邪悪な」マクロを使用していません:-))

于 2009-03-23T13:37:15.993 に答える
36

詳細については、http: //cpp.indi.frih.net/blog/2014/09/tippet-printing-numeric-values-for-chars-and-uint8_t/およびhttp://cpp.indi を参照してください。 frih.net/blog/2014/08/code-critique-stack-overflow-posters-cant-print-the-numeric-value-of-a-char/ . 上記の記事の著者が意図していないことが明らかになったので、私はこれを投稿しているだけです.

文字を 16 進数として出力するための最も簡単で最も正しい手法は次のとおりです。

unsigned char a = 0;
unsigned char b = 0xff;
auto flags = cout.flags(); //I only include resetting the ioflags because so
                           //many answers on this page call functions where
                           //flags are changed and leave no way to  
                           //return them to the state they were in before 
                           //the function call
cout << "a is " << hex << +a <<"; b is " << +b << endl;
cout.flags(flags);

これがどのように機能するかの読者のダイジェスト バージョンは、単項 + 演算子が、正しい符号を持つ int への no op 型変換を強制することです。したがって、unsigned char は unsigned int に変換され、signed char は int に変換され、char はプラットフォームで char が signed か unsigned かによって unsigned int または int に変換されます (char が特別であるということは、多くの人にとってショックです)。署名付きまたは署名なしのいずれかとして指定されていません)。

この手法の唯一の欠点は、慣れていない人に何が起こっているのかがはっきりしない場合があることです。ただ、間違ってもすぐにわかるようなことをするよりも、正しいテクニックを使って人に教えたほうがいいと思います。

于 2015-02-05T22:23:23.370 に答える
4

TrungTNとanonの答えは大丈夫だと思いますが、MartinStettnerのhex()関数の実装方法はそれほど単純ではなく、hex <<(int)mycharがすでに回避策であることを考えると暗すぎます。

「<<」演算子を簡単にするための私の解決策は次のとおりです。

#include <sstream>
#include <iomanip>

string uchar2hex(unsigned char inchar)
{
  ostringstream oss (ostringstream::out);
  oss << setw(2) << setfill('0') << hex << (int)(inchar);
  return oss.str();
}

int main()
{
  unsigned char a = 131;
  std::cout << uchar2hex(a) << std::endl;
}

ストリーム演算子を実装する価値はありません:-)

于 2012-09-28T08:51:22.643 に答える
4

これらの型変換がどのように機能するかについての説明が欠けていると思います。

charはプラットフォームに依存するsignedか、またはunsigned. x86 ではcharと同等signed charです。

整数型 ( charshortintlong) をより容量の大きい型に変換する場合、unsigned型の場合は左側に 0 を追加し、1 の場合は符号拡張して変換しsignedます。符号拡張は、ターゲット タイプのビット サイズに達するまで、元の数値の最上位 (左端) ビットを左に複製することで構成されます。

したがって、signed charデフォルトのシステムにいる場合、次のようにします。

char a = 0xF0; // Equivalent to the binary: 11110000
std::cout << std::hex << static_cast<int>(a);

F...F0先頭の1ビットが拡張されているため、取得できます。

任意のシステムでのみ出力することを確認したい場合はF0、追加の中間型キャストを an にキャストして、unsigned char代わりにゼロが追加されるようにする必要があります。8 ビットのみの整数では意味がないため、出力されません。

char a = 0xF0; // Equivalent to the binary: 11110000
std::cout << std::hex << static_cast<int>(static_cast<unsigned char>(a));

これにより、F0

于 2020-03-13T14:55:37.913 に答える
3

MartinStettnerと同じようにしますが、桁数のパラメーターを追加します。

inline HexStruct hex(long n, int w=2)
{
  return HexStruct(n, w);
}
// Rest of implementation is left as an exercise for the reader

したがって、デフォルトでは2桁ですが、必要に応じて4桁、8桁、またはその他の数字を設定できます。

例えば。

int main()
{
  short a = 3142;
  std:cout << hex(a,4) << std::endl;
}

やり過ぎのように思えるかもしれませんが、Bjarneが言ったように、「ライブラリは使いやすく、書くのは簡単ではないはずです」。

于 2009-03-23T14:00:42.350 に答える
2

次のコードを試すことができます。

unsigned char a = 0;
unsigned char b = 0xff;
cout << hex << "a is " << int(a) << "; b is " << int(b) << endl;
cout << hex
     <<   "a is " << setfill('0') << setw(2) << int(a)
     << "; b is " << setfill('0') << setw(2) << int(b)
     << endl;
cout << hex << uppercase
     <<   "a is " << setfill('0') << setw(2) << int(a)
     << "; b is " << setfill('0') << setw(2) << int(b)
     << endl;

出力:

a is 0; b is ff

a is 00; b is ff

a is 00; b is FF

于 2012-05-07T02:27:10.737 に答える
2

私はwin32/linux(32/64ビット)で以下を使用します:

#include <iostream>
#include <iomanip>

template <typename T>
std::string HexToString(T uval)
{
    std::stringstream ss;
    ss << "0x" << std::setw(sizeof(uval) * 2) << std::setfill('0') << std::hex << +uval;
    return ss.str();
}
于 2016-05-16T09:18:12.227 に答える
2

私は提案します:

std::cout << setbase(16) << 32;

出典: http://www.cprogramming.com/tutorial/iomanip.html

于 2012-02-02T15:23:52.637 に答える
1

これは古い質問だと思いますが、テンプレートクラス内で任意の整数から16進文字列への変換を実装したいという私が抱えている非常によく似た問題の解決策を検索することで、Googleのトップの結果でもあります。私の最終目標は実際には、Gtk::Entryさまざまな整数幅を 16 進数で編集できるサブクラス テンプレートでしたが、それは的外れです。

これは、単項operator+トリックをstd::make_unsignedfromと組み合わせて、この回答で発生する負または値<type_traits>の符号拡張の問題を防ぎますint8_tsigned char

とにかく、これは他の一般的なソリューションよりも簡潔だと思います。これは、符号付きまたは符号なしの整数型に対して機能するはずであり、整数以外の型で関数をインスタンス化しようとすると、コンパイル時エラーがスローされます。

template < 
  typename T,
  typename = typename std::enable_if<std::is_integral<T>::value, T>::type
>
std::string toHexString(const T v)
{ 
  std::ostringstream oss;
  oss << std::hex << +((typename std::make_unsigned<T>::type)v);
  return oss.str();
}

使用例:

int main(int argc, char**argv)
{
  int16_t val;
  // Prints 'ff' instead of "ffffffff". Unlike the other answer using the '+'
  // operator to extend sizeof(char) int types to int/unsigned int
  std::cout << toHexString(int8_t(-1)) << std::endl;

  // Works with any integer type
  std::cout << toHexString(int16_t(0xCAFE)) << std::endl;

  // You can use setw and setfill with strings too -OR- 
  // the toHexString could easily have parameters added to do that.
  std::cout << std::setw(8) << std::setfill('0') << 
    toHexString(int(100)) << std::endl;
  return 0;
}

更新:または、使用されているという考えが気に入らない場合はostringstream、テンプレートと単項演算子のトリックを、次の受け入れられた回答の構造体ベースのソリューションと組み合わせることができます。ここで、整数型のチェックを削除してテンプレートを変更したことに注意してください。使用make_unsigned法は、コンパイル時の型の安全性の保証には十分かもしれません。

template <typename T>
struct HexValue 
{
  T value;
  HexValue(T _v) : value(_v) { }
};

template <typename T>
inline std::ostream& operator<<(std::ostream& o, const HexValue<T>& hs)
{
  return o << std::hex << +((typename std::make_unsigned<T>::type) hs.value);
}

template <typename T>
const HexValue<T> toHex(const T val)
{
  return HexValue<T>(val);
}

// Usage:
std::cout << toHex(int8_t(-1)) << std::endl;
于 2016-03-13T04:54:08.843 に答える
0

@FredOverflow に基づいて再発明したバージョンを投稿したいと思います。以下の修正を行いました。

修理:

  • の右側は参照型であるoperator<<必要があります。const@FredOverflow のコードでは、驚くべきことに標準ライブラリと互換性のないh.x >>= 4output を変更し、型はコピー構築可能である必要があります。hT
  • のみCHAR_BITSが 4 の倍数であると仮定します。@FredOverflow のコードは 8 ビットであると仮定しますが、これは常に正しいとは限りません。DSP の一部の実装では、特に、 16 ビット、24 ビット、32 ビットなどはchar珍しくありません。 char.

向上:

  • 整数型で利用可能な他のすべての標準ライブラリ マニピュレータをサポートしますstd::uppercase。ではフォーマット出力が使用されるため_print_byte、標準ライブラリ マニピュレータは引き続き使用できます。
  • 個別のバイトを出力するために追加hex_sepします (C/C++ では、「バイト」は定義上、サイズが の記憶単位であることに注意してくださいchar)。テンプレート パラメーターを追加し、それぞれSepインスタンス化_Hex<T, false>します。_Hex<T, true>hexhex_sep
  • バイナリ コードの肥大化を回避します。関数は、異なる のインスタンス化を避けるために、関数パラメータ_print_byteを使用して から抽出されます。operator<< sizeSize

バイナリ コードの肥大化の詳細:

hex改善点 3 で述べたように、 とがどれほど広くhex_sep使用されていても、(ほぼ) 重複した関数の 2 つのコピーのみがバイナリ コードで存在します:_print_byte<true>_print_byte<false>. そして、この重複はまったく同じアプローチを使用して排除できることにも気付いたかもしれません: 関数パラメーターを追加しますsep。はい。ただし、その場合はランタイムif(sep)が必要です。プログラムで広範囲に使用できる共通のライブラリ ユーティリティが必要なので、実行時のオーバーヘッドではなく重複を妥協しました。私はコンパイル時を使用してこれを達成しましたif:C++ 11 std::conditional、関数呼び出しのオーバーヘッドは、うまくいけばinline.

hex_print.h:

namespace Hex
{
typedef unsigned char Byte;

template <typename T, bool Sep> struct _Hex
{
    _Hex(const T& t) : val(t)
    {}
    const T& val;
};

template <typename T, bool Sep>
std::ostream& operator<<(std::ostream& os, const _Hex<T, Sep>& h);
}

template <typename T>  Hex::_Hex<T, false> hex(const T& x)
{ return Hex::_Hex<T, false>(x); }

template <typename T>  Hex::_Hex<T, true> hex_sep(const T& x)
{ return Hex::_Hex<T, true>(x); }

#include "misc.tcc"

hex_print.tcc:

namespace Hex
{

struct Put_space {
    static inline void run(std::ostream& os) { os << ' '; }
};
struct No_op {
    static inline void run(std::ostream& os) {}
};

#if (CHAR_BIT & 3) // can use C++11 static_assert, but no real advantage here
#error "hex print utility need CHAR_BIT to be a multiple of 4"
#endif
static const size_t width = CHAR_BIT >> 2;

template <bool Sep>
std::ostream& _print_byte(std::ostream& os, const void* ptr, const size_t size)
{
    using namespace std;

    auto pbyte = reinterpret_cast<const Byte*>(ptr);

    os << hex << setfill('0');
    for (int i = size; --i >= 0; )
    {
        os << setw(width) << static_cast<short>(pbyte[i]);
        conditional<Sep, Put_space, No_op>::type::run(os);
    }
    return os << setfill(' ') << dec;
}

template <typename T, bool Sep>
inline std::ostream& operator<<(std::ostream& os, const _Hex<T, Sep>& h)
{
    return _print_byte<Sep>(os, &h.val, sizeof(T));
}

}

テスト:

struct { int x; } output = {0xdeadbeef};
cout << hex_sep(output) << std::uppercase << hex(output) << endl;

出力:

de ad be ef DEADBEEF

于 2013-11-01T12:50:46.207 に答える
-2

これも機能します:

std::ostream& operator<< (std::ostream& o, unsigned char c)
{
    return o<<(int)c;
}

int main()
{
    unsigned char a = 06;
    unsigned char b = 0xff;
    std::cout << "a is " << std::hex << a <<"; b is " << std::hex << b << std::endl;
    return 0;
}
于 2009-03-23T13:01:43.957 に答える