C ++ static_cast从int *到void *到char * - 你能帮我理解这段代码吗?

时间:2013-07-04 13:36:06

标签: c++ static-cast

我是C ++的初学者,我对理解某些代码有疑问。

我做了一个练习,要编写返回int大小的函数,不要使用sizeof()reinterpret_cast。有人给了我解决方案,但我不明白它是如何工作的。你能帮我理解一下吗?这是代码:

int intSize() {
  int intArray[10];
  int * intPtr1;
  int * intPtr2;

  intPtr1 = &intArray[1];
  intPtr2 = &intArray[2];

  //Why cast int pointer to void pointer?
  void* voidPtr1 = static_cast<void*>(intPtr1);

  //why cast void pointer to char pointer?
  char* charPtr1 = static_cast<char*>(voidPtr1);

  void* voidPtr2 = static_cast<void*>(intPtr2);
  char* charPtr2 = static_cast<char*>(voidPtr2);

  //when I try to print 'charPtr1' there is nothing printed
  //when try to print charPtr2 - charPtr1, there is correct value shown - 4, why?
  return charPtr2 - charPtr1;
}

总结一下我不理解的是,为什么我们必须将int*更改为void*然后更改为char*才能执行此任务?为什么我们在减去charPtr2charPtr1时得到结果,但是在尝试仅打印charPtr1时没有显示任何内容?

6 个答案:

答案 0 :(得分:17)

首先,永远不要在现实世界的代码中这样做。你会吹掉你的腿,看起来像个白痴,所有很酷的孩子都会嘲笑你。

话虽如此,这是它的工作原理: 基本思想是int的大小等于int数组中两个元素之间的偏移量(以字节为单位)。数组中的Int是紧密打包的,所以第二个int的开头就在第一个int的结尾之后:

int* intPtr1 = &intArray[0];
int* intPtr2 = &intArray[1];

这里的问题是,当减去两个int指针时,你不会得到字节差异,而是ints的差异。所以intPtr2 - intPtr11,因为它们相距1个。

但是我们使用C ++,所以我们可以指向任何东西!因此,我们不是使用int指针,而是将值复制到char指针,这些指针大小为1个字节(至少在大多数平台上)。

char* charPtr1 = reinterpret_cast<char*>(intPtr1);
char* charPtr2 = reinterpret_cast<char*>(intPtr2);

差异charPtr2 - charPtr1是以字节为单位的大小。指针仍然指向与之前相同的位置(即数组中第二个和第一个int的开始),但现在将以char的大小计算差异,而不是int的大小

由于练习不允许reinterpret_cast,你将不得不诉诸另一招。您不能直接从static_castint* char*。这是C ++保护你做一些愚蠢行为的方法。诀窍是首先转向void*。您可以static_cast任何指针类型void*void*到任何指针类型。

答案 1 :(得分:4)

这是重要的一点:

intPtr1 = &intArray[1];
intPtr2 = &intArray[2];

这将创建指向数组中相邻整数的两个指针。这两个指针之间的距离是您尝试检索的整数的大小。然而,指针运算的工作方式是,如果你减去这两个,那么编译器将以int的形式返回大小,它总是为1。

所以你接下来要做的就是将它们重新转换为字符指针。字符是(或事实上)每个1字节,所以这两个指针之间的差异作为字符指针将给你一个字节的答案。这就是为什么你要转换为字符指针并减去。

至于void* - 这是为了避免使用reinterpret_cast。您不允许使用int*直接从char*投射到static_cast<>,但是通过void*会删除此限制,因为编译器不再知道它是以int*。您也可以使用C风格的强制转换,(char*)(intPtr1)

答案 2 :(得分:2)

“不要使用sizeof()和reinterpret_cast”......没有关于std :: numeric_limits的说法,所以你可以这样做:)

#include <limits>

int intSize()
{
   // digits returns non-sign bits, so add 1 and divide by 8 (bits in a byte)
   return (std::numeric_limits<int>::digits+1)/8;
}

答案 3 :(得分:1)

请阅读:评论很丰富。

int intSize()
{
    int intArray[2]; // Allocate two elements. We don't need any more than that.

    /*intPtr1 and intPtr2 point to the addresses of the zeroth and first array elements*/
    int* intPtr1 = &intArray[0]; // Arrays in C++ are zero based
    int* intPtr2 = &intArray[1];

    /*Note that intPtr2 - intPtr1 measures the distance in memory 
      between the array elements in units of int*/

    /*What we want to do is measure that distance in units of char;
      i.e. in bytes since once char is one byte*/

    /*The trick is to cast from int* to char*. In c++ you need to 
      do this via void* if you are not allowed to use reinterpret_cast*/

     void* voidPtr1 = static_cast<void*>(intPtr1);
     char* charPtr1 = static_cast<char*>(voidPtr1);

     void* voidPtr2 = static_cast<void*>(intPtr2);
     char* charPtr2 = static_cast<char*>(voidPtr2);

    /*The distance in memory will now be measure in units of char; 
      that's how pointer arithmetic works*/
    /*Since the original array is a contiguous memory block, the 
      distance will be the size of each element, i.e. sizeof(int) */
     return charPtr2 - charPtr1;
}

答案 4 :(得分:1)

C ++中的指针减法给出了之间的元素数量 指向的对象。换句话说,intPtr2 - intPtr1 将在这两个指针之间返回int的数量。 程序想要知道字节数(char),所以它 将int*转换为char*。显然,作者没有 想要使用reinterpret_cast。并static_cast会 不允许从int*直接转换为char*,所以他 经过void*(允许)。

说完了这一切:从功能的名称来判断 如何实际初始化指针,更简单 实施这个将是:

int
intSize()
{
    return sizeof( int );
}

答案 5 :(得分:1)

除了避免void*之外,实际上无需转换为reinterpret_cast

从指针转换为 - int转换为指针转移到char可以使用reinterpret_cast或C样式转换(通过标准,最终做reinterpret_cast)。你可以直接进行C风格的演员表,但是因为(在标准中) reinterpret_cast在那个环境中,你违反了要求。非常棘手!

但是,您可以仅使用int*通过char*中介从void*转换为static_cast。这是C ++类型系统中的一个小漏洞 - 您执行两步reinterpret_cast而没有调用它 - 因为void*转换被授予了通过static_cast特殊权限

因此,所有void*内容只是为了避免reinterpret_cast的要求,并且在实际代码中是愚蠢的 - 意识到你可以做到这一点可能有助于理解何时有人这样做< em>意外地代码中(即,你的int*似乎指向一个字符串:这是怎么发生的?好吧,有人必须经历过类型系统中的漏洞。要么是C风格的施放(因此reinterpret_cast),或者它必须通过void*通过static_cast进行往返。

如果我们忽略那种体操,我们现在有一系列int。我们指向相邻的元素。在C ++中,数组是打包的,相邻元素之间的差异等于元素的sizeof

然后我们将这些指针转换为指针 - char,因为我们知道(按标准)sizeof(char)==1。我们减去这些char指针,因为它告诉我们它们之间存在多少个sizeof(char)倍数(如果我们减去int指针,我们得到多少倍{ - 1}} {{{ 1}}它们之间存在),最终是sizeof(int)的大小。

如果我们尝试通过int打印charPtr1,则std::cout会假定我们的std::cout是指针 - char* - 终止缓冲区 - - \0,由于C / C ++惯例。指向的第一个charchar,因此\0不打印任何内容。如果我们想要打印std::cout的指针值,我们必须将其转换为char*(可能通过void*)。