问题描述
在C / C ++中,我们知道指针。也就是说,可变的内存地址。
说,下面的代码不会编译,但是想法是这样的:
#include <iostream>
using namespace std;
main() {
int* pi;
int i = 1;
pi = &i;
while (true) {
cout<<"i = " << i++ << "; pi = " << pi;
pi = π
}
}
我们可以把地址地址走多远。 “最终地址”在哪里?
int* pi = &i;
int** ppi = π
int*** pppi = &ppi;
int**** ppppi = &pppi;
.....
解决方法
没有理论上的限制,但请记住:
-
您不能合法使用
pi = π
,因为这是严格别名的违规行为。读取pi
之后的行为是未定义。 -
未初始化的指针在读取或取消引用上的行为是未定义。
-
读取曾经用于指向有效内存的指针的行为是不确定。但这不是未定义。因此,您的伪代码将产生无限数量的悬空指针这一事实是没有意义的。
您可以通过模板(将所有指针保留在范围内)来设计一个带有模板的工作示例,就像您可以使用模板来实现阶乘函数一样。
,为了有趣,这里有一个示例,您可以使用模板生成长指针类型。编译器将生成具有数百个间接级别的类型。
#include <iostream>
template<size_t N,typename T>
struct PointerVariable
{
T* myPtr;
PointerVariable<N - 1,T*> ptrToMyPtr;
PointerVariable(T* p) :
myPtr(p),ptrToMyPtr(&myPtr)
{
// display pointer value
std::cout << "ptr:" << ((void*)myPtr) << std::endl;
}
};
template<typename T>
struct PointerVariable<0,T>
{
PointerVariable(T* p) {
int a = 3.3; // generate a compiler warning so we can see the names of types
}
};
int main()
{
int val = 0;
auto pp = PointerVariable<499,int>{ &val };
return 0;
}
在MSVC ++ 2019上,这会产生编译器警告:
warning C4244: 'initializing': conversion from 'double' to 'int',possible loss of data
message : while compiling class template member function 'PointerVariable<0,T *>::PointerVariable(int ********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************)'
当N = 500时,发生编译器错误:
fatal error C1202: recursive type or function dependency context too complex
使用显式类型,VC ++ 2019允许1491级间接访问:
int
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
******************************************************************************************************************************** // 128
**************************************************************** // 64
**************** // 16
*** // 3
x = nullptr;
再增加一个指针级别将发出:
fatal error C1026: parser stack overflow,program too complex
这是编译器在变量存储和类型复杂性方面的能力的练习。忽略这两个限制,例如,仅使用uintptr_t
而不是实际的指针类型,那么您的限制就是系统内存。
在这种结构中,由于您采用了不同变量的地址,因此没有理论上的限制:
int* pi = &i;
int** ppi = π
int*** pppi = &ppi;
int**** ppppi = &pppi;
.....
您有一个变量i
。您有一个变量pi
,其中包含地址i
。您有一个变量ppi
,其中包含变量pi
的地址。您有一个变量pppi
,其中包含变量ppi
的地址。等等。这可以无限期地进行,尽管在实践中当然不需要。
i
,pi
,ppi
,pppi
,...都是变量,对于每个变量,您都可以使用&i
,{ {1}},&pi
,&ppi
……这些变量的类型无关。
&pppi
,int
,int*
,int**
…都是类型(int***
属于该类型)。
您可以输入所有这些别名:
*
并这样写:
using int_ptr_t = int*;
using int_ptr_ptr_t = int**;
using int_ptr_ptr_ptr_t = int***;
等同于您的
int_ptr_t pi = &i;
int_ptr_ptr_t ppi = π
int_ptr_ptr_ptr_t pppi = &ppi;
因此从技术上讲,它仅限于系统的内存。
,正如@Bathsheba解释的那样,您的代码存在一些技术问题。但是,如果我们忽略这些内容,那么恐怕您的循环仍然没有意义-至少不是我对您的意图的理解。
main() {
int* pi;
int i = 1;
pi = &i; // <- **1**
while (true) {
cout<<"i = " << i++ << "; pi = " << pi;
pi = π // <- **2**
}
}
您在1
上将i
的地址写在名为pi
的纸上。然后在2
上,将pi
的地址写在pi
上。到目前为止,如果我们仅将内存视为一张桌子上的纸,这确实是有道理的。
但是,在pi
工作表上写东西的动作不会使它移动到新位置。将pi
的位置写到pi
工作表后,每隔pi = &pi
个分配都不会改变。
当然,您可以抓住一堆新纸,在第一页上写上pi
的位置,然后在另一页上写上该纸的位置,依此类推,直到用完所有纸。但这只会为您提供一堆到最后的文件,而这并不是我想要的。
指出问题存在缺陷的一种方法是,pi
工作表在您的办公桌上有一个位置,而您可以在任何工作表上写下该位置。图纸的位置是该图纸固有的概念,但是图纸的位置没有自己的位置。这意味着询问位置的位置并没有任何意义。
@Kit在他的评论中写道:
,没有“地址的地址”之类的东西。有一个 例如“变量的地址”
语言实现会带来一些实际的限制。 C ++标准建议至少支持256个级别,但这仅是一个建议,下限并不意味着不符合该标准。