How many levels of pointers can we have?
Asked Answered
C

15

476

How many pointers (*) are allowed in a single variable?

Let's consider the following example.

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

Similarly we can have

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

and so on.

For example,

int ****************zz;
Carbonate answered 10/4, 2012 at 10:34 Comment(17)
Technically there is probably no limit (although that is compiler specific). The real limit is your (and your coworkers') mental capacity to keep track of what your variable actually represents. Making and keeping the code readable is usually more important than low level optimization. From this point of view, in production code already triple indirection (e.g. int***) is discouraged in most cases.Annunciator
You can keep adding levels of pointers until your brain explodes or the compiler melts - whichever happens soonest.Inglebert
Since a pointer to a pointer is again, well, just a pointer, there shouldn't be any theoretical limit. Maybe the compiler won't be able to handle it beyond some ridiculously high limit, but well...Homocercal
with the newest c++ you should use something like std::shared_ptr<shared_ptr<shared_ptr<...shared_ptr<int>...>>>Fireball
Why not just write a simple program to test this on your compiler? Whatever answered by others is likely to not exactly match your environment.Cantaloupe
@Thecrocodilehunter: Why? What is so destructive (or unconstructive ) about it?Wiesbaden
@Cantaloupe worst possible advice, it may compile and fail in the next version or it may fail now to a bug in the compiler. check the compiler documentation, check the standard and as last resort check the current implementation.Fireball
it can make sense for code generators...Faustinafaustine
@Fireball - this shows a problem in the C++ standard - there's no way to raise smart pointers to powers. We must immediately demand an extension to support e.g. (pow (std::shared_ptr, -0.3))<T> x; for -0.3 levels of indirection.Grandmotherly
It depends on your system's memory. If it exceeds that, the system will hang. Otherwise, it's infinite. You can try with while(1).Hassett
@Thecrocodilehunter: Mmmm, nah. This is on topic. And if any of you reddit people want to go through my questions/answers and massively upvote them, please feel free!Lyra
well logically it doesnt make any sense to create pointers to this n level. Compiler has no problems, but your program has if it uses soChristianism
@Steve314: You mean, like this? coliru.stacked-crooked.com/a/92a6666764acbaff :DPlethora
Note that there is one pretty common pointer which is usually written with three levels: char*** argv (or often written as char** argv[]). For example this is exactly the way you call MPI_Init (and other functions that may change your argv when passing it).Fence
@SevaAlekseyev Any program that requires that many levels of pointers is [1] probably very inefficient and [2] most likely has a lot of bugs in it. Just stick with one or two layers of pointers.Anaglyph
Tell that to Microsoft :) The object in question was the pointer to a smart pointer around a COM interface pointer, which effectively points at a pointer to an array of function pointers. With me yet? :)Haeres
It depends on how much memory you have to store the pointers (which are just variables holding addresses to other variables). Before declaring and initializing a pointer, it doesn't exist in memory.Barbarbarbara
F
418

The C standard specifies the lower limit:

###5.2.4.1 Translation limits

276 The implementation shall be able to translate and execute at least one program that contains at least one instance of every one of the following limits: [...]

279 — 12 pointer, array, and function declarators (in any combinations) modifying an arithmetic, structure, union, or void type in a declaration

The upper limit is implementation specific.

Farmstead answered 10/4, 2012 at 10:45 Comment(10)
The C++ standard "recommends" that an implementation support at least 256. (Readability recommends that you not exceed 2 or 3, and even then: more than one should be exceptional.)Stove
This limit is about how many in a single declaration; it does not impose an upper bound on how much indirection you can achieve via multiple typedefs.Bindle
@Bindle - yes, that is true. But since the spec is (no pun intended) specifying a mandatory lower limit, it is the effective upper bound all spec-compliant compilers are required to support. It could be lower than the vendor-specific upper bound, of course. Rephrased differently (to align it with the OP's question), it is the maximum allowed by the spec (anything else would be vendor-specific.) A bit off the tangent, programmers should (at least in the general case) treat that as their upper limit (unless they have a valid reason to rely on a vendor-specific upper bound)... me thinks.Carsick
On another note, I would start to cut myself if I had to work with code that had long-ass dereferencing chains (specially when liberally peppered all over the place.)Carsick
ok, but why exactly 12? Why not 5 or 40. Is there some explanation for this?Curarize
@beryllium: Usually these numbers come from taking a survey of pre-standardization software. In this case presumably they looked at common C programs and existent C compilers, and found at least one compiler that would have trouble with more than 12 and/or no programs that broke if you restricted it to 12.Bebel
I don't understand why a answer get more upvotes just because the writer has more reputation. This way answers which are actually good but by low reputation are hided.This answer was not as much constructive , but its upvotes are massive. Above Sachin Mhetra Answer was good but has so low upvotes.Oodles
@SurajJain When I posted my answer my rep was about 1500. All the existing answers were all wrong at that time. Hence, it got many upvotes. Later on, others edited their answers and/or improved after I posted my answer. You can see this from the edit history and time stamps of the answers. For example, see Alok Save's initial answer from the edit history, which was competely wrong.Farmstead
@P.P. Still , sachin mehra answer was constructive and i saw its edit history , no massive change, you jut posted some standard not explaining in your words , and it has above 300 upvotes .Maybe because OP did not asked why and just ask what is the upper limit and best way to tell is show the standard , but it would be nicer if you would explain reasoning also. Like Theoretically And Practically , down mihai user checked it, and showed what is limit and it is implementation specific , all these things in one answer makes it deserving for all those upvote ,not just referring to the standard.Oodles
@SurajJain If it's not upto what your standard, you can post a better answer. If you are concerned about "deservedness" of upvotes you can flag it to a moderator and explain the right number of upvotes this should have and see if they can help you.Farmstead
B
164

Actually, C programs commonly make use of infinite pointer indirection. One or two static levels are common. Triple indirection is rare. But infinite is very common.

Infinite pointer indirection is achieved with the help of a struct, of course, not with a direct declarator, which would be impossible. And a struct is needed so that you can include other data in this structure at the different levels where this can terminate.

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

now you can have list->next->next->next->...->next. This is really just multiple pointer indirections: *(*(..(*(*(*list).next).next).next...).next).next. And the .next is basically a noop when it's the first member of the structure, so we can imagine this as ***..***ptr.

There is really no limit on this because the links can be traversed with a loop rather than a giant expression like this, and moreover, the structure can easily be made circular.

Thus, in other words, linked lists may be the ultimate example of adding another level of indirection to solve a problem, since you're doing it dynamically with every push operation. :)

Bindle answered 10/4, 2012 at 15:44 Comment(14)
That's a completely different issue, though - a struct containing a pointer to another struct is very different than a pointer-pointer. An int***** is a distinct type from an int****.Azotic
It is not "very" different. The difference is fluffy. It is closer to syntax than semantics. A pointer to a pointer object, or a pointer to a structure object which contains a pointer? It's the same kind of thing. Getting to the tenth element of a list is ten levels of addressing indirection. (Of course, the ability to express an infinite structure depends on the struct type being able to point to itself via the incomplete struct type so that list->next and list->next->next are the same type; otherwise we would have to construct an infinite type.)Bindle
I didn't consciously notice that your name is fluffy when I used the word "fluffy". Subsconscious influence? But I am sure I have used the word in this way before.Bindle
Hmmm! I'm not sure this is really an infinite pointer indirection. Semantically speaking you're dealing with only one type (struct list *) and only one pointer is dereferenced at a time, each padded by an "almost no op". (As has already been practically pointed out, but hey.)Compeer
In C, one pointer is dereferenced at a time no matter what. You cannot dereference the next pointer until you load the previous one. *******x is one at a time, exactly like x->next->next->next->next.Bindle
Also remember that in machine language, you could just iterate on something like LOAD R1, [R1] as long as R1 is a valid pointer at every step. There are no types involved other than "word which holds an address". Whether or not there are declared types doesn't determine the indirection and how many levels it has.Bindle
+1. But in the strict sense - for having infinite number of indirections - we must have infinite address space, which isn't the case.Chemmy
Not if the structure is circular. If R1 holds the address of a location which points to itself then LOAD R1, [R1] can be executed in an infinite loop.Bindle
This answer is not answering the question asked, which is about declarations.Jewry
@Jewry it certainly is about a declarations. The struct declaration is self-referential ("I contain a pointer to myself"), something which C painstakingly allows via the incomplete struct type mechanism. This creates an infinite level of pointer indirection.Bindle
@Bindle the point is that traversal of a long chain of ->next elements is controlled by the programmer; to handle *****....*****int the compiler has to internally represent the depth of the pointers (so it can detect incorrect dereferencing). Some ways of storing this representation may only be limited by memory (used by the compiler) but some methods may have hard limits.Britska
@Britska P->memb and *P are both syntax. How deeply that syntax is nested is controlled by the programmer. The compiler has to represent P->memb->memb->memb->memb... and it has to represent ***..P.Bindle
There is a difference, mainly during compilation. For P->next that happens to point to another P, the compiler only needs to know that it is a pointer to the parent structure. At compile time it doesn't care how long a string of linked elements you create at runtime. For *****int, the compiler at compile time has to represent the type for every level ... that is *****int is a pointer to a ****int which points to ***int ... to a *int which finally points to an int. For some compilers, under some conditions, it may run out of space to do so.Britska
@Britska You can get avoid constructing a deep static type by using something like DEREFI(DEREFV(DEREFV...(DEREFV(P)) ...)) where P is a void * pointer, DEREFV is #define DEREFV(P) (*((void **) (P))) and DEREFI is similar, but with a cast to int *. I.e. an arbitrarily long chain of void * pointers that point to void *, and finally an int.Bindle
T
84

Theoretically:

You can have as many levels of indirections as you want.

Practically:

Of course, nothing that consumes memory can be indefinite, there will be limitations due to resources available on the host environment. So practically there is a maximum limit to what an implementation can support and the implementation shall document it appropriately. So in all such artifacts, the standard does not specify the maximum limit, but it does specify the lower limits.

Here's the reference:

C99 Standard 5.2.4.1 Translation limits:

— 12 pointer, array, and function declarators (in any combinations) modifying an arithmetic, structure, union, or void type in a declaration.

This specifies the lower limit that every implementation must support. Note that in a footenote the standard further says:

18) Implementations should avoid imposing fixed translation limits whenever possible.

Transience answered 10/4, 2012 at 10:35 Comment(8)
indirections don't overflow any stacks!Dentistry
Corrected, I had this errie feeling of reading & answering the q as limit of parameters being passed to the function. I don't know why?!Transience
@basile - I'd expect stack-depth to be an issue in the parser. Many formal parsing algorithms have a stack as a key component. Most C++ compilers probably use a variant of recursive descent, but even that relies on the processor stack (or, pedantically, on the language acting as if there was a processor stack). More nesting of grammar rules means a deeper stack.Grandmotherly
indirections don't overflow any stacks!--> No! parser stack can overflow. How does the stack relate to pointer indirection? Parser stack!Stomacher
If * is overloaded for a number of classes in a row, and each overload returns an object of other type in the row, then there can be stackoverflow for such chained function calls.Wiesbaden
@PéterTörök may be stack matter while compiler parse an expression consist of many *sAraby
From a practical perspective, the translation limits are at best recommendations, since the Standard says nothing about what combinations of limits must be supported. Many real-world implementations would die if code tried to include the maximum allowable number of parameters and automatic objects in a function, and they all took 32767 bytes each. On the other hand, nothing in the Standard would forbid a conforming-but-useless implementation from saying that no function can both contain more than one automatic object and have any automatic object which is larger than one byte in size.Theresiatheresina
The authors of the Standard acknowledged the possibility of conforming-but-useless implementations, but expected that (1) people making a bona fide effort to produce a usable implementation won't do stupid things merely because the Standard would allow them to, and (2) it wasn't worth worrying about what kinds of compilers might be produced by people who weren't making such an effort.Theresiatheresina
S
80

As people have said, no limit "in theory". However, out of interest I ran this with g++ 4.1.2, and it worked with size up to 20,000. Compile was pretty slow though, so I didn't try higher. So I'd guess g++ doesn't impose any limit either. (Try setting size = 10 and looking in ptr.cpp if it's not immediately obvious.)

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;
}
Shoelace answered 10/4, 2012 at 11:12 Comment(2)
I couldn't get more than 98242 when I tried it. (I did the script in Python, doubling the number of * until I got one that failed, and the preceding one that passed; I then did a binary search over that interval for the first one that failed. The whole test took less than a second to run.)Stove
For 200_000, the ptr.cpp file weighs 19G, and 150G of RAM is not enough to compile it.Bradney
S
64

Sounds fun to check.

  • Visual Studio 2010 (on Windows 7), you can have 1011 levels before getting this error:

    fatal error C1026: parser stack overflow, program too complex

  • gcc (Ubuntu), 100k+ * without a crash ! I guess the hardware is the limit here.

(tested with just a variable declaration)

Scorn answered 10/4, 2012 at 18:36 Comment(1)
Indeed, the productions for unary operators are right-recursive, which means that a shift-reduce parser will shift all of the * nodes onto the stack before being able to make a reduction.Bindle
T
30

There is no limit, check example at Pointers :: C Interview Questions and Answers.

The answer depends on what you mean by "levels of pointers." If you mean "How many levels of indirection can you have in a single declaration?" the answer is "At least 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 */

If you mean "How many levels of pointer can you use before the program gets hard to read," that's a matter of taste, but there is a limit. Having two levels of indirection (a pointer to a pointer to something) is common. Any more than that gets a bit harder to think about easily; don't do it unless the alternative would be worse.

If you mean "How many levels of pointer indirection can you have at runtime," there's no limit. This point is particularly important for circular lists, in which each node points to the next. Your program can follow the pointers forever.

Tritanopia answered 10/4, 2012 at 10:37 Comment(7)
There's almost certainly a limit, since the compiler has to keep track of the information in a finite amount of memory. (g++ aborts with an internal error at 98242 on my machine. I expect that the actual limit will depend on the machine and the load. I also don't expect this to be a problem in real code.)Stove
Yeah @MatthieuM. : I just considered theoretically :) Thanks James for completing answerTritanopia
Well, linked lists aren't really a pointer to a pointer, they're a pointer to a struct that contains a pointer (either that or you end up doing a lot of unnecessary casting)Valentijn
@Valentijn : Yeah! but don't you think it is well structured?Tritanopia
@Random832: Nand said 'If you mean "How many levels of pointer indirection can you have at runtime,"' so he was explicitly removing the restriction of just talking about pointers to pointers (*n).Brockbrocken
I don't get your point: 'There is no limit, check example here.' The example is no proof that there is no limit. It only proves that a 12 star indirection is possible. Neither proves anything the circ_list example regarding the OP's question: The fact that you can traverse a pointers list doesn't imply that the compiler can compile a n-stars indirection.Buckwheat
I mean, There is no limit and for more info check that example. Think on circ_list example.Tritanopia
F
26

It's actually even funnier with pointer to functions.

#include <cstdio>

typedef void (*FuncType)();

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

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

As illustrated here this gives:

Hello, World!
Hello, World!
Hello, World!

And it does not involve any runtime overhead, so you can probably stack them as much as you want... until your compiler chokes on the file.

Frumenty answered 10/4, 2012 at 14:16 Comment(0)
H
21

There is no limit. A pointer is a chunk of memory whose contents are an address.
As you said

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

A pointer to a pointer is also a variable which contains an address of another pointer.

int **q = &p;

Here q is pointer to pointer holding the address of p which is already holding the address of a.

There is nothing particularly special about a pointer to a pointer.
So there is no limit on chain of poniters which are holding the address of another pointer.
ie.

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

is allowed.

Honewort answered 11/4, 2012 at 10:39 Comment(0)
I
19

Every C++ developer should have heard of the (in)famous Three star programmer.

And there really seems to be some magic "pointer barrier" that has to be camouflaged.

Quote from C2:

Three Star Programmer

A rating system for C-programmers. The more indirect your pointers are (i.e. the more "*" before your variables), the higher your reputation will be. No-star C-programmers are virtually non-existent, as virtually all non-trivial programs require use of pointers. Most are one-star programmers. In the old times (well, I'm young, so these look like old times to me at least), one would occasionally find a piece of code done by a three-star programmer and shiver with awe. Some people even claimed they'd seen three-star code with function pointers involved, on more than one level of indirection. Sounded as real as UFOs to me.

Indispose answered 12/6, 2012 at 12:27 Comment(0)
B
13

Note that there are two possible questions here: how many levels of pointer indirection we can achieve in a C type, and how many levels of pointer indirection we can stuff into a single declarator.

The C standard allows a maximum to be imposed on the former (and gives a minimum value for that). But that can be circumvented via multiple typedef declarations:

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

So ultimately, this is an implementation issue connected to the idea of how big/complex can a C program be made before it is rejected, which is very compiler specific.

Bindle answered 10/4, 2012 at 11:41 Comment(0)
O
6

I'd like to point out that producing a type with an arbitrary number of *'s is something that can happen with template metaprogramming. I forget what I was doing exactly, but it was suggested that I could produce new distinct types that have some kind of meta maneuvering between them by using recursive T* types.

Template Metaprogramming is a slow descent into madness, so it is not necessary to make excuses when generating a type with several thousand level of indirection. It's just a handy way to map peano integers, for example, onto template expansion as a functional language.

Oviposit answered 14/10, 2014 at 8:40 Comment(0)
P
2

Rule 17.5 of the 2004 MISRA C standard prohibits more than 2 levels of pointer indirection.

Phenol answered 7/5, 2012 at 3:59 Comment(2)
Pretty sure that's a recommendation for programmers, not for compilers.Rhombencephalon
I read the document with rule 17.5 about more than 2 levels of pointer indirection. And it doesn't necessarily prohibit more than 2 levels. It does state that the ruling should be followed as more than 2 levels is "non-compliant" to their standards. The important word or phrase in their ruling is the use of the word "should" from this statement: Use of more than 2 levels of indirection can seriously impair the ability to understand the behavior of the code, and should therefore be avoided. These are guidelines set by this organization as opposed to rules set by the language standard.Erdei
E
1

The limits vary quite drastically between languages: 256 in C++, 12 in C. Note that the limit not only applies to how many pointers there can be in a declarator; it also applies to function, array, etc. declarators.

C++23

The minimum limit can be found in Annex B - Implementation quantities .

Pointer, pointer-to-member, array, and function declarators (in any combination) modifying a type in a declaration [256].

- [implimits] p2.3

C17

The minimum limit can be found in 5.2.4.1 Translation limits.

12 pointer, array, and function declarators (in any combinations) modifying an arithmetic, structure, union, or void type in a declaration

- 5.2.4.1 Translation limits, p1


Both limits are minimums. The exact limit is implementation-defined and may be greater.

Elenoraelenore answered 9/1, 2024 at 22:32 Comment(0)
G
-3

There isn't such a thing like real limit but limit exists. All pointers are variables that are usually storing in stack not heap. Stack is usually small (it is possible to change its size during some linking). So lets say you have 4MB stack, what is quite normal size. And lets say we have pointer which is 4 bytes size (pointer sizes are not the same depending on architecture, target and compiler settings).

In this case 4 MB / 4 b = 1024 so possible maximum number would be 1048576, but we shouldn't ignore the fact that some other stuff is in stack.

However some compilers may have maximum number of pointer chain, but the limit is stack size. So if you increase stack size during linking with infinity and have machine with infinity memory which runs OS which handles that memory so you will have unlimited pointer chain.

If you use int *ptr = new int; and put your pointer into heap, that is not so usual way limit would be heap size, not stack.

EDIT Just realize that infinity / 2 = infinity. If machine has more memory so the pointer size increases. So if memory is infinity and size of pointer is infinity, so it is bad news... :)

Gunsel answered 30/7, 2013 at 7:15 Comment(5)
A) Pointers can be stored on the heap (new int*). B) An int* and an int********** have the same size, at least on reasonable architectures.Subacid
@rightfold A) Yes pointers can be stored in heap. But it would be very different thing like creating container which hold pointers what are pointing to the next previous pointer. B) Of course int* and an int********** have the same size, I didn't said that they have different.Gunsel
Then I don't see how stack size is even remotely relevant.Subacid
@rightfold I've been thinking about usual way of data distribution when all data is in heap and on stack it is just pointers to that data. It would be usual way, but I agree that it is possible to put pointers in stack.Gunsel
"Of course int* and an int********** have the same size" - the standard doesn't guarantee that (although I know of no platform where it isn't true).Epoch
G
-4

It depends on the place where you store pointers. If they are in stack you have quite low limit. If you store it in heap, you limit is much much much higher.

Look at this program:

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

It creates 1M pointers and at the shows what point to what it is easy to notice what the chain goes to the first variable number.

BTW. It uses 92K of RAM so just imagine how deep you can go.

Gunsel answered 1/8, 2013 at 20:0 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.