我们可以有多less级别的指针?
在一个variables中允许多less个指针( *
)?
我们来看下面的例子。
int a = 10; int *p = &a;
同样我们可以有
int **q = &p; int ***r = &q;
等等。
例如,
int ****************zz;
C
标准规定了下限:
5.2.4.1翻译限制
276实施应能够翻译和执行至less一个包含以下每个限制的至less一个实例的程序:
指针,数组和函数声明符(以任何组合方式)修改声明中的算术,结构,联合或无效types
上限是具体实现。
实际上,C程序通常使用无限指针间接。 一个或两个静态水平是常见的。 三重间接是罕见的。 但无限是非常普遍的。
无限的指针间接是通过struct的帮助来实现的,当然不是直接声明的,这是不可能的。 并且需要一个结构体,以便可以在可以终止的不同级别上包含此结构体中的其他数据。
struct list { struct list *next; ... };
现在你可以有list->next->next->next->...->next
。 这只是多指针间接指针: *(*(..(*(*(*list).next).next).next...).next).next
。 而.next
基本上是noop,因为它是结构的第一个成员,所以我们可以把它想象成***..***ptr
。
对此没有限制,因为链接可以循环遍历,而不是像这样的巨大expression式,而且结构可以很容易地制成圆形。
因此,换句话说,链表可能是增加另一个间接级来解决问题的最终例子,因为每个推动操作都是dynamic的。 🙂
从理论上讲:
你可以拥有任意数量的间接指针。
几乎:
当然,没有任何消耗内存的东西可以是不确定的,由于主机环境上可用的资源,会有限制。 所以实际上对于实现能够支持什么是最大的限制,并且实现应该适当地logging它。 所以在所有这些工件中,标准没有指定最大限制,但是它确定了下限。
这里是参考:
C99标准5.2.4.1翻译限制:
– 12个指针,数组和函数声明符(以任何组合方式)修改声明中的算术,结构,联合或无效types。
这指定了每个实现必须支持的下限。 请注意,在标准脚本中进一步说:
18)实施应尽可能避免强加固定的翻译限制。
正如人们所说,“理论上”没有限制。 但是,出于兴趣,我用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; }
听起来很有趣。
-
Visual Studio 2010(在Windows 7上),你可以有1011级别之前得到这个错误:
致命错误C1026:parsing器堆栈溢出,程序太复杂
-
gcc(Ubuntu),100k +
*
没有崩溃! 我猜硬件是这里的限制。
(只用variables声明进行testing)
没有限制, 在这里查看示例。
答案取决于你所指的“指针级别”。 如果你的意思是“你可以在一个声明中有多less层次的间接性?” 答案是“至less12”。
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 */
如果你的意思是“程序难读之前可以使用多less级别的指针”,这是一个有趣的问题,但是有一个限制。 有两个间接级别(指向某个指针的指针)是常见的。 除此之外,有一点难以轻易思考; 不要这样做,除非替代品会更糟。
如果你的意思是“你可以在运行时有多less级别的指针间接寻址”,那么没有限制。 这一点对于循环列表来说尤为重要,其中每个节点指向下一个节点。 你的程序可以永远遵循指针。
指针函数实际上更有趣。
#include <cstdio> typedef void (*FuncType)(); static void Print() { std::printf("%s", "Hello, World!\n"); } int main() { FuncType const ft = &Print; ft(); (*ft)(); (**ft)(); /* ... */ }
如这里所示,这给出:
你好,世界!
你好,世界!
你好,世界!
它不涉及任何运行时间的开销,所以你可以尽可能多地堆叠它们,直到你的编译器扼杀文件。
没有限制 。 指针是一块内存,其内容是一个地址。
如你所说
int a = 10; int *p = &a;
指向指针的指针也是一个包含另一个指针地址的variables。
int **q = &p;
这里q
是指向保存p
的地址的指针, p
已经保存了a
的地址。
指针指针没有特别的特殊之处。
所以对持有另一个指针的地址的连锁群没有限制。
即。
int **************************************************************************z;
被允许。
请注意,这里有两个可能的问题:我们可以在Ctypes中实现多less级别的指针间接寻址,以及我们可以将多less级别的指针间接寻址到一个单独的声明器中。
C标准允许对前者施加最大值(并给出最小值)。 但是这可以通过多个typedef声明来绕过:
typedef int *type0; typedef type0 *type1; typedef type1 *type2; /* etc */
所以最终,这是一个实现问题,它涉及到C程序被拒绝之前可以有多大/多么复杂的想法,这是非常特定的编译器。
每个C ++开发者都应该听说(着名) 三星程序员
而且似乎真的有一些魔法“指针障碍”,必须被伪装
引用C2:
三星程序员
C程序员的评分系统。 你的指针越是间接的(即你的variables之前的“*”越多),你的声望就越高。 几乎没有明星的C程序员几乎不存在,因为几乎所有非平凡的程序都需要使用指针。 大多数是一星级程序员。 在旧时代(当然,我还年轻,所以这些对我来说至less看起来是旧时代的),偶尔会发现一个由三星级程序员完成的代码,并且惊愕起来。 有些人甚至声称他们已经看到三星级代码与涉及函数指针,在不止一个层面的间接。 听起来和UFO一样真实。
2004年MISRA C标准规则17.5禁止超过2级的指针间接。
没有像真正的限制这样的事情,但存在限制。 所有的指针都是variables,通常是堆栈而不是堆 。 堆栈通常很小(在一些链接过程中可以改变它的大小)。 所以可以说,你有4MB的堆栈,这是相当正常的大小。 让我们说我们有4个字节大小的指针(指针大小不一样,取决于架构,目标和编译器设置)。
在这种情况下, 4 MB / 4 b = 1024
这样可能的最大数量将是1048576,但我们不应该忽略其他一些东西在堆栈中的事实。
然而,一些编译器可能有最大数量的指针链,但是限制是堆栈大小。 所以如果你在连接无穷大的时候增加堆栈的大小,并且让无限内存的机器运行操作系统来处理内存,那么你将拥有无限的指针链。
如果你使用int *ptr = new int;
并把你的指针放入堆中,这是不是很平常的方式限制将堆大小,而不是堆栈。
编辑只要意识到infinity / 2 = infinity
。 如果机器有更多的内存,所以指针大小增加。 所以如果记忆是无限的,指针的大小是无限的,那么这是坏消息… 🙂
我想指出的是,生成一个具有任意数量*的types是模板元编程可能发生的事情。 我完全忘记了自己在做什么,但是有人build议我可以通过recursion T *types来产生新的独特types,它们之间有一些元操作。
模板元编程是一个缓慢的陷入疯狂,所以没有必要为生成一个具有几千级间接types的借口。 例如,将peano整数映射到作为function语言的模板扩展上是一种方便的方法。
这取决于你存储指针的地方。 如果他们在堆栈,你有相当低的限制。 如果你把它存储在堆中,你的限制要高得多。
看这个节目:
#include <iostream> const int CBlockSize = 1048576; int main() { int number = 0; int** ptr = new int*[CBlockSize]; ptr[0] = &number; for (int i = 1; i < CBlockSize; ++i) ptr[i] = reinterpret_cast<int *> (&ptr[i - 1]); for (int i = CBlockSize-1; i >= 0; --i) std::cout << i << " " << (int)ptr[i] << "->" << *ptr[i] << std::endl; return 0; }
它创build了1M个指针,并在显示什么点容易注意到什么链到第一个可变number
。
BTW。 它使用92K
的RAM,所以想象你可以走多远。