Search code examples
carraysbit-manipulationnibble

C: Implementing array of nibbles


I am trying to stuff 16 unsigned values into 8 bytes (64 bit), and access them using an array-like syntax.
Every entry in the "array" will be one nibble - 4 bit long. (The values I plan to store are never bigger than 15).

My first attempt was this:

int main(int argc, char* argv[]) {

    union nibbles_array {
        uint64_t as_long;

        struct inner_array {
            unsigned entry0  : 4;
            unsigned entry1  : 4;
            unsigned entry2  : 4;
            unsigned entry3  : 4;
            unsigned entry4  : 4;
            unsigned entry5  : 4;
            unsigned entry6  : 4;
            unsigned entry7  : 4;
            unsigned entry8  : 4;
            unsigned entry9  : 4;
            unsigned entry10 : 4;
            unsigned entry11 : 4;
            unsigned entry12 : 4;
            unsigned entry13 : 4;
            unsigned entry14 : 4;
            unsigned entry15 : 4;
        } as_array;     
    } array;

    array.as_long = 0x0123456789abcdef;

    printf("%d \n", array.as_array.entry0);
    printf("%d \n", array.as_array.entry1);
    printf("%d \n", array.as_array.entry2);
    printf("%d \n", array.as_array.entry3);
    printf("%d \n", array.as_array.entry4);

    return 0;
}

Two problems arises from this implementation: the first is that the values are stored in reverse order. I can, of course, assign the values in reverse order to get the desired result: array.as_long = 0xfedcba9876543210, but I want this code to be portable, and not endianness-dependent. The second is that I can't access the nibbles with an index, in an array-like syntax.

The second attempt was this:

int main(int argc, char* argv[]) {

    uint64_t pseudo_array = 0x0123456789abcdef;

    #define Array(i) (unsigned)((pseudo_array & (0xfUL << i)) >> i)

    int i;
    for (i = 0; i < 16; ++i) {
        printf("%d ", Array(i));
    }

    printf("\n");

    return 0;
}

The above can solve the second problem (array-like syntax); now I can access "elements" with index, but the problem of endianness remains, plus this produces wrong output:

15 7 11 13 14 15 7 11 13 6 3 9 12 14 15 7

  1. Why is the above produces this output?
  2. Can you please suggest implementation that will both allow me to access the "array" by index and will solve the endianness problem?

Solution

  • Your first attempt definitely has portability issues: the mapping between bitfields and actual bits in memory is not defined in the Standard. It is not exactly an endianness issue, and you cannot have an array like syntax either.

    Your second attempt is much more portable. The issue is not endianness either, but your own conception of what is the n-th element of the array. The macro is erroneous because you do not shift by the correct number of bits: i must be multiplied by 4. I would suggest this macro to fit your understanding:

    #define Array(a, i)  ((unsigned)(((a) >> (60 - 4 * (i))) & 0xf))