memset char * with Integer

1

I have to memset 4 bytes in a char * with an integer.

For exemple I have an integer int i = 3276854 (0x00320036) and a char header[54] = {0}. I have to write i at header + 2, as if I print memory from header[2] to header[6], I get :

0032 0036

I tried this:

memset(&header[2], i, 1);

But it seems to put only the last byte into header, I get :

0036 0000

I also tried:

memset(&header[2], i, 4);

But it fill each byte with the last byte of i, I get :

3636 3636

I also tried to use binary masks like that :

ft_memset(&header[2], (int)(54 + size) & 0xff000000, 1);
ft_memset(&header[3], (int)(54 + size) & 0x00ff0000, 1);
ft_memset(&header[4], (int)(54 + size) & 0x0000ff00, 1);
ft_memset(&header[5], (int)(54 + size) & 0x000000ff, 1);

I get :

3600 0000.

So I don't know how I can get my 0032 0036, or at least 3600 3200 (maybe there is a thing with little and big endian into that, because I run it under MacOS, which is big endian).

c
memory
endianness
memset
asked on Stack Overflow Nov 28, 2019 by 185.165.168.41 • edited Nov 28, 2019 by 185.165.168.41

2 Answers

2

memset fills memory with a constant byte value. The second parameter (of type int) is converted to an unsigned char value.

You could use memcpy like this:

memcpy(&header[2], &i, sizeof(i));

However, it depends what exactly you are trying to achieve. If the header needs the integer to be in a particular format, you may need to convert the value in some way. For example, if the value needs to be big-endian (which is also known as "network byte order" in several Internet protocols), you can convert it with the htonl function:

uint32_t bi = htonl(i);
memcpy(&header[2], &bi, sizeof(bi));

(The htonl function is defined by #include <arpa/inet.h>.)

Also check the newer byte order conversion functions htobe16, htole16, be16toh, le16toh, htobe32, htole32, be32toh, le32toh, htobe64, htole64, be64toh, and le64toh declared by:

#define _BSD_SOURCE
#include <endian.h>

These convert between host byte order and little-endian byte order, or between host byte order and big-endian byte order, and work on uint16_t, uint32_t or uint64_t values, depending on the function name.

If there are no equivalents to those byte-order conversion functions provided on your system the following non-optimized, but portable (on implementations that support uint16_t, uint32_t and uint64_t) functions may be used:

myendian.h

#ifndef MYENDIAN_H__INCLUDED_
#define MYENDIAN_H__INCLUDED_

#include <stdint.h>

uint16_t my_htobe16(uint16_t h16);
uint16_t my_htole16(uint16_t h16);
uint16_t my_be16toh(uint16_t be16);
uint16_t my_le16toh(uint16_t le16);

uint32_t my_htobe32(uint32_t h32);
uint32_t my_htole32(uint32_t h32);
uint32_t my_be32toh(uint32_t be32);
uint32_t my_le32toh(uint32_t le32);

uint64_t my_htobe64(uint64_t h64);
uint64_t my_htole64(uint64_t h64);
uint64_t my_be64toh(uint64_t be64);
uint64_t my_le64toh(uint64_t le64);

#endif

myendian.c

#include "myendian.h"

union swab16
{
    uint16_t v;
    uint8_t b[2];
};

union swab32
{
    uint32_t v;
    uint8_t b[4];
};

union swab64
{
    uint64_t v;
    uint8_t b[8];
};

static uint16_t xbe16(uint16_t x)
{
    union swab16 s;

    s.b[0] = (x >> 8) & 0xffu;
    s.b[1] = x & 0xffu;
    return s.v;
}

static uint16_t xle16(uint16_t x)
{
    union swab16 s;

    s.b[0] = x & 0xffu;
    s.b[1] = (x >> 8) & 0xffu;
    return s.v;
}

static uint32_t xbe32(uint32_t x)
{
    union swab32 s;

    s.b[0] = (x >> 24) & 0xffu;
    s.b[1] = (x >> 16) & 0xffu;
    s.b[2] = (x >> 8) & 0xffu;
    s.b[3] = x & 0xffu;
    return s.v;
}

static uint32_t xle32(uint32_t x)
{
    union swab32 s;

    s.b[0] = x & 0xffu;
    s.b[1] = (x >> 8) & 0xffu;
    s.b[2] = (x >> 16) & 0xffu;
    s.b[3] = (x >> 24) & 0xffu;
    return s.v;
}

static uint64_t xbe64(uint64_t x)
{
    union swab64 s;

    s.b[0] = (x >> 56) & 0xffu;
    s.b[1] = (x >> 48) & 0xffu;
    s.b[2] = (x >> 40) & 0xffu;
    s.b[3] = (x >> 32) & 0xffu;
    s.b[4] = (x >> 24) & 0xffu;
    s.b[5] = (x >> 16) & 0xffu;
    s.b[6] = (x >> 8) & 0xffu;
    s.b[7] = x & 0xffu;
    return s.v;
}

static uint64_t xle64(uint64_t x)
{
    union swab64 s;

    s.b[0] = x & 0xffu;
    s.b[1] = (x >> 8) & 0xffu;
    s.b[2] = (x >> 16) & 0xffu;
    s.b[3] = (x >> 24) & 0xffu;
    s.b[4] = (x >> 32) & 0xffu;
    s.b[5] = (x >> 40) & 0xffu;
    s.b[6] = (x >> 48) & 0xffu;
    s.b[7] = (x >> 56) & 0xffu;
    return s.v;
}

uint16_t my_htobe16(uint16_t h16)
{
    return xbe16(h16);
}

uint16_t my_htole16(uint16_t h16)
{
    return xle16(h16);
}

uint16_t my_be16toh(uint16_t be16)
{
    return xbe16(be16);
}

uint16_t my_le16toh(uint16_t le16)
{
    return xle16(le16);
}

uint32_t my_htobe32(uint32_t h32)
{
    return xbe32(h32);
}

uint32_t my_htole32(uint32_t h32)
{
    return xle32(h32);
}

uint32_t my_be32toh(uint32_t be32)
{
    return xbe32(be32);
}

uint32_t my_le32toh(uint32_t le32)
{
    return xle32(le32);
}

uint64_t my_htobe64(uint64_t h64)
{
    return xbe64(h64);
}

uint64_t my_htole64(uint64_t h64)
{
    return xle64(h64);
}

uint64_t my_be64toh(uint64_t be64)
{
    return xbe64(be64);
}

uint64_t my_le64toh(uint64_t le64)
{
    return xle64(le64);
}

Test harness: myendiantest.c

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include "myendian.h"

#define TEST(n, fn, v) \
    printf("%s(%#" PRIx##n ") = %#" PRIx##n "\n", #fn, (v), (fn)(v))

int main(void)
{
    const uint16_t t16 = UINT16_C(0x1234);
    const uint32_t t32 = UINT32_C(0x12345678);
    const uint64_t t64 = UINT64_C(0x123456789abcdef);

    TEST(16, my_htobe16, t16);
    TEST(16, my_htole16, t16);
    TEST(16, my_be16toh, t16);
    TEST(16, my_le16toh, t16);

    TEST(32, my_htobe32, t32);
    TEST(32, my_htole32, t32);
    TEST(32, my_be32toh, t32);
    TEST(32, my_le32toh, t32);

    TEST(64, my_htobe64, t64);
    TEST(64, my_htole64, t64);
    TEST(64, my_be64toh, t64);
    TEST(64, my_le64toh, t64);

    return 0;
}

Output on a little endian system:

my_htobe16(0x1234) = 0x3412
my_htole16(0x1234) = 0x1234
my_be16toh(0x1234) = 0x3412
my_le16toh(0x1234) = 0x1234
my_htobe32(0x12345678) = 0x78563412
my_htole32(0x12345678) = 0x12345678
my_be32toh(0x12345678) = 0x78563412
my_le32toh(0x12345678) = 0x12345678
my_htobe64(0x123456789abcdef) = 0xefcdab8967452301
my_htole64(0x123456789abcdef) = 0x123456789abcdef
my_be64toh(0x123456789abcdef) = 0xefcdab8967452301
my_le64toh(0x123456789abcdef) = 0x123456789abcdef

Output on a big endian system (expected, but not tested by me):

my_htobe16(0x1234) = 0x1234
my_htole16(0x1234) = 0x3412
my_be16toh(0x1234) = 0x1234
my_le16toh(0x1234) = 0x3412
my_htobe32(0x12345678) = 0x12345678
my_htole32(0x12345678) = 0x78563412
my_be32toh(0x12345678) = 0x12345678
my_le32toh(0x12345678) = 0x78563412
my_htobe64(0x123456789abcdef) = 0x123456789abcdef
my_htole64(0x123456789abcdef) = 0xefcdab8967452301
my_be64toh(0x123456789abcdef) = 0x123456789abcdef
my_le64toh(0x123456789abcdef) = 0xefcdab8967452301
answered on Stack Overflow Nov 28, 2019 by Ian Abbott • edited Nov 28, 2019 by Ian Abbott
0

You could use memcpy but that would make your code dependent on the underlying endianess of the CPU. I don't think that's what you want.

My take is that you want to convert to the network endianess of whatever data communications protocol this is, regardless of CPU endianess.

The only way to achieve that is bit shifts. The byte order depends on the target network endianess:

void u32_to_big_endian (uint8_t* dst, const uint32_t src)
{
  dst[0] = (uint8_t) ((src >> 24) & 0xFFu);
  dst[1] = (uint8_t) ((src >> 16) & 0xFFu);
  dst[2] = (uint8_t) ((src >>  8) & 0xFFu);
  dst[3] = (uint8_t) ((src >>  0) & 0xFFu);
}

void u32_to_little_endian (uint8_t* dst, const uint32_t src)
{
  dst[3] = (uint8_t) ((src >> 24) & 0xFFu);
  dst[2] = (uint8_t) ((src >> 16) & 0xFFu);
  dst[1] = (uint8_t) ((src >>  8) & 0xFFu);
  dst[0] = (uint8_t) ((src >>  0) & 0xFFu);
}

In these functions, it doesn't matter what the CPU endianess is. You can forget about memcpy() and non-standard endianess conversion functions. Full example:

#include <stdio.h>
#include <stdint.h>

void u32_to_big_endian (uint8_t* dst, const uint32_t src)
{
  dst[0] = (uint8_t) ((src >> 24) & 0xFFu);
  dst[1] = (uint8_t) ((src >> 16) & 0xFFu);
  dst[2] = (uint8_t) ((src >>  8) & 0xFFu);
  dst[3] = (uint8_t) ((src >>  0) & 0xFFu);
}

void u32_to_little_endian (uint8_t* dst, const uint32_t src)
{
  dst[3] = (uint8_t) ((src >> 24) & 0xFFu);
  dst[2] = (uint8_t) ((src >> 16) & 0xFFu);
  dst[1] = (uint8_t) ((src >>  8) & 0xFFu);
  dst[0] = (uint8_t) ((src >>  0) & 0xFFu);
}


int main(void)
{
  uint32_t i = 0x00320036u; 
  uint8_t header[54] = {0};

  u32_to_little_endian(&header[2], i);
  for(size_t i=0; i<6; i++)
  {
    printf("%.2x ", (unsigned int)header[i]);
  }
}

Output, including the first 2 bytes as zeroes:

00 00 36 00 32 00
answered on Stack Overflow Nov 28, 2019 by Lundin • edited Nov 28, 2019 by Lundin

User contributions licensed under CC BY-SA 3.0