6

我是 C++ 的初学者,我在理解一些代码时遇到了问题。

我有一个练习要做,编写返回大小的函数,int不要使用sizeof()and 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*执行此任务?为什么我们减去charPtr2and时有结果charPtr1,但尝试仅打印时没有显示任何内容charPtr1

4

6 回答 6

17

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

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

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

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

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

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*指向任何指针类型。

于 2013-07-04T13:43:28.893 回答
4

这是重要的一点:

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

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

因此,您接下来要做的是将这些重新转换为字符指针。每个字符是(或事实上是)1 个字节,因此这两个指针作为字符指针之间的差异将以字节为单位给出答案。这就是为什么您要转换为字符指针并进行减法。

至于 via void*- 这是为了避免必须使用reinterpret_cast. 您不能直接从 aint*转换为 a char*with static_cast<>,但是通过 viavoid*消除了这个限制,因为编译器不再知道它以 a 开头int*。您也可以只使用 C 样式转换,(char*)(intPtr1).

于 2013-07-04T13:43:20.903 回答
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;
}
于 2013-07-04T13:51:21.830 回答
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;
}
于 2013-07-04T13:45:08.237 回答
1

void*除了避免之外,实际上没有必要转换为reinterpret_cast

从指针到指针的转换int可以通过 a或 C 风格的强制转换(按照标准,最终执行 a )char一步完成。您可以直接进行 C 风格的转换,但由于(按照标准)在这种情况下a ,因此您将违反要求。非常棘手!reinterpret_castreinterpret_castreinterpret_cast

但是,您可以int*通过仅char*使用. 这是 C++ 类型系统中的一个小漏洞——你正在做一个两步而不调用它——因为转换被赋予了特殊的权限,可以通过.void*static_castreinterpret_castvoid*static_cast

所以所有的void*东西都只是为了避免这个reinterpret_cast要求,在真正的代码中做这件事会很愚蠢——意识到你可以做到这一点可能有助于理解有人在代码中意外做了什么(即,你int*似乎指向一个字符串: 那是怎么发生的呢?嗯,肯定有人在类型系统中遇到了一个漏洞。要么是 C 风格的强制转换(因此是 a reinterpret_cast),要么它必须通过void*via 往返static_cast)。

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

然后我们将这些指针转换为pointers-to- char,因为我们(按照标准)知道sizeof(char)==1. 我们减去这些char指针,因为这告诉我们sizeof(char)它们之间有多少倍数(如果我们减去int指针,我们会得到它们之间有多少倍数sizeof(int)),这最终是int.

如果我们尝试通过 打印charPtr1std::cout假设std::cout我们char*是指向-\0终止缓冲区的-的指针char,由于 C/C++ 约定。第一个char指向的是\0,所以std::cout什么也不打印。如果我们想打印 的指针值char*,我们必须将它转换为类似的东西void*(也许是 via static_cast<void*>(p))。

于 2013-07-04T13:51:15.053 回答
1

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

说了这么多:从函数的名称和指针的实际初始化方式来看,更简单的实现是:

int
intSize()
{
    return sizeof( int );
}
于 2013-07-04T13:45:58.573 回答