Каков наилучший способ конвертировать unsigned long в char* в C++
Мне нужно отправить unsigned long
с помощью сокетов. В связи с тем, что unsigned long
4 байта, получатель ожидает только 4 байта. Функция преобразования, которую я написал, и вы найдете ниже, работает, но только если число, которое должно быть сохранено в символе, не больше, чем 127 соответственно. 0x7F. Для значений больше 0x7f я ожидал бы, что символы в расширенной таблице ASCII ( http://www.asciitable.com/) будут храниться в char, но это определенно не тот случай. Для 0x90 например ничего не сохраняется. Я использую VS12 с набором символов Unicode.
Есть идеи, как сделать правильное преобразование?
void number2char(unsigned long number, char* nrAsByte){
std::stringstream numberSS;
numberSS << std::hex << number;
int length = numberSS.str().length();
length = length / 2.0 + 0.5;
nrAsByte = new char[sizeof(number)]();
std::fill(nrAsByte, nrAsByte + length, '\x20');
while (length > 0){
int lastTwo = (number & 0xff);
number >>= 8;
unsigned char a = lastTwo; // this doesn't work if lastTwo > 0x7F
std::memcpy(nrAsByte + length - 1, &a, 1);
--length;
}
}
Извините за код, он не был хорошо проверен мной и содержит ошибки, пожалуйста, НЕ ИСПОЛЬЗУЙТЕ ЕГО, следуйте советам в ответах
2 ответа
Почему не что-то вроде:
void number2char(unsigned long number, char* nrAsByte){
unsigned char *dst= reinterpret_cast<unsigned char *> nrAsByte;
for (int i=0; i<sizeof(unsigned long); ++i) {
*dst++= number & 0xFF;
number >>= 8;
}
}
Хм, я возился с ответом Антонио, так как он не был полным и правильным, и в конце концов я закончил с чем-то более сложным, чем ожидал, но иногда сложность имеет свою цель.
Следующий код делает среди прочего также htonl
/ntohl
-подобно преобразование вручную (возможно, с обратным порядком байтов, поэтому не желательно смешивать это с htonl
, используйте либо это, либо перепишите его htonl
).
В отличие от источника Антонио, он не будет перезаписывать память, если тип входного номера имеет длину 8 байт (unsigned long
на моей тестовой платформе - 8 байт - кстати, попробуйте?!), вместо этого оно будет усекать значение, чтобы поместиться в требуемый сетевой буфер char*.
Я попытался прокомментировать это подробно, чтобы дать вам идею каждого решения, чтобы добавить дополнительную сложность (к тому, что в основном используется unsigned int number
используется в качестве (char *)(&number)
, что тоже обеспечивает, но не защищает от байтов, и может закончиться перезаписью памяти, если вы смешаете разные типы длины). Но спросите что-нибудь, если вы видите что-то неясное.
#include <iostream>
#include <string>
// Experiment with different types to see differences
// (and how data are truncated when sizeof number > sizeof networkdata)
//typedef unsigned int numberType_t;
typedef unsigned long numberType_t; // on my platform this is 8 bytes long
constexpr int networkBytesSize = 4; // number of chars to be sent trough network with (char *)
// define network data type:
// used "struct" to make sizeof(networkData_t) return actual number of bytes
typedef struct {
unsigned char d[networkBytesSize];
char *cptr() { return reinterpret_cast<char *>(d); }
} networkData_t;
// Writes number into network char* buffer nrAsByte, endianness agnostic
void number2char(numberType_t number, networkData_t & nrAsByte) {
for (size_t i = 0; i < sizeof(networkData_t); ++i) {
nrAsByte.d[i] = number & 0xFF;
number >>= 8;
}
}
// Read number back from network char* buffer
numberType_t char2number(const networkData_t & nrAsByte) {
numberType_t number = 0;
size_t i = sizeof(networkData_t);
while (i--) number = (number<<8) | nrAsByte.d[i];
return number;
}
int main()
{
printf("numberType_t size in bytes: %lu, networkData_t size in bytes: %lu\nAll following numbers are hex:\n",
sizeof(numberType_t), sizeof(networkData_t));
numberType_t number = numberType_t(0x9ABCDEF0123456FEul);
std::cout << "source number: " << std::hex << number << std::endl;
// Write number into char buffer
networkData_t networkData;
number2char(number, networkData);
std::cout << "network bytes:";
for (size_t i = 0; i < sizeof(networkData_t); ++i) std::cout << " [" << unsigned(networkData.d[i]) << "]";
std::cout << std::endl;
// Test usability of (char *) pointer access
const char * testCharPtrConversion = networkData.cptr();
printf("as char * (decimal signed): %d %d ...\n", testCharPtrConversion[0], testCharPtrConversion[1]);
// Read number from char buffer
number = char2number(networkData);
std::cout << "read number: 0x" << std::hex << number << std::endl;
}