我们可以拥有多少级别的指针?

Par*_*rag 432 c c++ pointers

*单个变量中允许多少个指针()?

让我们考虑以下示例.

int a = 10;
int *p = &a;
Run Code Online (Sandbox Code Playgroud)

同样我们也可以

int **q = &p;
int ***r = &q;
Run Code Online (Sandbox Code Playgroud)

等等.

例如,

int ****************zz;
Run Code Online (Sandbox Code Playgroud)

P.P*_*.P. 396

C标准规定了下限:

5.2.4.1翻译限制

276实施应能够翻译和执行至少一个包含以下每个限制的至少一个实例的程序:[...]

279 - 12指针,数组和函数声明符(以任意组合)修改声明中的算术,结构,联合或void类型

上限是特定于实现的.

  • C++标准"建议"实现支持至少256个.(可读性建议您不要超过2或3,即便如此:不止一个应该是例外.) (118认同)
  • 这个限制是关于单个声明中的数量; 它没有强加一个上限,你可以通过多个`typedef来实现多少间接. (22认同)
  • @Kaz - 是的,这是真的.但由于规范(没有双关语)指定了强制性下限,因此所有符合规范的编译器都需要支持**.当然,它可能低于供应商特定的上限.改写不同(为了与OP的问题保持一致),它是规范允许的**最大***(其他任何东西都是供应商特定的.)有点偏离正切,程序员*应该*(至少在一般情况下)将其视为上限(除非他们有正当理由依赖供应商特定的上限)......我认为. (11认同)
  • @beryllium:通常这些数字来自对预标准化软件的调查.在这种情况下,他们可能会查看常见的C程序和现有的C编译器,并且发现至少有一个编译器会遇到12个以上的问题和/或没有程序如果将其限制为12则会破坏. (11认同)
  • 另一方面,如果我不得不使用具有长de去引用链的代码(特别是当**遍布各处时**)时,我会开始削减自己.) (5认同)
  • 好的,但为什么到12?为什么不是5或40.对此有一些解释吗? (2认同)

Kaz*_*Kaz 152

实际上,C程序通常使用无限指针间接.一个或两个静态级别很常见.三重间接是罕见的.但无限是非常普遍的.

无限指针间接是在结构的帮助下实现的,当然,不是直接声明器,这是不可能的.并且需要一个结构,以便您可以在此结构中包含可以终止的不同级别的其他数据.

struct list { struct list *next; ... };
Run Code Online (Sandbox Code Playgroud)

现在你可以拥有list->next->next->next->...->next.这实际上只是多个指针间接:*(*(..(*(*(*list).next).next).next...).next).next.而且.next基本上是一个空操作时,它的结构的第一个成员,所以我们可以想像这是***..***ptr.

对此没有任何限制,因为链接可以用循环遍历而不是像这样的巨型表达式,而且,结构可以很容易地变成圆形.

因此,换句话说,链接列表可能是添加另一级间接来解决问题的最终示例,因为您在每次推送操作时都会动态地执行此操作.:)

  • 但这是一个完全不同的问题 - 包含指向另一个结构的指针的结构与指针指针非常不同.int*****是与int****不同的类型. (48认同)
  • 当我使用"蓬松"这个词时,我并没有有意识地注意到你的名字是蓬松的.潜意识的影响力?但我相信我之前已经以这种方式使用过这个词. (34认同)
  • 它并非"非常"不同.差别是蓬松的.它更接近语法而不是语义.指向指针对象的指针,或指向包含指针的结构对象的指针?这是同样的事情.到达列表的第十个元素是十个级别的寻址间接.(当然,表达无限结构的能力取决于结构类型能够通过不完整的结构类型指向自身,因此`list-> next`和`list-> next-> next`是相同的类型;否则我们将不得不构建一个无限类型.) (12认同)
  • 如果结构是圆形的,则不是.如果`R1`保存指向自身的位置的地址,那么`LOAD R1,[R1]`可以在无限循环中执行. (4认同)
  • 还要记住,在机器语言中,只要R1在每一步都是有效指针,你就可以迭代`LOAD R1,[R1]`之类的东西.除了"持有地址的词"之外,没有涉及的类型.是否存在声明的类型并不确定间接以及它具有多少级别. (3认同)
  • 这个答案没有回答问题,这是关于声明的问题. (2认同)

Alo*_*ave 82

从理论上讲:

您可以根据需要拥有多个级别的间接.

几乎:

当然,任何消耗内存的东西都是无限期的,由于主机环境上可用的资源而存在限制.实际上,实现可以支持的最大限制是,实现应该适当地记录它.因此,在所有此类工件中,标准未指定最大限制,但它确实指定了下限.

这是参考:

C99标准5.2.4.1翻译限制:

- 12个指针,数组和函数声明符(以任意组合)修改声明中的算术,结构,联合或void类型.

这指定了每个实现必须支持的下限.请注意,在标准中,标准进一步说:

18)实施应尽可能避免强加固定的翻译限制.

  • 间接不会溢出任何堆栈! (16认同)
  • 堆栈如何与指针间接相关? (11认同)
  • _indirections不会溢出任何堆栈!_-->不!解析器堆栈可以溢出._堆栈如何与指针间接相关?_解析器堆栈! (8认同)
  • @basile - 我希望堆栈深度成为解析器中的一个问题.许多正式的解析算法都将堆栈作为关键组件.大多数C++编译器可能使用递归下降的变体,但即使这样也依赖于处理器堆栈(或者,讽刺的是,语言就像处理器堆栈一样).更多的语法规则嵌套意味着更深层次的堆栈. (2认同)

BoB*_*ish 76

正如人们所言,"在理论上"没有限制.然而,出于兴趣,我使用g ++ 4.1.2运行它,它的大小高达20,000.编译虽然很慢,所以我没有尝试更高.所以我猜g ++也没有任何限制.(尝试设置size = 10并查看ptr.cpp,如果它不是很明显的话.)

g++ create.cpp -o create ; ./create > ptr.cpp ; g++ ptr.cpp -o ptr ; ./ptr

create.cpp

#include <iostream>

int main()
{
    const int size = 200;
    std::cout << "#include <iostream>\n\n";
    std::cout << "int main()\n{\n";
    std::cout << "    int i0 = " << size << ";";
    for (int i = 1; i < size; ++i)
    {
        std::cout << "    int ";
        for (int j = 0; j < i; ++j) std::cout << "*";
        std::cout << " i" << i << " = &i" << i-1 << ";\n";
    }
    std::cout << "    std::cout << ";
    for (int i = 1; i < size; ++i) std::cout << "*";
    std::cout << "i" << size-1 << " << \"\\n\";\n";
    std::cout << "    return 0;\n}\n";
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 当我尝试时,我得不到超过98242.(我在Python中编写脚本,将`*`的数量增加一倍,直到我得到一个失败,然后前一个通过;然后在那个时间间隔内对第一个失败进行了二元搜索.整个测试花费较少跑了一秒钟.) (72认同)

mih*_*hai 63

听起来很有趣.

  • Visual Studio 2010(在Windows 7上),在获得此错误之前,您可以拥有1011个级别:

    致命错误C1026:解析器堆栈溢出,程序太复杂

  • gcc(Ubuntu),100k + *没有崩溃!我猜硬件是这里的限制.

(仅使用变量声明进行测试)

  • 实际上,一元运算符的生成是右递归的,这意味着shift-reduce解析器会将所有`*`节点移到堆栈上,然后才能进行缩减. (5认同)

Nan*_*ale 27

没有限制,例如检查这里.

答案取决于"指针水平"的含义.如果你的意思是"你可以在一个声明中有多少级别的间接?" 答案是"至少12".

int i = 0;

int *ip01 = & i;

int **ip02 = & ip01;

int ***ip03 = & ip02;

int ****ip04 = & ip03;

int *****ip05 = & ip04;

int ******ip06 = & ip05;

int *******ip07 = & ip06;

int ********ip08 = & ip07;

int *********ip09 = & ip08;

int **********ip10 = & ip09;

int ***********ip11 = & ip10;

int ************ip12 = & ip11;

************ip12 = 1; /* i = 1 */
Run Code Online (Sandbox Code Playgroud)

如果你的意思是"在程序难以阅读之前你可以使用多少级别的指针",这是一个品味问题,但是有一个限制.具有两个间接级别(指向某事物的指针)是常见的.除此之外,更容易思考; 除非替代方案会更糟,否则不要这样做.

如果你的意思是"你可以在运行时有多少级别的指针间接",那就没有限制.这一点对于循环列表尤为重要,其中每个节点都指向下一个节点.你的程序可以永远遵循指针.

  • 几乎可以肯定有限制,因为编译器必须在有限的内存中跟踪信息.(`g ++`在我的机器上以98242的内部错误中止.我希望实际的限制取决于机器和负载.我也不认为这在实际代码中是个问题.) (7认同)
  • 好吧,链接列表实际上不是指向指针的指针,它们是指向包含指针的结构的指针(或者你最终做了很多不必要的转换) (3认同)
  • 是的@MatthieuM.:我刚刚在理论上考虑过:)感谢James完成答案 (2认同)

Mat*_* M. 24

实际上,指向函数的指针更加有趣.

#include <cstdio>

typedef void (*FuncType)();

static void Print() { std::printf("%s", "Hello, World!\n"); }

int main() {
  FuncType const ft = &Print;
  ft();
  (*ft)();
  (**ft)();
  /* ... */
}
Run Code Online (Sandbox Code Playgroud)

如图所示,这给出了:

你好,世界!
你好,世界!
你好,世界!

并且它不涉及任何运行时开销,因此您可以根据需要堆叠它们......直到编译器对文件进行阻塞.


Sac*_*tre 19

没有限制.指针是一块内存,其内容是一个地址.
如你所说

int a = 10;
int *p = &a;
Run Code Online (Sandbox Code Playgroud)

指向指针的指针也是一个包含另一个指针地址的变量.

int **q = &p;
Run Code Online (Sandbox Code Playgroud)

q是指向指针的指针,其地址p已经保存了地址a.

指向指针的指针没有什么特别之处.
所以对持有另一个指针地址的poniters链没有限制.
即.

 int **************************************************************************z;
Run Code Online (Sandbox Code Playgroud)

被允许.


Mar*_*tus 15

每个C++开发人员都应该听说过着名的三星级程序员

而且似乎有一些神奇的"指针屏障"必须被伪装

来自C2的报价:

三星程序员

C程序员的评级系统.你的指针越间接(即你的变量之前的"*"越多),你的声誉就越高.没有明星的C程序员几乎不存在,因为几乎所有非平凡的程序都需要使用指针.大多数是一星级程序员.在过去(好吧,我很年轻,所以这些看起来至少对我而言),偶尔会找到一个由三星级程序员完成的代码并且敬畏地颤抖.有些人甚至声称他们在不止一个间接层面上看到了涉及函数指针的三星代码.听起来像UFO一样真实.

  • https://github.com/psi4/psi4public/blob/master/src/lib/libdpd/buf4_init.cc等是由一位四星级程序员编写的.他也是我的朋友,如果你读了足够的代码,你就会明白它为什么值得四星级的原因. (2认同)

Kaz*_*Kaz 13

请注意,这里有两个可能的问题:我们可以在C类型中实现多少级别的指针间接,以及我们可以将多少级别的指针间接填充到单个声明符中.

C标准允许对前者施加最大值(并给出最小值).但是这可以通过多个typedef声明来规避:

typedef int *type0;
typedef type0 *type1;
typedef type1 *type2; /* etc */
Run Code Online (Sandbox Code Playgroud)

所以最终,这是一个实现问题,它与C程序在被拒绝之前的大/复杂程度有关,这是一个非常特定于编译器的.


JDł*_*osz 6

我想指出的是,生成具有任意数量 * 的类型是模板元编程可能发生的事情。我忘记了我到底在做什么,但有人建议我可以通过使用递归T* 类型来生成新的不同类型,这些类型在它们之间具有某种元操作。

模板元编程是慢慢陷入疯狂的,所以在生成几千级间接的类型时没有必要找借口。例如,这只是将 peano 整数映射到模板扩展作为功能语言的一种方便的方法。