C:实现半字节数组
C: Implementing array of nibbles
我正在尝试将 16 个无符号值填充到 8 个字节(64 位)中,并使用类似数组的语法访问它们。
"array" 中的每个条目都是一个半字节 - 4 位长。 (我计划存储的值永远不会超过 15)。
我的第一次尝试是这样的:
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;
}
这个实现产生了两个问题:第一个是值的存储顺序相反。当然,我可以按相反的顺序分配值以获得所需的结果:array.as_long = 0xfedcba9876543210
,但我希望此代码是可移植的,而不是字节顺序相关的。
第二个是我无法以类似数组的语法访问带有索引的半字节。
第二次尝试是这样的:
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;
}
以上可以解决第二个问题(类数组语法);现在我可以使用索引访问 "elements",但是字节序问题仍然存在,而且这会产生错误的输出:
15 7 11 13 14 15 7 11 13 6 3 9 12 14 15 7
- 为什么上面会产生这个输出?
- 你能否建议允许我通过索引 访问 "array" 和 来解决字节序问题的实施方案?
您的第一次尝试肯定存在可移植性问题:位域与内存中实际位之间的映射未在标准中定义。这不完全是一个字节序问题,你也不能有类似数组的语法。
你的第二次尝试更便携。问题也不在于字节序,而是您自己对 数组 的第 n 个元素的概念。该宏是错误的,因为您没有按正确的位数移动:i
必须乘以 4。我建议使用此宏来满足您的理解:
#define Array(a, i) ((unsigned)(((a) >> (60 - 4 * (i))) & 0xf))
不是将 "vector" 存储为位域,而是可以使用动态分配的数组和一组函数来访问数组的单个 "indexes" 以及设置或获取更大部分的函数的 "array" 使用更大的类型(例如 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)。
出现异常输出是宏定义错误造成的;你的意思是移动半字节,而不是位。可以通过如下定义宏来实现所需的行为。
#define Array(i) (unsigned)((pseudo_array & ((uint64_t)0x0f << (4*i))) >> (4*i))
这样的事情怎么样:
#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;
}
我相信@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))
我正在尝试将 16 个无符号值填充到 8 个字节(64 位)中,并使用类似数组的语法访问它们。
"array" 中的每个条目都是一个半字节 - 4 位长。 (我计划存储的值永远不会超过 15)。
我的第一次尝试是这样的:
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;
}
这个实现产生了两个问题:第一个是值的存储顺序相反。当然,我可以按相反的顺序分配值以获得所需的结果:array.as_long = 0xfedcba9876543210
,但我希望此代码是可移植的,而不是字节顺序相关的。
第二个是我无法以类似数组的语法访问带有索引的半字节。
第二次尝试是这样的:
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;
}
以上可以解决第二个问题(类数组语法);现在我可以使用索引访问 "elements",但是字节序问题仍然存在,而且这会产生错误的输出:
15 7 11 13 14 15 7 11 13 6 3 9 12 14 15 7
- 为什么上面会产生这个输出?
- 你能否建议允许我通过索引 访问 "array" 和 来解决字节序问题的实施方案?
您的第一次尝试肯定存在可移植性问题:位域与内存中实际位之间的映射未在标准中定义。这不完全是一个字节序问题,你也不能有类似数组的语法。
你的第二次尝试更便携。问题也不在于字节序,而是您自己对 数组 的第 n 个元素的概念。该宏是错误的,因为您没有按正确的位数移动:i
必须乘以 4。我建议使用此宏来满足您的理解:
#define Array(a, i) ((unsigned)(((a) >> (60 - 4 * (i))) & 0xf))
不是将 "vector" 存储为位域,而是可以使用动态分配的数组和一组函数来访问数组的单个 "indexes" 以及设置或获取更大部分的函数的 "array" 使用更大的类型(例如 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)。
出现异常输出是宏定义错误造成的;你的意思是移动半字节,而不是位。可以通过如下定义宏来实现所需的行为。
#define Array(i) (unsigned)((pseudo_array & ((uint64_t)0x0f << (4*i))) >> (4*i))
这样的事情怎么样:
#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;
}
我相信@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))