C++ | 将整数转换为字节[4],反之亦然

4

我正在尝试将整数转换为字节(也称为无符号字符)数组,以便在C ++中通过TCP流发送该数组,反之亦然。

我已经尝试了stackoverflow上的许多解决方案和自己的想法,但似乎都不太适合我。

我的最后一个解决方案看起来像这样:

#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include "tcpconnector.h"

typedef unsigned char byte;

using namespace std;

/*
char* byteToChar(byte* b, int length) {
    char c[length];
    for (int i = 0; i < length; i++) {
    c[i] = b[i] - 128;
    }
    return c;
}

byte* charToByte(char* c, int length) {
    byte b[length];
    for (int i = 0; i < length; i++) {
    b[i] = c[i] + 128;
    }
    return b;
}
*/

byte* intToByte(int n) {

    byte byte[4];

     byte[0] = n & 0x000000ff;
     byte[1] = n & 0x0000ff00 >> 8;
     byte[2] = n & 0x00ff0000 >> 16;
     byte[3] = n & 0xff000000 >> 24; 

     return byte;
}

int byteToInt(byte* byte) {

    int n = 0;

    n = n + (byte[0] & 0x000000ff);
    n = n + ((byte[1] & 0x000000ff) << 8);
    n = n + ((byte[2] & 0x000000ff) << 16);
    n = n + ((byte[3] & 0x000000ff) << 24);


    return n;
}

int main(int argc, char** argv)
{
    if (argc != 3) {
        printf("usage: %s <port> <ip>\n", argv[0]);
        exit(1);
    }

    int number = 42;
    byte* line = intToByte(number);

    cout << "Number: " << number << "\n";
    cout << "ArrayLength: " << sizeof line << "\n";
    cout << "Array: " << line << "\n";
    cout << "Array to Number: " << byteToInt(line) << "\n";

/*

    TCPConnector* connector = new TCPConnector();
    TCPStream* stream = connector->connect(argv[2], atoi(argv[1]));
    if (stream) {
        stream->send(byteToChar(line, 4), 4);
        delete stream;
    }

*/
    exit(0);
}

每次我执行这段代码,无论我将"int number"设置为什么值,都会得到结果"4202308"。
任何帮助将不胜感激。
更新:
void intToByte(int n, byte* result) {

     result[0] = n & 0x000000ff;
     result[1] = n & 0x0000ff00 >> 8;
     result[2] = n & 0x00ff0000 >> 16;
     result[3] = n & 0xff000000 >> 24; 
}

main()函数中的摘录:

int number = 42;
byte line[4];
intToByte(number, line);

cout << "Number: " << number << "\n";
cout << "ArrayLength: " << sizeof line << "\n";
cout << "Array: " << line << "\n";
cout << "Array to Number: " << byteToInt(line) << "\n";

你确定了你的机器的字节序吗? - Cherkesgiller
很难确定代码中缺失的注释没有显示结果数组应该存在的位置 - 要么提供它的地址/引用作为参数,要么动态分配它。考虑更改函数的名称 - 至少更改为 ...Bytes...() - greybeard
在这种情况下,由于我在同一台机器上来回移动相同的字节,所以这应该不会有问题。但是,一旦我通过TCP流传输数据,这可能会成为一个问题。 - eheller
5个回答

4
你的 intToByte 函数在其函数体中分配了一个 byte[4] 并返回指向它的指针。
因此,值在函数返回时就被破坏了,调用者只接收到指向无效位置的指针 - 值在超出作用域时被销毁,而指针不会扩展生命周期。
要么使用标准容器对象,如 std::arraystd::vector,使你的函数将其返回给调用者,要么在 intToByte 中接受一个 byte[4]/byte* 作为参数并填充其中。
为了完整性... 你也可以让函数使用 new 创建字节数组,但那样你就必须记得 delete[] 它,虽然在这种情况下似乎很容易做到,但如果没有充分的理由进行动态分配,则通常是不好的实践。
此外,语句 x & y >> z 将首先执行 y >> z,然后对结果与 x 进行按位与运算,这显然不是你想要的。

非常感谢!这很有道理,我现在记得你必须通过参数传递引用。这也可以解释为什么 sizeof 行始终返回 8 而不是 4。 - eheller
我觉得你的意思是“通过引用传递参数”,尽管在这里并不适用,因为一切都是按值传递的,虽然指针可以被视为引用,但实际上你只是传递了指针的值。不过,顺便提一下,你也可以接受byte (&)[4]而不是byte*来解决这个问题。不过如果可以使用std::array,最好直接使用它。 - Olipro
不幸的是,将签名更改为void intToByte(int n, byte* result)并填充数组并没有真正帮助。也许我的转换逻辑仍然存在问题。我将尝试使用std :: array,但我想知道为什么将byte *作为参数传递不起作用。 - eheller
你实际传递给它的是什么?你应该在调用者中创建一个 byte [4] 并将其传递给 intToByte - Olipro
我将更改作为更新添加到了我的原始帖子中。 - eheller
实际上,在位与操作周围需要放置括号,但除此之外,我的简单测试用例可以正常工作:http://ideone.com/pFK0nk - Olipro

2

这个函数将C++中的所有标准类型转换为字节向量。

template <typename T>
std::vector<byte> ToByte(T input) 
{
    byte* bytePointer = reinterpret_cast<byte*>(&input);
    return std::vector<byte>(bytePointer, bytePointer + sizeof(T));
}

2

第一点: 你从byte* intToByte(int n)返回的缓冲区不安全。 你正在返回局部数组的基地址。要么将字节数组作为输入传递,要么在堆中分配字节并返回地址。

第二点:

考虑运算符优先级。

 byte byte[4];
 byte[0] = n & 0x000000ff;
 byte[1] = ( n & 0x0000ff00 ) >> 8;
 byte[2] = ( n & 0x00ff0000 ) >> 16;
 byte[3] = ( n & 0xff000000 ) >> 24; 

谢谢!在将结果数组用作参数后,缺少的括号是问题所在。我不知道为什么没有想到那个。 - eheller

0
如果有人正在寻找现代的C++20解决方案(使用标准库中的)。
这里有两个很好的模板函数,用于将基本类型(如int、short等)转换为字节,并进行必要的字节序转换:
template<typename T, std::endian to_endianness = std::endian::big>
std::vector<uint8_t> toBytes(T value)
{
    std::vector<uint8_t> buffer(sizeof(T));

    std::copy_n(reinterpret_cast<uint8_t*>(&value), sizeof(T), buffer.begin());

    if constexpr (std::endian::native != to_endianness)
        std::reverse(buffer.begin(), buffer.end());

    return buffer;
}

...而要将字节转换为其他单位,您可以使用以下方法:
template<typename T, std::endian from_endianness = std::endian::big>
T fromBytes(std::vector<uint8_t> bytes)
{
    if constexpr (std::endian::native != from_endianness)
        std::reverse(bytes.begin(), bytes.end());

    T* buffer = reinterpret_cast<T*>(bytes.data());

    return *buffer;
}

为了保持这个答案中的代码简单,我故意删除了对混合字节顺序和std::is_fundamental<T>的检查。如果需要,您可以在我的GitHub gist(Unlicense许可证)上查看带有这些检查的代码。

0

很遗憾这不是你问题的确切答案,但或许有所帮助。我认为你可以像以下代码一样实现。

#include <stdio.h>
using namespace std;

unsigned char* intToByte(const int& N) {

    unsigned char* byte = new unsigned char[4];

    byte[0] = (N >> 24) & 0xFF;
    byte[1] = (N >> 16) & 0xFF;
    byte[2] = (N >> 8) & 0xFF;
    byte[3] = N & 0xFF;

    return byte;
}


int main()
{
    unsigned char * result = intToByte(255);
    printf("%x %x %x %x\n", (unsigned char)result[0], (unsigned char)result[1],
                            (unsigned char)result[2], (unsigned char)result[3]);

    delete result;
    return 0;
}

修复了问题,但恰好是我说过的不好的事情(TM)。虽然我很想知道你为什么认为传递int&而不是int更好。 - Olipro
它是const int&而不是int&。 - Cherkesgiller
是的,为了简洁起见,我省略了它,因为它与我的问题无关,我对您的const正确性没有任何异议。 - Olipro
质量不是一种行为,而是一种习惯。 :))) - Cherkesgiller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接