Home > Software design >  To byte conversion in c
To byte conversion in c

Time:03-07

i tried making a c function that takes in a pointer and then returns a pointer to a char array representing the bytes

char* toBytes(void* src, int byteSize) {
        char convertedToBytes[byteSize];
        memcpy(&convertedToBytes, src, byteSize);
        return _strdup(convertedToBytes);
    }

though when using it and checking each char i see the output is 3 0 ffffffffd ffffffffd instead of 3 0 0, here is the full code:

int32_t i_lengthOfCommand = 3;
char *s_lengthOfCommand = toBytes(&i_lengthOfCommand);
printf("%x %x %x %x", s_lengthOfCommand[0], s_lengthOfCommand[1], s_lengthOfCommand[2], s_lengthOfCommand[3]);

stdout: 3 0 ffffffffd ffffffffd

PS: i know there is no support for big/little endian

CodePudding user response:

I suggest using a std::string:

template<class T>
std::string toBytes(const T& src) {
    return {reinterpret_cast<const char*>(&src),
            reinterpret_cast<const char*>(&src)   sizeof src};
}

or if you don't need to be able to change the content, a std::string_view:

template<class T>
std::string_view toBytes(const T& src) {
    return {reinterpret_cast<const char*>(&src),
            reinterpret_cast<const char*>(&src)   sizeof src};
}

You can then use either with send() etc:

auto res = toBytes(i_lengthOfCommand);
send(sockfd, res.data(), res.size(), flags);

CodePudding user response:

You don't need a function. And without explicitly passing the size of the array to a function, the pointer's length can never be correctly inferred.

Just use a cast to get what you want:

int32_t i_lengthOfCommand = 3;
char *s_lengthOfCommand = reinterpret_cast<char*>(&i_lengthOfCommand);
printf("%x %x %x %x", s_lengthOfCommand[0], s_lengthOfCommand[1], s_lengthOfCommand[2], s_lengthOfCommand[3]);

On a little endian system, such as all Intel chips, the above code will print 3 0 0 0. the least significant byte value in i_lengthOfCommand will get printed first.

On a Big Endian system, it would print 0 0 0 3.

CodePudding user response:

If your intention is for endian support have a look at the boost endian library: https://www.boost.org/doc/libs/1_71_0/libs/endian/doc/html/endian.html

Here's a example of enforcing big endian https://www.boost.org/doc/libs/1_71_0/libs/endian/doc/html/endian.html#buffers_example

Generally speaking you want to establish of the library use case then making the necessary conversions not make a general library then find out it doesn't fit your use case. If it's just endian support boost endian is simpler then rolling your own.

CodePudding user response:

There is a lot wrong here

char* toBytes(void* src, int byteSize) {
    char convertedToBytes[sizeof(src)];
    memcpy(&convertedToBytes, src, byteSize);
    return _strdup(convertedToBytes);
}

first

char convertedToBytes[sizeof(src)];

will always allocate 4 or 8 bytes depending on whether 32 or 64 bit executable. You cannot find the size of an array from a pointer that points at it. I mean you already pass in the length as the second arg

Next why do you have an intermediate buffer anyway

    memcpy(&convertedToBytes, src, byteSize);

But this is incorrect anyway, you need

    memcpy(convertedToBytes, src, byteSize);

Then you copy the bytes to the heap. But strdup needs a null terminated string, there is no guarantee that your void* points to such a thing

You function doesnt 'convert' anything all it does is a very fiddly memcpy

The whole thing can be replaced by

   char* toBytes(void* src, int byteSize) {
        return (char*)src;
   }

CodePudding user response:

Okay, let's look.

char* toBytes(void* src) {
    char convertedToBytes[sizeof(src)];
    memcpy(&convertedToBytes, src, sizeof(src));
    return _strdup(convertedToBytes);
}

Let's start with this. char is signed. So some of the values will be positive, and some will be negative. You should use unsigned char instead.

The last line of your method is troubling. It assumes you have a C-style string, but you don't.

C-style strings are a series of characters with one at the end that is a zero-byte. And characters are represented in ASCII. That strdup() call is going to return garbage, because you have random non-ASCII data in your array.

You are probably on 64-bit architecture, so your array is 8 bytes long with no guarantee of a null byte. But if the address is low enough, you have a LEADING null byte.

Let's say src points to address 0x00 00 00 00 12 34 56 78. I put spaces in to make a point. That's eight bytes with their hex values.

So now your strdup is going to see the leading 00 and say, "Oh, end of string". But of course, your address could be far more complicated than this example, and it could be 8 bytes of non-zeros, or anything in between. Not good.

strdup() is the wrong method. What you have to do is build a string as you go. The easiest way is to use one of the various number-to-string methods. Such as this:

std::string result;
char holder[10];
for (int index = 0; index < sizeof(src);   index) {
    sprintf(holder, "%0x", convertedToBytes[index]);
    result  = holder;
}

Then if your method returns a std::string, you can return result.

CodePudding user response:

You may consider something more modern (post C 17). I would probably do something more like this, utilizing std::array.

template<typename T>
std::array<std::byte, sizeof(T)> toBytes(T const& t)
{
    std::array<std::byte, sizeof(T)> a;
    std::copy((std::byte const*)&t, (std::byte const*)(&t   1), a.data());
    return a;
}

in main()
{
    int32_t i_lengthOfCommand = 3;
    auto s_lengthOfCommand = toBytes(i_lengthOfCommand);
    printf("%x %x %x %x", int(s_lengthOfCommand[0]), int(s_lengthOfCommand[1]), int(s_lengthOfCommand[2]), int(s_lengthOfCommand[3]));
}

The benefit of using a std::array is that you don't risk dangling pointers or memory leaks, it remembers its length and is easily iterable. For example:

for(auto b: s_lengthOfCommand)
    std::cout << std::hex << b << ' ';
std::cout << '\n';
  •  Tags:  
  • c
  • Related