464

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

让我们考虑以下示例。

int a = 10;
int *p = &a;

同样我们可以有

int **q = &p;
int ***r = &q;

等等。

例如,

int ****************zz;
4

14 回答 14

412

C标准规定了下限:

5.2.4.1 翻译限制

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

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

上限是特定于实现的。

于 2012-04-10T10:45:00.040 回答
161

实际上,C 程序通常使用无限指针间接寻址。一到两个静态级别是常见的。三重间接很少见。但是无限是很常见的。

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

struct list { struct list *next; ... };

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

这实际上没有限制,因为可以用循环而不是像这样的巨型表达式来遍历链接,而且结构可以很容易地做成圆形。

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

于 2012-04-10T15:44:27.307 回答
84

理论上:

您可以根据需要拥有尽可能多的间接级别。

几乎:

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

这是参考:

C99 标准 5.2.4.1 翻译限制:

— 12 个指针、数组和函数声明符(任意组合),用于修改声明中的算术、结构、联合或 void 类型。

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

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

于 2012-04-10T10:35:59.573 回答
80

正如人们所说,“理论上”没有限制。但是,出于兴趣,我使用 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

创建.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;
}
于 2012-04-10T11:12:01.883 回答
64

检查听起来很有趣。

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

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

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

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

于 2012-04-10T18:36:16.660 回答
30

没有限制,请在此处查看示例。

答案取决于您所说的“指针级别”是什么意思。如果您的意思是“在单个声明中可以有多少级间接性?” 答案是“至少 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 */

如果您的意思是“在程序难以阅读之前您可以使用多少级指针”,那是一个品味问题,但有一个限制。具有两个间接级别(指向某物的指针的指针)很常见。比这更容易想起来有点困难。除非替代方案更糟,否则不要这样做。

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

于 2012-04-10T10:37:38.800 回答
26

实际上,使用指向函数的指针更有趣。

#include <cstdio>

typedef void (*FuncType)();

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

int main() {
  FuncType const ft = &Print;
  ft();
  (*ft)();
  (**ft)();
  /* ... */
}

如此处所示这给出了:

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

而且它不涉及任何运行时开销,因此您可以随意堆叠它们......直到您的编译器阻塞文件。

于 2012-04-10T14:16:47.007 回答
22

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

int a = 10;
int *p = &a;

指向指针的指针也是一个变量,其中包含另一个指针的地址。

int **q = &p;

q是指向持有地址的指针的指针,该地址p已经持有 的地址a

指向指针的指针没有什么特别之处。
因此,持有另一个指针地址的指针链没有限制。
IE。

 int **************************************************************************z;

被允许。

于 2012-04-11T10:39:52.333 回答
18

每个 C++ 开发人员都应该听说过(臭名昭著的)三星级程序员

而且似乎真的有一些必须伪装的魔法“指针屏障”

来自 C2 的报价:

三星级程序员

C 程序员的评级系统。您的指针越间接(即变量前的“*”越多),您的声誉就越高。No-star C 程序员几乎不存在,因为几乎所有重要的程序都需要使用指针。大多数是一星程序员。在过去(嗯,我还年轻,所以至少在我看来,这些看起来像旧时代),偶尔会发现一段由三星级程序员完成的代码,并且敬畏地颤抖。有些人甚至声称他们在不止一个间接级别上看到了包含函数指针的三星级代码。对我来说听起来像不明飞行物一样真实。

于 2012-06-12T12:27:17.937 回答
13

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

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

typedef int *type0;
typedef type0 *type1;
typedef type1 *type2; /* etc */

所以最终,这是一个实现问题,与 C 程序在被拒绝之前可以制作多大/复杂的想法有关,这是非常特定于编译器的。

于 2012-04-10T11:41:37.407 回答
6

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

模板元编程是一种缓慢下降到疯狂的过程,因此在生成具有数千个间接级别的类型时没有必要找借口。例如,这只是将 peano 整数映射到模板扩展作为函数式语言的一种便捷方式。

于 2014-10-14T08:40:04.883 回答
4

2004 MISRA C标准的规则 17.5禁止超过 2 级的指针间接寻址。

于 2012-05-07T03:59:57.543 回答
0

没有真正的极限这样的东西,但极限是存在的。所有指针都是变量,通常存储在 stack而不是 heap中。堆栈通常很小(在某些链接期间可以更改其大小)。所以假设你有 4MB 堆栈,这是相当正常的大小。假设我们有 4 字节大小的指针(指针大小因架构、目标和编译器设置而异)。

在这种情况下4 MB / 4 b = 1024,可能的最大数量为 1048576,但我们不应忽视堆栈中存在其他一些东西的事实。

然而,一些编译器可能有最大数量的指针链,但限制是堆栈大小。因此,如果您在与无穷大链接期间增加堆栈大小并拥有运行具有无穷大内存的机器来运行处理该内存的操作系统,那么您将拥有无限的指针链。

如果您使用int *ptr = new int;并将指针放入堆中,那么通常的方式限制将是堆大小,而不是堆栈。

编辑只是意识到infinity / 2 = infinity。如果机器有更多内存,则指针大小会增加。因此,如果内存是无穷大并且指针的大小是无穷大,那么这是个坏消息...... :)

于 2013-07-30T07:15:19.707 回答
-1

这取决于您存储指针的位置。如果它们在堆栈中,则限制非常低。如果将其存储在堆中,则限制要高得多。

看看这个程序:

#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;
}

它创建 1M 指针,并在显示什么指向什么时很容易注意到链指向第一个变量number

顺便提一句。它使用92KRAM,所以想象一下你能走多深。

于 2013-08-01T20:00:00.857 回答