数据类型及指针大小

最近写代码,内存分配不管二级还是一级全放在一起申请,导致各种地址偏移,转换。这里记录几个常见的数据的大小

#include <iostream>
#include <stdint.h>
int main() {
  std::cout << "void** :" << sizeof(void**) << std::endl;
  std::cout << "void* :" << sizeof(void*) << std::endl;
  std::cout << "int** :" << sizeof(int**) << std::endl;
  std::cout << "int* :" << sizeof(int*) << std::endl;
  std::cout << "int :" << sizeof(int) << std::endl;
  std::cout << "uint8_t* :" << sizeof(uint8_t*) << std::endl;
  std::cout << "uint8_t :" << sizeof(uint8_t) << std::endl;
  std::cout << "char :" << sizeof(char) << std::endl;
  return 0;
}

实验

我打算申请这样一块地址

  /* ------- 4 * int --------- 2 * (int *) -------- (int **) --- */
  /* ----------16------------------16------------------8-------- */
  int size = 4 * sizeof(int) + 2 * sizeof(int *) + sizeof(int **);
  void* ptr = new uint8_t[size];
  void* ptr_bak = new uint8_t[size];
  delete[] static_cast<uint8_t*>(ptr);
  delete[] static_cast<uint8_t*>(ptr_bak);

前4个int大小的放四个int数据, 中间2个int* 大小的将前4个int 分成2个int* 的地址存放,最后一个int**,放前两个int*的地址。

  int* int_ptr = reinterpret_cast<int *>(ptr);
  int_ptr[0] = 0;
  int_ptr[1] = 1;
  int_ptr[2] = 2;
  int_ptr[3] = 3;
  int test = *(static_cast<int*>(ptr) + 1);
  std::cout << "test : " << test << std:: endl;

这里ptr是用new uint8_t申请的,而一个uint8_t是1。返回的是uint8_t*。那么当前的ptr是一个大小为40的uint8_t的数组。
同理我将uint8_t*的指针转成int*的指针,那么int_ptr 就类似表示为大小为8的int数组。我对ptr数组的前四个,分别赋值为0,1,2,3.

int test = *(static_cast<int*>(ptr) + 1);

这行的意思就是先将ptr转换成int*的指针,那么实际的每个元素就是一个int,那么+1的意思就是加1个int的偏移。
最外层的*就是解引用,将int_ptr[1]的值取出来。这里一定要小心,不要以为int为四个字节,就需要加上4.
所以上面的打印结果就是1.

接下来我们将前面的4个int,分为两组,然将这个两个值赋值给后面的两个``int*```

  int** int_s_ptr = reinterpret_cast<int**>(static_cast<int**>(ptr) + 2);
  int_s_ptr[0] = &int_ptr[0];
  int_s_ptr[1] = &int_ptr[2];

这里怎么看这段代码呢?首先我们把ptr想成里面放int*的数据,那么对于这些地址的地址就是一个int**的存在,
那么我们就应该先static_cast<int**>, 然后偏移两个int*,找到实际的int*,存放的地址。

在接下来我们将2个int*的地址放入最后一个int**中。

  int*** int_ss_ptr = reinterpret_cast<int***>(static_cast<int***>(ptr) + 4);
  int_ss_ptr[0] = &int_s_ptr[0];

对于最后一个应该放int**,那么该地址的指针就是int***的,然后就是前面偏4个int和2个int*,就是偏4个int**
最后我们验证下结果,将数组拷贝到ptr_bak中测试

#include <cstring>
#include <iostream>
#include <stdint.h>

int main(int argc, char **argv) {
  /* ------- 4 * int --------- 2 * (int *) -------- (int **) --- */
  /* ----------16------------------16------------------8-------- */
  int size = 4 * sizeof(int) + 2 * sizeof(int *) + sizeof(int **);
  void *ptr = new uint8_t[size];
  void *ptr_bak = new uint8_t[size];
  int *int_ptr = reinterpret_cast<int *>(ptr);
  int_ptr[0] = 0;
  int_ptr[1] = 1;
  int_ptr[2] = 2;
  int_ptr[3] = 3;
  int test = *(static_cast<int *>(ptr) + 1);
  std::cout << "test : " << test << std::endl;

  int **int_s_ptr = reinterpret_cast<int **>(static_cast<int **>(ptr) + 2);
  int_s_ptr[0] = &int_ptr[0];
  int_s_ptr[1] = &int_ptr[2];
  int ***int_ss_ptr = reinterpret_cast<int ***>(static_cast<int ***>(ptr) + 4);

  int_ss_ptr[0] = int_s_ptr;
  memcpy(ptr_bak, ptr, sizeof(uint8_t) * size);
  int ***test_ptr =
      reinterpret_cast<int ***>(static_cast<int ***>(ptr_bak) + 4);
  std::cout << "test_ptr[0][0][0] = " << test_ptr[0][0][0] << std::endl;
  std::cout << "test_ptr[0][0][1] = " << test_ptr[0][0][1] << std::endl;

  int **test_s_ptr = test_ptr[0];
  int *tt = test_s_ptr[1];
  std::cout << "test_ptr[0][1][0] = " << test_ptr[0][1][0] << std::endl;
  std::cout << "test_ptr[0][1][1] = " << test_ptr[0][1][1] << std::endl;

  delete[] static_cast<uint8_t *>(ptr);
  delete[] static_cast<uint8_t *>(ptr_bak);
  return 0;
}

运行结果

这里有个小技巧,就是申请这些地址,不知道到底应该当成什么地址看,那么可以直接将申请的的地址当成char* 或者uint8_t*之类的,这时候其实加1就是加1个字节的偏移,然后偏移好后
使用reinterpret_cast转成自己想要的地址就好了。
还有就是&[0]其实就是首地址,我这只是方便阅读而已。

posted @ 2020-11-25 15:28  cyssmile  阅读(669)  评论(0编辑  收藏  举报