在 void* 中使用指针算法

using pointer aritmetic in void*

我的机器是ubuntu20.04

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

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

这是我的代码(大小是参数类型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]”

我必须将长度存储在第一个元素中,因为赋值说明了这一点,但我没有警告我想编写干净的代码。是合法的。我已经阅读 Pointer arithmetic when void has unknown size 但找不到解决问题的答案。

当您使用指针运算 (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);

在这种情况下,data是一个void *,所以指针运算对于编译器来说是不清楚的(需要计算下一个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 returns 字节数又名 uint8_t。)

如您所见,除非您使用 GCC 扩展或类似扩展,否则无法使用 void* 进行算术运算。

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

在您的代码中您已经拥有

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

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

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

使用它来获取呼叫者的地址:

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

当您想在 myfree 函数中返回 header 时,您可以使用类似的技巧:

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

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

注:

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

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

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