【问题标题】:using pointer aritmetic in void*在 void* 中使用指针算术
【发布时间】:2021-04-29 02:33:27
【问题描述】:

我的机器是ubuntu 20.04

我有一个任务说 “使用系统调用(mmap 和 munmap)实现您自己的动态内存分配函数:mymalloc 和 myfree,它们具有与 malloc 相同的功能,并且从标准 C 库中免费。将实现保存在文件 mymalloc.h 和 mymalloc.c 中。”

然后它说 “我们必须至少分配所需的长度 + 用于存储的变量的大小 长度(size_t)。我们将长度存储为第一个元素,然后返回下一个元素。”

这是我的代码(大小是 size_t 类型的参数)

size_t total_size = size + sizeof(size_t);

size_t allocation_overflow = total_size % page_size;

if(allocation_overflow > 0)
    total_size += page_size - allocation_overflow;

void *data = mmap(NULL ,total_size, PROT_EXEC | PROT_READ | PROT_WRITE , MAP_PRIVATE , 0 , 0);

  // size_ptr will point to stored size
  size_t *size_ptr =(size_t *)data;

  size_ptr[0] = total_size;


  // pointer to allocated memory
  void* allocated_mem_pointer = data + sizeof(size_t);

它给出警告“指针类型'void *'用于算术[-Wpointer arith]”

我必须将长度存储在第一个元素中,因为赋值说明了它,但我不警告我想编写干净的代码。 İS 有它的合法性。我已经读过Pointer arithmetic when void has unknown size 但找不到任何答案来解决它。

【问题讨论】:

  • 您可以将data 设为指向char (char*) 的指针。您正在递增从sizeof 返回的数字,sizeof(char) 保证为 1,因此无论如何您基本上都是在递增 sizeof(char) 的倍数。

标签: c memory malloc mmap


【解决方案1】:

当您使用指针算术 (ie add a number to pointer) 时,您需要正确键入指针:

IntPointer32Bits + 1 => will give the address of the next adjacent 32 bits integer
IntPointer16Bits + 1 => will give the address of the next adjacent 16 bits integer

指针运算的结果地址取决于指针类型。

void* allocated_mem_pointer = data + sizeof(size_t);

在这种情况下,datavoid *,因此编译器不清楚指针运算(需要计算下一个8 bitsnext 16 bitsnext 32 bits?)。你不应该使用void *,但你应该有一个真正的类型,我假设uint8_t

size_t total_size = size + sizeof(size_t);

size_t allocation_overflow = total_size % page_size;

if(allocation_overflow > 0)
    total_size += page_size - allocation_overflow;

uint8_t *data = mmap(NULL ,total_size, PROT_EXEC | PROT_READ | PROT_WRITE , MAP_PRIVATE , 0 , 0);

  // size_ptr will point to stored size
  size_t *size_ptr =(size_t *)data;

  size_ptr[0] = total_size;


  // pointer to allocated memory
  uint8_t *allocated_mem_pointer = data + sizeof(size_t);

在这种情况下,编译器的意图非常明确:allocated_mem_pointer 是由uint8_t 组成的内存区域,计算的地址是data 的基地址,偏移量为size_t 字节。 (运算符sizeof 返回字节数又名uint8_t。)

【讨论】:

  • 但是如果我键入 cast 我的 void 指针数据可以毫无问题地返回那个“数据”指针。也许不是第一次尝试,但多次调用是否有任何问题
  • 是的,您也可以在进行算术运算之前对其进行转换。 void* allocated_mem_pointer = (uint8_t *)data + sizeof(size_t); 应该也可以。
【解决方案2】:

正如您所发现的,除非您使用 GCC 扩展或类似的东西,否则您无法使用 void* 进行算术运算。

简单的解决方案是:不要使用void*

在您的代码中,您已经拥有

  // size_ptr will point to stored size
  size_t *size_ptr =(size_t *)data;
  size_ptr[0] = total_size;

现在您要计算计数器标头之后的地址。

arr[0] 之后的元素就是arr[1]

使用它来获取调用者的地址:

  // pointer to allocated memory
  void* allocated_mem_pointer = &size_ptr[1];

当您想在 myfree 函数中返回该标题时,您可以执行类似的技巧:

void myfree(void*addr) {
   size_t *buff = (size_t*) addr;
   size_t header = buff[-1];
   // ... 
}

这是有效的,因为buff[-1] 指向最初在您的myalloc 函数中分配的内存对象。

注意:

如果您要创建一个不仅用于教育目的的函数,您还需要处理返回给调用者的地址的正确对齐。

【讨论】:

  • 你能告诉我这些 GCC 扩展是什么吗?谢谢你的回答
  • GCC 允许对void* 进行计算。就好像它是char*。与所有扩展一样,它们不是可移植的,除非您确实需要,否则我不建议使用它们。显然,这个扩展从来都不是这种情况。
  • 我相当怀疑在这种情况下正确的解决方案是使用某种方式的结构指针,其中第一项是size_t。最后可能是一个灵活的数组成员:typedef struct { size_t size; uint8_t data[]; } heapseg_t; 或类似的。
【解决方案3】:

在处理void* 时,对char*unsigned char* 指针进行算术运算。

void *allocated_mem_pointer = (char*)data + sizeof(size_t);

【讨论】:

  • (size_t *)data + 1
  • 我想你最好解释一下原因
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-30
  • 2015-01-01
  • 1970-01-01
  • 2014-04-09
  • 2020-10-03
  • 1970-01-01
相关资源
最近更新 更多