3

How do I read 3 bytes from unsigned char buffer at once (as a whole number)?

uint_24 p = *(unsigned char[3])buffer;

The above code doesn't work.

Imobilis
  • 1,475
  • 8
  • 29
  • 1
    `uint_24 p = buffer[0] + ((uint_24)buffer[1] << 8) + ((uint_24)buffer[2] << 16);`, that's assuming `buffer[0]` has the least significant byte. Change the order if needed. – lurker Apr 11 '15 at 12:24
  • Is that faster than reading it byte by byte instead? I was hoping for something that requires less mechanical computation. – Imobilis Apr 11 '15 at 12:26
  • What do you mean by *reading it byte by byte*? The shifts are needed to get the respective bytes into the correct positions in the final result. It is, essentially, reading byte by byte. Another potential option is `union` which would be a more direct insertion of bytes into their respective locations, but that's non-portable. – lurker Apr 11 '15 at 12:28
  • For instance if I attempt to cast a simple comparison between arbitrary 24 bit integers. A regular way would be `if(byte1 == byte2 && byte3 == byte4 && byte5 == byte6)` is that still faster after all.. – Imobilis Apr 11 '15 at 12:30
  • `if (p1 == p2)` (where `p1` and `p2` are the `uint_24` equivalents) is faster than that, if that's what you mean. – lurker Apr 11 '15 at 12:31
  • 2
    how did you define 'uint_24' ? – user3629249 Apr 11 '15 at 12:32
  • yes but `p1` and `pt` must be additionally computed with `byte1 + byte3 * 256 + byte5 * 256 * 256` or with the bitwise you presented. How is that faster. @user3629249 `typedef unsigned char uint_24 [0x3];` or it could be simply an `int` or something.. – Imobilis Apr 11 '15 at 12:33
  • 2
    You seem to have a larger context here that you haven't revealed affecting the answer you're looking for. Your question asked for the proper way to convert three consecutive bytes as a whole number, and what I gave is how you would do that. But if it's not satisfactory, then what are your overall requirements? When you represent your data, there is a trade between how you obtain it, how you represent it, how you process it, and how you display it (if needed). – lurker Apr 11 '15 at 12:38
  • Right, which is a comment not an answer. Counts as an answer but It isn't much "satisfactory" as it is not the most efficient solution either.. I was hoping for a way to directly read/write (access) byte trio, faster than `if(byte1 == byte2 && byte3 == byte4 && byte5 == byte6)` which performs different logical comparison for each byte instead of one comparison between 3-byte values. Why would I want that anyway.. if not for performance and/or easier management. – Imobilis Apr 11 '15 at 12:43
  • 2
    Yes, it was a comment not an answer because the question is unclear and I answered the one-liner question at face value with a one-liner suggestion. You haven't really given any context or requirements. If you have more to add in that regard, you should edit your question and elaborate rather than elaborating in the comments where it isn't visible to others reviewing and considering answering your question. – lurker Apr 11 '15 at 12:48
  • Maybe because there are no requirements other than the need of an efficient way, which is I believe... what everyone are looking for. So I will just wait for a better answer that for example.. lists all the ways, their downsides and positive sides and eventually.. a variety of test results. Otherwise it is halfway, therefore not very useful or "satisfactory" as you call it. – Imobilis Apr 11 '15 at 12:52
  • @user3629249 FYI: 24-bit integers available as an intrinsic type on various compilers using embedded processors that use 24-bit instructions (PIC24). Maybe that is where OP is getting this - maybe not. – chux - Reinstate Monica Apr 11 '15 at 15:59

3 Answers3

1

If the buffer can be redefined as part of a union and integer endian is as expected:

union {
  unsigned char buffer[3];
  uint_24 p;
} x;

foo(x.buffer);              // somehow data is loaded into x.buffer
uint_24 destination = x.p;  // read: let compiler do the work

By putting into a union, alignment issues are satisfied.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • One just needs to [be careful with `unions`](http://stackoverflow.com/questions/2310483/purpose-of-unions-in-c-and-c). At least up until C99, the `union` was only designed to provide a way to share storage, but no guarantee that you'd have the alignment that you would want in order to use it as a memory type conversion. – lurker Apr 11 '15 at 19:51
  • @lurker Agree about pre C99 concerns. – chux - Reinstate Monica Apr 11 '15 at 20:10
  • @Malina Note C11 (maybe c99?) offers `_Alignas`, This could be used to align `buffer` to agree with `uin_24` without using a union. I have not used it much. Of, course there is still the endian issue - but I assume your code can fill `buffer` as needed – chux - Reinstate Monica Apr 11 '15 at 22:49
  • @chux once I saw a trick of one using int - char type-casting and a pointer to char[3] `volatile char (*ptr)[3];` to do the same without more than a single minus computation or unions. It was in advanced programming pdf. – Imobilis Apr 11 '15 at 23:25
  • @Malina Unless alignment is insured, the "trick" you saw is not portable and is undefined behavior. What compiler/platform are you using? – chux - Reinstate Monica Apr 11 '15 at 23:28
  • I think it worked only on big-endian. But I forgot the details. But at least for me, portability is not an issue. I am working on a C89-emulated cross-platform environment that is OS-independent. – Imobilis Apr 11 '15 at 23:30
0

The short answer is: you can't (unless the machine int size is 3 bytes).

As machines generally have an even number of bytes as its int size (word size, register size), the hardware architecture will always fetch an even number of bytes from memory over the bus into its registers, or can fetch one single byte into a (lower) register. Hence the solutions provided in the comments to your question load a byte, shift it left and load the next byte etc. Alternatively you can fetch a word and AND-out the upper byte(s). You must also take the endianness into account. Lastly, not all machines can read ints starting at odd memory addersses, or they require them to be alligned at some even multiple.

Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
0

you can copy any number of bytes that you want as following:

#include <stdio.h>
void showbits(int n)
{
    int i,k,andmask;

    for(i=31;i>=0;i--)
    {
        andmask = 1 << i;
        k = n & andmask;

        k == 0 ? printf("0") : printf("1");
    }
    printf("\n");

}

int main()
{
    unsigned char buff[] = {'a',0,0,
                            0,'b',0,
                            0,0,'c'};
    //'a'=97=01100001
    //'b'=98=01100010
    //'c'=99=01100011
    void * src_ptr= (void *) buff;
    int i;
    for(i = 0 ; i < sizeof(buff) ; i += 3)
    {
        int num = 0 ;
        void * num_ptr = &num;
        memcpy(num_ptr , src_ptr , 3);
        showbits(num);
        src_ptr += 3;

    }

    return 0;
}

output:

00000000000000000000000001100001
00000000000000000110001000000000
00000000011000110000000000000000

houssam
  • 1,823
  • 15
  • 27