可以将不同的数据类型存储在C中相同的已分配内存中吗?

时间:2015-12-31 17:03:38

标签: c memory memory-management

我想在同一个分配的内存中存储不同的数据类型,以便通过仅分配一次内存来减少执行时间。我发现实际上可以创建一个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;
}

2 个答案:

答案 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])应该总是正常的。但是,它应该需要演员。你的编译器应该警告你。

如果您计划将指针转换为已分配块内部的任意位置,则不能依赖对齐是正确的。

另请注意,使用此方案可能会损害性能,而不是改善性能。特别是,某些处理器对于正确对齐的数据具有更好的加载和存储性能,即使它们可以处理其他对齐。您需要为每个块支付一次内存分配费用 - 您需要支付每次访问时未对齐访问的任何费用。

  

我知道我需要注意内存的界限,但这种风格是不是很糟糕?

是的,非常。您正在牺牲代码清晰度来获得不确定的假设性能增益。增加的开发和维护成本可能会淹没任何性能提升,特别是因为您也混淆了编译器的意图,从而使生成高效的机器代码变得更加困难。

此外,开始进行这种微优化可能还为时过早。首先让你的程序正常工作。如果它不够快,测试找到瓶颈并专注于改进这些部分。您最有可能通过选择更好的算法来提高性能;像你提出的小技巧很少值得。