3

I have some char array: char char[8] which containing for example two ints, on first 4 indexes is first int, and on next 4 indexes there is second int.

char array[8] = {0,0,0,1,0,0,0,1};
int a = array[0-3]; // =1;  
int b = array[4-8]; // =1;

How to cast this array to two int's?

There can be any other type, not necessarily int, but this is only some example:

I know i can copy this array to two char arrays which size will be 4 and then cast each of array to int. But i think this isn't nice, and breaks the principle of clean code.

Maciej Wojcik
  • 2,115
  • 2
  • 28
  • 47

3 Answers3

6

If your data has the correct endianness, you can extract blitable types from a byte buffer with memcpy:

int8_t array[8] = {0,0,0,1,0,0,0,1};
int32_t a, b;
memcpy(&a, array + 0, sizeof a);
memcpy(&b, array + 4, sizeof b);

While @Vivek is correct that ntohl can be used to normalize endianness, you have to do that as a second step. Do not play games with pointers as that violates strict aliasing and leads to undefined behavior (in practice, either alignment exceptions or the optimizer discarding large portions of your code as unreachable).

int8_t array[8] = {0,0,0,1,0,0,0,1};
int32_t tmp;
memcpy(&tmp, array + 0, sizeof tmp);
int a = ntohl(tmp);
memcpy(&tmp, array + 4, sizeof tmp);
int b = ntohl(tmp);

Please note that almost all optimizing compilers are smart enough to not call a function when they see memcpy with a small constant count argument.

Community
  • 1
  • 1
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
1

Let's use a little bit of the C++ algorithms, such as std::accumulate:

#include <numeric>
#include <iostream>

int getTotal(const char* value, int start, int end)     
{
    return std::accumulate(value + start, value + end, 0, 
                           [](int n, char ch){ return n * 10 + (ch-'0');});
}

int main()
{
    char value[8] = {'1','2','3','4','0','0','1','4'};
    int total1 = getTotal(value, 0, 4);
    int total2 = getTotal(value, 4, 8);
    std::cout << total1 << " " << total2;
}

Note the usage of std::accumulate and the lambda function. All we did was have a running total, multiplying each subtotal by 10. The character is translated to a number by simply subtracting '0'.

Live Example

PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45
  • 1
    If the `char[]` contains ASCII text with fixed-width decimal numbers, then this would be a good approach. I interpreted the question as having binary data not text, but it didn't actually say so this could be correct. – Ben Voigt Nov 21 '16 at 23:22
  • ^ same. I thought it was supposed to be more of a cast than an `atoi`. – Charles Nov 21 '16 at 23:36
-1

You can type cast the bytes from the array to an int *. Then dereferencing will cause 4 bytes to be read as an int. Then doing an ntohl, will ensure that the bytes in the int are arranged as per the host order.

char array[8] = {0,0,0,1,0,0,0,1};

int a = *((int *)array);  
int b = *((int *)&array[4]);

a = ntohl(a);  
b = ntohl(b);

This will set a and b to 1 on both little and big endian systems.

If the compiler is set for strict aliasing, memcpy could be used to achieve the same, as follows:

char array[8] = {0,0,0,1,0,0,0,1};
int a, b;

memcpy(&a, array, sizeof(int));
memcpy(&b, array+4, sizeof(int));

a = ntohl(a);  
b = ntohl(b);  
Vivek
  • 320
  • 1
  • 5
  • 3
    This is undefined behavior. Strict aliasing allows you to access an `int` using `char*`, it does not allow you to access a `char[]` using `int*`. Badness ensues. – Ben Voigt Nov 21 '16 at 22:56
  • It works on a little endian system. i just tried it out to double check. memcpy could be used as well. – Vivek Nov 21 '16 at 22:56