我是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*
才能执行此任务?为什么我们在减去charPtr2
和charPtr1
时得到结果,但是在尝试仅打印charPtr1
时没有显示任何内容?
答案 0 :(得分:17)
首先,永远不要在现实世界的代码中这样做。你会吹掉你的腿,看起来像个白痴,所有很酷的孩子都会嘲笑你。
话虽如此,这是它的工作原理: 基本思想是int的大小等于int数组中两个元素之间的偏移量(以字节为单位)。数组中的Int是紧密打包的,所以第二个int的开头就在第一个int的结尾之后:
int* intPtr1 = &intArray[0];
int* intPtr2 = &intArray[1];
这里的问题是,当减去两个int指针时,你不会得到字节差异,而是ints的差异。所以intPtr2 - intPtr1
是1
,因为它们相距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_cast
到int*
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 ++惯例。指向的第一个char
是char
,因此\0
不打印任何内容。如果我们想要打印std::cout
的指针值,我们必须将其转换为char*
(可能通过void*
)。