微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

指针到指针我们能走多远?

如何解决指针到指针我们能走多远?

在C / C ++中,我们知道指针。也就是说,可变的内存地址。

说,下面的代码不会编译,但是想法是这样的:

#include <iostream>
using namespace std;

main() {
    int* pi;
    int i = 1;
    pi = &i;
    
    while (true) {
        cout<<"i = " << i++ << "; pi = " << pi;
        pi = &pi;
    }
}

我们可以把地址地址走多远。 “最终地址”在哪里?

int* pi = &i;
int** ppi = &pi;
int*** pppi = &ppi;
int**** ppppi = &pppi;
.....

解决方法

没有理论上的限制,但请记住:

  1. 您不能合法使用pi = &pi;,因为这是严格别名的违规行为。读取pi之后的行为是未定义

  2. 未初始化的指针在读取取消引用上的行为是未定义

  3. 读取曾经用于指向有效内存的指针的行为是不确定。但这不是未定义。因此,您的伪代码将产生无限数量的悬空指针这一事实是没有意义的。

可以通过模板(将所有指针保留在范围内)来设计一个带有模板的工作示例,就像您可以使用模板来实现阶乘函数一样。

,

为了有趣,这里有一个示例,您可以使用模板生成长指针类型。编译器将生成具有数百个间接级别的类型。

#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 = &pi;
int*** pppi = &ppi;
int**** ppppi = &pppi;
.....

您有一个变量i。您有一个变量pi,其中包含地址i。您有一个变量ppi,其中包含变量pi的地址。您有一个变量pppi,其中包含变量ppi的地址。等等。这可以无限期地进行,尽管在实践中当然不需要。

,

ipippipppi,...都是变量,对于每个变量,您都可以使用&i,{ {1}},&pi&ppi……这些变量的类型无关。

&pppiintint*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 = &pi;
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 = &pi;    // <- **2**
    }
}

您在1上将i的地址写在名为pi的纸上。然后在2上,将pi的地址写在pi上。到目前为止,如果我们仅将内存视为一张桌子上的纸,这确实是有道理的。

但是,在pi工作表上写东西的动作不会使它移动到新位置。将pi的位置写到pi工作表后,每隔pi = &pi个分配都不会改变。

当然,您可以抓住一堆新纸,在第一页上写上pi的位置,然后在另一页上写上该纸的位置,依此类推,直到用完所有纸。但这只会为您提供一堆到最后的文件,而这并不是我想要的。

指出问题存在缺陷的一种方法是,pi工作表在您的办公桌上有一个位置,而您可以在任何工作表上写下该位置。图纸的位置是该图纸固有的概念,但是图纸的位置没有自己的位置。这意味着询问位置的位置并没有任何意义。

@Kit在他的评论中写道:

没有“地址的地址”之类的东西。有一个 例如“变量的地址”

,

语言实现会带来一些实际的限制。 C ++标准建议至少支持256个级别,但这仅是一个建议,下限并不意味着不符合该标准。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。