2015-12-08 3 views
3

Я пытаюсь записать 16 значений без знака в 8 байтов (64 бит) и получить к ним доступ с использованием синтаксиса типа массива.
Каждая запись в массиве будет одним полубайтом - длиной 4 бит. (Значения, которые я планирую хранить, никогда не больше 15).C: Реализация массива nibbles

Моя первая попытка была такова:

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 = 0xabcdef; 

    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; 
} 

Две проблемы проистекают из этой реализации: первая в том, что значения сохраняются в обратном порядке. Я могу, конечно, назначить значения в обратном порядке, чтобы получить желаемый результат: array.as_long = 0xfedcba, но я хочу, чтобы этот код был портативным, а не зависящим от него. Во-вторых, я не могу получить доступ к nibbles с индексом в синтаксисе типа массива.

Вторая попытка была такова:

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

    uint64_t pseudo_array = 0xabcdef; 

    #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; 
} 

выше может решить вторую задачу (массив-подобный синтаксис); Теперь я могу получить доступ к «элементов» с индексом, но проблема остается порядок байтов, плюс это дает неправильный вывод:

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

  1. Почему выше производит этот выход?
  2. Можете ли вы предложить реализацию, которая позволит мне получить доступ к «массиву» по индексу , а решит проблему с энтианностью?
+0

Что заставляет вас думать, что ваша вторая попытка все еще имеет проблему с энтианностью? –

+1

Вам нужно умножить 'i' на 4 при использовании его в качестве величины сдвига смешения (суммы сдвига в битах). Кроме того, вы можете устранить сдвиг, просто перенесив 'pseudo_array' справа на' 4 * i', затем замаскируйте (вы можете даже сделать перед маскировкой). –

+0

Пожалуйста, покажите определение 'uint64_t'. – Codor

ответ

2

Ваша первая попытка определенно имеет проблемы с переносимостью: сопоставление между битовыми полями и фактическими битами в памяти не определено в стандарте. Это не совсем проблема с контентом, и у вас тоже не может быть такой синтаксис.

Ваша вторая попытка гораздо более переносимая. Проблема также не в допущении, а в вашей собственной концепции того, что является n-м элементом массива . Макрос является ошибочным, потому что вы не сместится на правильное число битов: i нужно умножить на 4. Я хотел бы предложить этот макрос, чтобы соответствовать вашему пониманию:

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

«Проблема также не в сущности, а в вашей собственной концепции того, что является n-м элементом массива». Абсолютно верно. Наверное, это меня смутило. Спасибо за ваше решение, это сработает для меня. –

0

Вместо того, чтобы хранить «вектор», как битовых полей, может использовать динамически выделенный массив и набор функций для доступа к отдельным «индексам» вашего массива, а также функции для установки или получения больших частей «массива» с использованием более крупных типов (например, uint64_t).

Что-то вроде следующего API

// The "na" prefix stands for Nibble Array 

struct nibble_array 
{ 
    uint8_t *data; // The actual "array" of nibbles 
    size_t size; // Current size of "array" (number of elements) 
}; 

// Create an array containing a number of elements 
struct nibble_array *na_create(const size_t elements); 

// Get a value from specified index in array 
int na_get(const struct nibble_array *array, const size_t index); 

// Set a specified index in the array to a value 
void na_set(struct nibble_aray *array, const size_t index, const int value); 

// Get the number of elements (nibbles) in the array 
size_t na_size(const struct nibble_array *array); 

// Set a larger part of the array to some values 
void na_set64(struct nibble_array *array, size_t start_index, const uint64_t value); 

Вы не должны обеспечить определение nibble_array структуры, если вы хотите, чтобы данные были частными (читать о opaque pointers).

1

Нежелательный выход вызван тем, что макрос определен неверно; вы хотите сдвинуть nibble-wise, а не bit-wise. Желаемое поведение может быть достигнуто путем определения макроса следующим образом.

#define Array(i) (unsigned)((pseudo_array & ((uint64_t)0x0f << (4*i))) >> (4*i)) 
+0

Будьте осторожны, чтобы заключить в скобки «i» в расширении макроса. – chqrlie

0

Как о чем-то вроде этого:

#include <stdio> 

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

    unsigned char array[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }; 

    #define Array(i) (i%2 ? (array[i/2] & 0x0F) : ((array[i/2] & 0xF0) >> 4)) 

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

    printf("\n"); 
    return 0; 
} 
0

Я считаю @chqrlie правильно в отношении вопросов, порядок байт, но я думаю, что было бы проще просто использовать массив.

uint8_t ary[20]; /* 40 nibbles */ 
#define getnibblelen(ary) (sizeof(ary) * 2) /* works because it's an ARRAY of uint8_t, wouldn't work if you're passing the array to a function. */ 
#define getnibble(ary, idx) (idx % 2 ? /* if odd */ ary[idx/2] & 0xf0 >> 4 : /* if even */ ary[idx/2] & 0x0f) 
#define setnibble(ary, idx, val) (idx % 2 ? ary[idx/2] = (ary[idx/2] & 0x0f) | ((val & 0x0f) << 4) : ary[idx/2] = (ary[idx/2] & 0xf0) | (val & 0x0f)) 
+0

У меня нет 40 грызков, чтобы потратить ... У меня только 16. –

+1

В аргументе макросов должны быть заключены в скобки. Оценивать их не один раз следует избегать, поскольку это подвержено ошибкам. Если вы не можете этого достичь, используйте встроенные функции. – chqrlie

+0

@ so.very.tired: 40 был произвольным выбором, измените его на 8-байтовый массив. – drognisep