我想在同一个分配的内存中存储不同的数据类型,以便通过仅分配一次内存来减少执行时间。我发现实际上可以创建一个uint8_t
变量数组并创建一个指向同一内存地址的新uint16_t
指针,然后我可以双向读取这些值。
这允许我创建一个指针,让我们说分配内存的中间位置,并将值存储在后半部分的不同数据类型中。
这样可以吗?我知道我需要关注内存边界,但这种风格是不是很糟糕?
这是我的代码:
#include <stdio.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdlib.h>
int main(void){
uint8_t *array;
uint16_t *array2;
array = calloc(6, 1);
array[0] = 257;
printf("array[0]= %" PRIu8 "\n", array[0]);
printf("array[1]= %" PRIu8 "\n", array[1]);
printf("Adresse von array[0] = %p\n", &array[0]);
array2 = &array[0];
printf("Adresse auf die array2 zeigt = %p\n", array2);
array2[0] = 257;
printf("array2[0]= %" PRIu16 "\n", array2[0]);
printf("array2[1]= %" PRIu16 "\n", array2[1]);
printf("array[0]= %" PRIu8 "\n", array[0]);
printf("array[1]= %" PRIu8 "\n", array[1]);
getchar();
return 0;
}
答案 0 :(得分:10)
使用union
创建一个有时存储一种类型的变量,有时是另一种类型。
union {
uint8_t u8;
uint16_t u16;
} *array_u;
size_t nmemb = 6;
array_u = calloc(nmemb, sizeof *array_u);
assert(array_u);
printf("array_u[0].u8 = %" PRIu8 "\n", array_u[0].u8);
array_u[0].u16 = 1234;
printf("array_u[0].u16 = %" PRIu16 "\n", array_u[0].u16);
...
这不会使用每个联合只有一个uint8_t u8
的所有空间。以下使用2 uint8_t
。
union {
uint8_t u8[2];
uint16_t u16;
} *array_u;
printf("array_u[0].u8[0] = %" PRIu8 "\n", array_u[0].u8[0]);
array_u[0].u16 = 1234;
printf("array_u[0].u16 = %" PRIu16 "\n", array_u[0].u16);
OTOH如果代码需要覆盖整个固定长度的数组
union {
uint8_t u8[12];
uint16_t u16[6];
} *array_u;
array_u = calloc(1, sizeof *array_u);
assert(array_u);
printf("array_u->u8[0] = %" PRIu8 "\n", array_u->u8[0]);
array_u->u16[0] = 1234;
printf("array_u->u16[0] = %" PRIu16 "\n", array_u->u16[0]);
...
答案 1 :(得分:4)
这样可以吗?
标准说:
指向对象类型的指针可以转换为指向不同对象类型的指针。如果生成的指针未针对引用的类型正确对齐,则行为未定义。否则,当再次转换回来时,结果将与原始指针进行比较。
(C2011,6.3.2.3/7)
请注意,您甚至不必使用转换的指针来产生未定义的行为 - 转换本身的行为是未定义的。
话虽如此,您的示例代码展示了一种特殊情况:成功调用calloc()
(或malloc()
或realloc()
)返回的指针保证适当地对齐任何类型的对象,所以你执行的特定指针转换(array2 = &array[0]
)应该总是正常的。但是,它应该需要演员。你的编译器应该警告你。
如果您计划将指针转换为已分配块内部的任意位置,则不能依赖对齐是正确的。
另请注意,使用此方案可能会损害性能,而不是改善性能。特别是,某些处理器对于正确对齐的数据具有更好的加载和存储性能,即使它们可以处理其他对齐。您需要为每个块支付一次内存分配费用 - 您需要支付每次访问时未对齐访问的任何费用。
我知道我需要注意内存的界限,但这种风格是不是很糟糕?
是的,非常。您正在牺牲代码清晰度来获得不确定的假设性能增益。增加的开发和维护成本可能会淹没任何性能提升,特别是因为您也混淆了编译器的意图,从而使生成高效的机器代码变得更加困难。
此外,开始进行这种微优化可能还为时过早。首先让你的程序正常工作。如果它不够快,测试找到瓶颈并专注于改进这些部分。您最有可能通过选择更好的算法来提高性能;像你提出的小技巧很少值得。