Je veux travailler avec des variables 8 bits non signées en C++. Non plus unsigned char
ou uint8_t
faire l'affaire en ce qui concerne l'arithmétique (ce qui est attendu, puisque AFAIK uint8_t
n'est qu'un alias pour unsigned char
, ou du moins le débogueur le présente.
Le problème est que si j'imprime les variables à l'aide d'ostream en C++, il les traite comme des caractères. Si j'ai:
unsigned char a = 0;
unsigned char b = 0xff;
cout << "a is " << hex << a <<"; b is " << hex << b << endl;
alors la sortie est:
a is ^@; b is 377
au lieu de
a is 0; b is ff
J'ai essayé d'utiliser uint8_t
, mais comme je l'ai déjà mentionné, c'est typé par unsigned char
, donc il fait de même. Comment imprimer correctement mes variables?
Edit: Je le fais à plusieurs endroits dans mon code. Existe-t-il un moyen de faire cela sans transtyper en int
chaque fois que je veux imprimer?
Je suggère d'utiliser la technique suivante:
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;
}
Il est court à écrire, a la même efficacité que la solution d'origine et vous permet de choisir d'utiliser la sortie de caractères "d'origine". Et il est sûr pour le type (n'utilise pas de macros "diaboliques" :-))
Utilisation:
cout << "a is " << hex << (int) a <<"; b is " << hex << (int) b << endl;
Et si vous voulez un remplissage avec des zéros en tête, alors:
#include <iomanip>
...
cout << "a is " << setw(2) << setfill('0') << hex << (int) a ;
Comme nous utilisons des transtypages de style C, pourquoi ne pas aller tout le porc avec la bêtise du terminal C++ et utiliser une macro!
#define HEX( x )
setw(2) << setfill('0') << hex << (int)( x )
vous pouvez alors dire
cout << "a is " << HEX( a );
Edit: Cela dit, la solution de MartinStettner est beaucoup plus agréable!
Vous pouvez en savoir plus à ce sujet sur http://cpp.indi.frih.net/blog/2014/09/tippet-printing-numeric-values-for-chars-and-uint8_t/ and - http://cpp.indi.frih.net/blog/2014/08/code-critique-stack-overflow-posters-cant-print-the-numeric-value-of-a-char/ . Je ne poste cela que parce qu'il est devenu clair que l'auteur des articles ci-dessus n'a pas l'intention de le faire.
La technique la plus simple et la plus correcte pour imprimer un caractère en hexadécimal est
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);
Les lecteurs résument la façon dont cela fonctionne, c'est que l'opérateur unaire + force une conversion de type sans op en un int avec la signature correcte. Ainsi, un caractère non signé est converti en entier non signé, un caractère signé est converti en entier et un caractère est converti en entier ou entier non signé selon que le caractère est signé ou non sur votre plate-forme (il est surprenant pour beaucoup que le caractère soit spécial et non spécifié comme signé ou non signé).
Le seul point négatif de cette technique est qu'il peut ne pas être évident de savoir ce qui arrive à une personne qui ne la connaît pas. Cependant, je pense qu'il vaut mieux utiliser la technique qui est correcte et en enseigner aux autres plutôt que de faire quelque chose qui est incorrect mais plus immédiatement clair.
Eh bien, cela fonctionne pour moi:
std::cout << std::hex << (0xFF & a) << std::endl;
Si vous venez de lancer (int)
comme suggéré, il pourrait ajouter 1s à gauche de a
si son bit le plus significatif est 1. Donc, faire cette opération binaire ET garantit que la sortie aura les bits de gauche remplis de 0 et les convertira également en entier non signé forçant cout à l'imprimer en hexadécimal.
J'espère que ça aide.
Hm, il semble que j'ai réinventé la roue hier ... Mais bon, au moins c'est une roue générique cette fois :) char
s sont imprimés avec deux chiffres hexadécimaux, short
s avec 4 chiffres hexadécimaux etc.
template<typename T>
struct hex_t
{
T x;
};
template<typename T>
hex_t<T> hex(T x)
{
hex_t<T> h = {x};
return h;
}
template<typename T>
std::ostream& operator<<(std::ostream& os, hex_t<T> h)
{
char buffer[2 * sizeof(T)];
for (auto i = sizeof buffer; i--; )
{
buffer[i] = "0123456789ABCDEF"[h.x & 15];
h.x >>= 4;
}
os.write(buffer, sizeof buffer);
return os;
}
Je pense que la réponse de TrungTN et anon est correcte, mais la manière de MartinStettner d'implémenter la fonction hex () n'est pas vraiment simple et trop sombre, étant donné que hex << (int) mychar est déjà une solution de contournement.
voici ma solution pour rendre l'opérateur "<<" plus facile:
#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;
}
Il n'est tout simplement pas digne d'implémenter un opérateur de flux :-)
Je le ferais comme MartinStettner mais j'ajouterais un paramètre supplémentaire pour le nombre de chiffres:
inline HexStruct hex(long n, int w=2)
{
return HexStruct(n, w);
}
// Rest of implementation is left as an exercise for the reader
Vous disposez donc de deux chiffres par défaut, mais vous pouvez en définir quatre, huit ou peu importe si vous le souhaitez.
par exemple.
int main()
{
short a = 3142;
std:cout << hex(a,4) << std::endl;
}
Cela peut sembler exagéré mais comme l'a dit Bjarne: "les bibliothèques devraient être faciles à utiliser, pas faciles à écrire".
Je voudrais suggerer:
std::cout << setbase(16) << 32;
J'utilise ce qui suit sur win32/linux (32/64 bits):
#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();
}
Vous pouvez essayer le code suivant:
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;
Sortie:
a is 0; b is ff
a is 00; b is ff
a is 00; b is FF
Je me rends compte que c'est une vieille question, mais c'est aussi un résultat majeur de Google dans la recherche d'une solution à un problème très similaire que j'ai, qui est le désir d'implémenter des conversions arbitraires entières en chaînes hexadécimales dans une classe de modèle. Mon objectif final était en fait un Gtk::Entry
modèle de sous-classe qui permettrait de modifier différentes largeurs entières en hexadécimal, mais c'est à côté du point.
Cela combine l'unaire operator+
astuce avec std::make_unsigned
de <type_traits>
pour éviter le problème de l'extension du signe négatif int8_t
ou signed char
valeurs qui se produisent dans cette réponse
Quoi qu'il en soit, je pense que c'est plus succinct que toute autre solution générique. Cela devrait fonctionner pour any types entiers signés ou non signés, et génère une erreur de compilation si vous essayez d'instancier la fonction avec des types non entiers.
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();
}
Quelques exemples d'utilisation:
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;
}
Mise à jour: Alternativement, si vous n'aimez pas l'idée d'utiliser ostringstream
, vous pouvez combiner l'astuce de modèle et d'opérateur unaire avec la solution basée sur la structure de la réponse acceptée pour les éléments suivants. Notez qu'ici, j'ai modifié le modèle en supprimant la vérification des types entiers. Le make_unsigned
l'utilisation peut être suffisante pour compiler les garanties de sécurité de type heure.
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;
Je voudrais publier ma version réinventée basée sur @ FredOverflow. J'ai apporté les modifications suivantes.
réparer:
operator<<
Doivent être de type de référence const
. Dans le code de @ FredOverflow, h.x >>= 4
Modifie la sortie h
, ce qui n'est étonnamment pas compatible avec la bibliothèque standard, et le type T
doit être construit en copie.CHAR_BITS
Est un multiple de 4. @ Le code de FredOverflow suppose que char
est de 8 bits, ce qui n'est pas toujours vrai, dans certaines implémentations sur les DSP, en particulier, il n'est pas rare que char
est 16 bits, 24 bits, 32 bits, etc.améliorer:
std::uppercase
. Étant donné que la sortie au format est utilisée dans _print_byte
, Les manipulateurs de bibliothèque standard sont toujours disponibles.hex_sep
Pour imprimer des octets séparés (notez qu'en C/C++, un "octet" est par définition une unité de stockage de la taille de char
). Ajoutez un paramètre de modèle Sep
et instanciez _Hex<T, false>
Et _Hex<T, true>
Dans hex
et hex_sep
Respectivement._print_byte
Est extraite de operator<<
, Avec un paramètre de fonctionsize
, pour éviter l'instanciation de différents Size
.Plus d'informations sur le ballonnement de code binaire:
Comme mentionné dans l'amélioration 3, peu importe à quel point hex
et hex_sep
Sont utilisés, seules deux copies de la fonction (presque) dupliquée sortiront en code binaire: _print_byte<true>
Et _print_byte<false>
. Et vous pourriez vous rendre compte que cette duplication peut également être éliminée en utilisant exactement la même approche: ajoutez un paramètre de fonction sep
. Oui, mais si vous le faites, un runtime if(sep)
est nécessaire. Je veux un utilitaire de bibliothèque commun qui peut être largement utilisé dans le programme, j'ai donc compromis la duplication plutôt que la surcharge d'exécution. J'y suis parvenu en utilisant le temps de compilation if
: C++ 11 std::conditional
, La surcharge de l'appel de fonction peut être optimisée avec 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));
}
}
tester:
struct { int x; } output = {0xdeadbeef};
cout << hex_sep(output) << std::uppercase << hex(output) << endl;
sortie:
de ad be ef DEADBEEF