Pointer to a pointer

427 views
Skip to first unread message

shan...@gmail.com

unread,
Mar 9, 2022, 9:58:42 PM3/9/22
to golang-nuts
This morning someone asked about dereferincing a pointer to a pointer to a pointer

At first nobody had ever thought about, let alone knew the answer, but some example code was shown, and sure enough ***val is possible
```
package main

import "fmt"

func main() {
        a := 0
        b := &a
        c := &b
        UltimatePointOne(&c)
        fmt.Println(a)
}

func UltimatePointOne(n ***int) {
        ***n = 1
}
```


On a lark a go playground example was tried to find what the maximum * is in Go

https://go.dev/play/p/YhibY3p7TSD

There's 28 there, but it's not the limit

Does anyone know what the upper bound on this could be?

256 * ?

32k * ?

Jan Mercl

unread,
Mar 9, 2022, 11:38:58 PM3/9/22
to shan...@gmail.com, golang-nuts
A linked list, for example, consists of pointers to pointers to pointers...

Why should any limit exist to the length of the list except resources available?

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/60cf1568-31d3-426e-bfdc-0b4b98b53acdn%40googlegroups.com.

Kurtis Rader

unread,
Mar 10, 2022, 12:08:02 AM3/10/22
to Jan Mercl, shan...@gmail.com, golang-nuts
On Wed, Mar 9, 2022 at 8:38 PM Jan Mercl <0xj...@gmail.com> wrote:
A linked list, for example, consists of pointers to pointers to pointers...

Why should any limit exist to the length of the list except resources available?

Yes, but the O.P. was asking about a silly example. Specifically, when defining a function that receives pointers how many levels of indirection are allowed in the declaration. In practice 99.9% of the time a single level of indirection is specified and 0.09% of the time two levels are specified. Etcetera.  For example, if

func wtf(i ********int) {
}

is supported, which has eight levels of indirection, why isn't 16? 32? 64? Etcetera levels of indirection supported when defining a function. It's a silly question that shows the O.P. doesn't understand how compilers work. Let alone how people use languages like Go in real life.
 


--
Kurtis Rader
Caretaker of the exceptional canines Junior and Hank

shan...@gmail.com

unread,
Mar 10, 2022, 12:11:19 AM3/10/22
to golang-nuts
Um

Really?

Which of these things are you specifically trying to prevent happening
 - Curiousity
 - Creativity
 - Asking questions
 - Some combination of the above


I mean, I appreciate that you think that people should *know* whatever it is you think you know, but that's a really *really* poor response

Kurtis Rader

unread,
Mar 10, 2022, 12:29:54 AM3/10/22
to shan...@gmail.com, golang-nuts
On Wed, Mar 9, 2022 at 9:12 PM shan...@gmail.com <shan...@gmail.com> wrote:
Um

Really?

Which of these things are you specifically trying to prevent happening
 - Curiousity
 - Creativity
 - Asking questions
 - Some combination of the above

I mean, I appreciate that you think that people should *know* whatever it is you think you know, but that's a really *really* poor response

Yes, your question was silly. The limit is going to be both platform dependent and dependent on the resources (e.g., memory) available on the platform. Your question is silly because regardless of the fundamental limits imposed by the Go language or the platform it runs on absolutely no one will ever write a function that gets within many orders of magnitude of the limit. So your question is interesting in a hypothetical sense but not in a practical sense. For the former I suggest you start a research project and write a paper for review that explains why, or why not, the existing limit is a problem.
 

Rob Pike

unread,
Mar 10, 2022, 12:44:52 AM3/10/22
to Kurtis Rader, shan...@gmail.com, golang-nuts
Here's a program with 1000 *s. You can see the pattern, make it any
number you like.

https://go.dev/play/p/FZXWcQTutEG

// You can edit this code!
// Click here and start typing.
package main

import "fmt"

type self *self

func main() {
var p self
p = &p
fmt.Println(****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************p)
}


-rob
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CABx2%3DD_JhPrwy88teX68DzceaKBK5YuFhn%2Bt%2BKLnW9M-4JYrJw%40mail.gmail.com.

Volker Dobler

unread,
Mar 10, 2022, 12:54:03 AM3/10/22
to golang-nuts

Does anyone know what the upper bound on this could be?


Yes, I do: math.MaxUint64 !

The nice thing about upper bounds is that they can be larger than
the least upper bound :-) 

V.

shan...@gmail.com

unread,
Mar 10, 2022, 1:04:22 AM3/10/22
to golang-nuts
> The limit is going to be both platform dependent and dependent on the resources (e.g., memory) available on the platform. 

Then this is the answer you should have started with


> absolutely no one will ever write a function that gets within many orders of magnitude of the limit

I mean, nobody will ever go to the stars, so nobody should ask what it would take to get their, right

That's really bad. That's attacking "creativity" and "curiousity" both at the same time


> So your question is interesting in a hypothetical sense 

This is also "curiousity" - learn to recognise it so you don't stifle it in future.



Message has been deleted

peterGo

unread,
Mar 10, 2022, 1:41:12 AM3/10/22
to golang-nuts
Kurtis Rader,

Stop it. When you are in a hole, stop digging.

When you don't understand a question or topic, don't launch ad hominem attacks, calling people's questions silly. That's nasty and it's against the Go Code of Conduct.

Like Jan, Rob, and Volker, I found it to be an interesting question, worthy of an answer. I wrote a program and looked at the executable code. Are we silly too?

Peter

Dan Kortschak

unread,
Mar 10, 2022, 1:53:05 AM3/10/22
to golang-nuts
I think aspects of this thread are sad. None of us know the background
of the OP and this kind of thinking illustrates a joyful level of
curiosity that could have been answered in a way that helps the
questioner and build a positive community (for Shane, Rob did answer it
and in way that is really quite deep, and thinking about how he
answered it will teach you something that is worth learning).

Calling a question "silly" demeans the questioner without understanding
where they are coming from.

Dan


Rob Pike

unread,
Mar 10, 2022, 4:31:58 AM3/10/22
to shan...@gmail.com, golang-nuts
On Thu, Mar 10, 2022 at 5:08 PM shan...@gmail.com <shan...@gmail.com> wrote:
>
> Is this really how you want to be known?

Sure, why not? It's a more interesting program than one might think.

For a richer example of the foundational idea here, see the peano.go
program in the test directory in the repo.

-rob

shan...@gmail.com

unread,
Mar 10, 2022, 4:45:49 AM3/10/22
to golang-nuts
Apologies; I had misunderstood your code and was thinking that - because of earlier responses - this wasn't going to be a nice day.

> for Shane, Rob did answer it and in way that is really quite deep, and thinking about how he answered it will teach you something that is worth learning)

Yeah it's definitely above my paygrade and will take some time for me to get my head around

Manlio Perillo

unread,
Mar 10, 2022, 6:41:52 AM3/10/22
to golang-nuts
Interesting example, thanks.

But how does `type self *self` works?  If I remember correctly, it is not discussed in the Language Specification and in The Go Programming Language book.

By the way, in C `typedef self *self` is invalid (assuming my code is written correctly).

Manlio

wagner riffel

unread,
Mar 10, 2022, 7:40:37 AM3/10/22
to Manlio Perillo, golang-nuts
On Thu Mar 10, 2022 at 12:41 PM CET, Manlio Perillo wrote:
> Interesting example, thanks.
>
> But how does `type self *self` works? If I remember correctly, it is not
> discussed in the Language Specification and in The Go Programming Language
> book.
>

I don't think it's mentioned in the specification, my bet is that
unless your type requires inifnity amout of memory (eg: `type t struct
{t}`) or the type is an interface and break its rules, (eg: `type
iface interface{ iface }`) you can use self-reference.

> By the way, in C `typedef self *self` is invalid (assuming my code is
> written correctly).

You're correct, C doesn't allow self reference in type decl, more
interesting types beside pointers:
`type fn func() fn`
`type s []s`
`type ch chan ch`
`type m map[*m]m`

-w

Jan Mercl

unread,
Mar 10, 2022, 8:04:44 AM3/10/22
to wagner riffel, Manlio Perillo, golang-nuts
On Thu, Mar 10, 2022 at 1:40 PM 'wagner riffel' via golang-nuts <golan...@googlegroups.com> wrote:

> I don't think it's mentioned in the specification, my bet is that
> unless your type requires inifnity amout of memory (eg: `type t struct
> {t}`) or the type is an interface and break its rules, (eg: `type
> iface interface{ iface }`) you can use self-reference.

The validity of `type T *T` in Go is based on two things: 1) The visibility of the identifier in `type T ...` is specified to start right after the identifier, 2) It's possible, in this case, to compute the size of type T. So no problem here.

> You're correct, C doesn't allow self reference in type decl, more ...

In `typedef self *self;` the visibility of the second instance of identifier `self` starts only after it, ie. preceding the final `;'. So the first identifier `self`, the one after `typedef` is undefined and that's the real reason it does not work. However:

----
jnml@e5-1650:~/tmp$ cat main.c
typedef int T;
typedef T T;

int main() {}
jnml@e5-1650:~/tmp$ gcc -Wall main.c
jnml@e5-1650:~/tmp$

----

This works because typedef is equal to Go type aliases. But the full Go thing cannot work:

----
jnml@e5-1650:~/tmp$ cat main.c
typedef int T;
typedef T *T;

int main() {}
jnml@e5-1650:~/tmp$ gcc -Wall main.c
main.c:2:12: error: conflicting types for ‘T’
    2 | typedef T *T;
      |            ^
main.c:1:13: note: previous declaration of ‘T’ was here
    1 | typedef int T;
      |            
----

Here the problem is that we define T to be two different types, the first it's an int and the second is a pointer to int.

Axel Wagner

unread,
Mar 10, 2022, 8:18:32 AM3/10/22
to golang-nuts
TBH I find it rather surprising that the spec does not mention why `type X X` is *not* allowed.
It's obvious that it can't be, but from what I can tell, the spec doesn't forbid it.
Otherwise I would've chalked up the validity of `type X *X` for "anything that's not forbidden, is allowed". A type definition is

TypeDef = identifier Type .
Type = TypeName | TypeLit | "(" Type ")" .
TypeName  = identifier | QualifiedIdent .
TypeLit   = … | PointerType | … . 
PointerType = "*" BaseType .
BaseType    = Type .
 
So `type X *X` is obviously valid (after taking scope into account, as Jam mentions). But so would `type X X` be.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Jan Mercl

unread,
Mar 10, 2022, 8:32:45 AM3/10/22
to Axel Wagner, golang-nuts
On Thu, Mar 10, 2022 at 2:18 PM 'Axel Wagner' via golang-nuts
<golan...@googlegroups.com> wrote:

> TBH I find it rather surprising that the spec does not mention why `type X X` is *not* allowed.

The specs guarantee unsafe.Sizeof() to produce the size. I think that
gives a Go compiler the right to reject that type definition.

Manlio Perillo

unread,
Mar 10, 2022, 8:37:22 AM3/10/22
to golang-nuts
On Thursday, March 10, 2022 at 2:04:44 PM UTC+1 Jan Mercl wrote:
On Thu, Mar 10, 2022 at 1:40 PM 'wagner riffel' via golang-nuts <golan...@googlegroups.com> wrote:

> I don't think it's mentioned in the specification, my bet is that
> unless your type requires inifnity amout of memory (eg: `type t struct
> {t}`) or the type is an interface and break its rules, (eg: `type
> iface interface{ iface }`) you can use self-reference.

The validity of `type T *T` in Go is based on two things: 1) The visibility of the identifier in `type T ...` is specified to start right after the identifier, 2) It's possible, in this case, to compute the size of type T. So no problem here.


The only reference I found in the spec (after a quick search) is:
    8. The scope of a type identifier declared inside a function begins at the identifier in the TypeSpec and ends at the end of the innermost containing block.

> [...]

Thanks 
Manlio

wagner riffel

unread,
Mar 10, 2022, 8:41:30 AM3/10/22
to Jan Mercl, Manlio Perillo, golang-nuts
On Thu Mar 10, 2022 at 2:02 PM CET, Jan Mercl wrote:
> In `typedef self *self;` the visibility of the second instance of
> identifier `self` starts only after it, ie. preceding the final `;'. So the
> first identifier `self`, the one after `typedef` is undefined and that's
> the real reason it does not work. However:

Thanks for the clarification, I knew by heart that it wasn't allowed,
at laest, with pointers.

-w

Axel Wagner

unread,
Mar 10, 2022, 8:48:27 AM3/10/22
to Manlio Perillo, golang-nuts
Also:
The scope of an identifier denoting a constant, type, variable, or function (but not method) declared at top level (outside any function) is the package block.


 

> [...]

Thanks 
Manlio

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Holloway Kean Ho

unread,
Mar 10, 2022, 8:54:12 AM3/10/22
to golang-nuts
HI all, 

> Does anyone know what the upper bound on this could be?

Interesting question. I have a thought experiment theory: "LIMIT_MAX = (TOTAL_MEMORY - OTHER_SERVICES - DATA) / MBIT"

Explanation:
1. Assuming you're using 64-bit OS and memory structure is aligned using 64-bit memory, your MBIT should be `uint64` for each memory address as a value.
2. Each level of pointer (e.g. pointer of pointer ) shall only consume 1 `uint64` memory space.
3. Iterate the thinking recursively and you eventually ran out of memory available on a given system.
4. Hence, the upper limit, LIMIT_MAX shall be the remaining free memory after subtracting other services consuming the memory space (OTHER_SERVICES)  and the initial data size, the value of the first degree pointer (DATA).

Note:
1. For 32-bit memory addressing, it's at max 4GB at `uint32` per iteration due to the addressing capacity limit, which is why 64-bit CPU is created at the first place.

----
I may be wrong but I'm curious to know. As for how to prove it, I have no idea and I don't intend to crash my laptop at the moment. Besides, I never use pointer of pointer beyond 3 degree deep. =p

Regards,
Holloway

Manlio Perillo

unread,
Mar 10, 2022, 9:00:32 AM3/10/22
to golang-nuts
On Thursday, March 10, 2022 at 2:48:27 PM UTC+1 axel.wa...@googlemail.com wrote:
On Thu, Mar 10, 2022 at 2:38 PM Manlio Perillo <manlio....@gmail.com> wrote:
On Thursday, March 10, 2022 at 2:04:44 PM UTC+1 Jan Mercl wrote:
On Thu, Mar 10, 2022 at 1:40 PM 'wagner riffel' via golang-nuts <golan...@googlegroups.com> wrote:

> I don't think it's mentioned in the specification, my bet is that
> unless your type requires inifnity amout of memory (eg: `type t struct
> {t}`) or the type is an interface and break its rules, (eg: `type
> iface interface{ iface }`) you can use self-reference.

The validity of `type T *T` in Go is based on two things: 1) The visibility of the identifier in `type T ...` is specified to start right after the identifier, 2) It's possible, in this case, to compute the size of type T. So no problem here.


The only reference I found in the spec (after a quick search) is:
    8. The scope of a type identifier declared inside a function begins at the identifier in the TypeSpec and ends at the end of the innermost containing block.

Also:
The scope of an identifier denoting a constant, type, variable, or function (but not method) declared at top level (outside any function) is the package block.


But this seems different from "The scope of a type identifier declared inside a function **begins** at the identifier in the ...".
My interpretation of the text you mentioned is: the identifier is **not** in scope **until** the type definition is complete.

Thanks
Manlio

Axel Wagner

unread,
Mar 10, 2022, 9:17:53 AM3/10/22
to Manlio Perillo, golang-nuts
"The identifier in the type declaration" is the identifier in the production
TypeDecl = identifier Type .
Note that Type itself does not have to be an identifier (and often isn't), so that can't be what's meant.

So, FWIW, a) the code posted by Rob doesn't include a type declared inside a function, but b) even if it did, it would be valid, because the scope would begin after the `type` keyword and extend until the end of the block.

Axel Wagner

unread,
Mar 10, 2022, 9:18:17 AM3/10/22
to Manlio Perillo, golang-nuts
Correction: The production is called "TypeDef", not "TypeDecl"

Axel Wagner

unread,
Mar 10, 2022, 9:34:09 AM3/10/22
to Manlio Perillo, golang-nuts
For the type declaration
type self *self
The relevant parts of the grammar are
TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
TypeSpec = AliasDecl | TypeDef .
AliasDecl = identifier "=" Type .

TypeDef = identifier Type .
Type = TypeName | TypeLit | "(" Type ")" .
TypeName  = identifier | QualifiedIdent .
TypeLit   = … | PointerType | … .
PointerType = "*" BaseType .
BaseType    = Type .

1. The `type` keyword introduces the `TypeDecl` production
2. What follows must either be `identifier Type` or `identifer = Type`, so either way it has to be an `identifier`. In this case, it's the former, with `identifier` being `self`. It's this `identifier`, which starts the scope (there is an ambiguity about whether "at the identifier" means "before" or "after" the identifier, but for this question, it doesn't matter, as it contains the `Type` part of the `TypeDef` either way)
3. The `Type` of the `TypeDef` then has to be a `PointerType` `TypeLit`, as it starts with a "*"
4. The `BaseType` of the `PointerType` is then another `identifier` "self". This must refer to the "self" declared by the `TypeDecl`, as the scope of that "self" includes this part.

Note that `type self *self` working is not materially different from `type List struct { v int; next *List }` working. Just that in the latter case, it's not a `PointerType`, but a `StructType`. But the underlying syntactical rules around scoping are exactly the same.

 

Rob Pike

unread,
Mar 10, 2022, 6:59:52 PM3/10/22
to Manlio Perillo, golang-nuts
This topic has come up before. The scope of the identifier is set up
to allow, unlike in C, constructs such as

type stateFunction func() stateFunction

as is used in my talk about a scanner, https://talks.golang.org/2011/lex.slide

As I mentioned above, recursive type definitions appear even in an
early (and very interesting) test.

-rob
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/43992fed-8c73-444d-aedc-19d549726896n%40googlegroups.com.

Jochen Voss

unread,
Mar 15, 2022, 7:10:00 AM3/15/22
to golang-nuts
Thanks for the pointer to peano.go, this is fun!

It took me a while to locate the file.  In case others are interested: peano.go is at https://github.com/golang/website/blob/master/_content/doc/play/peano.go and can also be found by choosing "Peano Integers" from the top-right menu in the Go playground.

All the best,
Jochen

Rob Pike

unread,
Mar 15, 2022, 8:12:17 AM3/15/22
to Jochen Voss, golang-nuts
Actually the one I was referring to is in the main repo at
test/peano.go, but it's the same idea.

-rob
> --
> You received this message because you are subscribed to the Google Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/efbb4589-ff23-4e57-ae6f-0ab4e82ca1cfn%40googlegroups.com.

Thomas Bushnell BSG

unread,
Mar 15, 2022, 11:42:43 AM3/15/22
to Jan Mercl, shan...@gmail.com, golang-nuts
On Wed, Mar 9, 2022 at 11:38 PM Jan Mercl <0xj...@gmail.com> wrote:
A linked list, for example, consists of pointers to pointers to pointers...

Not in the normal implementation it doesn't. Typically it might be:

type element struct {
  value int
  next *element
}

next is a pointer to an element, not a pointer to a pointer. That element contains within it a pointer, but next is not a pointer to a pointer. If it were, it would be declared with two stars.

Thomas 

Jan Mercl

unread,
Mar 15, 2022, 11:51:46 AM3/15/22
to Thomas Bushnell BSG, shan...@gmail.com, golang-nuts
On Tue, Mar 15, 2022 at 4:41 PM Thomas Bushnell BSG
<tbus...@google.com> wrote:

> Not in the normal implementation it doesn't. Typically it might be:
>
> type element struct {
> value int
> next *element
> }
>
> next is a pointer to an element, not a pointer to a pointer. That element contains within it a pointer, but next is not a pointer to a pointer. If it were, it would be declared with two stars.

Although linked lists usually do contain a payload, as you note, I
consider `type peano *peano` a payload-less linked list. Or a linked
list with zero sized payload one could also say. And once a node
contains zero bits of a payload, it contains only the link field. And
a struct with a single field is another interesting case that can be
abstracted out. Numbers 0 and 1 make many things much more interesting
;-)

Thomas Bushnell BSG

unread,
Mar 15, 2022, 11:55:06 AM3/15/22
to Rob Pike, shan...@gmail.com, golang-nuts
This is lovely. I'm thinking about the relation to other ways of defining models of numerals.

For example, Church numerals are much more annoying (if logically simpler in some sense); peano.go offers a trivial predecessor function which is famously annoying for Church numerals.

Finite Von Neumann ordinals are very similar - we interpret  a Number as a set of all the things which can be reached from it by applying *, and then each number is the set of all smaller numerals in the right way. And infinite chains of pointers map to ordinals exactly as we might want. However this doesn't encode general sets; by definition it can only define transitive sets, so it gets you ordinals, but only ordinals and not in the larger universe.

The name "peano.go" also suggests to me that it's not quite PA that we're modeling here so much as PA numerals.... what happens if we are working in a space where the Numbers might be non-standard? The functions now don't terminate, but because of Tennenbaum's Theorem, there might be very little we can do to improve on that.

Thomas

Thomas Bushnell BSG

unread,
Mar 15, 2022, 11:57:08 AM3/15/22
to Jan Mercl, shan...@gmail.com, golang-nuts
Yes indeed! These constructions all give us expressions with many consecutive stars. But they don't give us types with that. (and you can't assign a *Number to a **Number, for example)

Brian Candler

unread,
Mar 15, 2022, 1:01:24 PM3/15/22
to golang-nuts
On Tuesday, 15 March 2022 at 15:51:46 UTC Jan Mercl wrote:
a struct with a single field is another interesting case that can be
abstracted out.

Aside: a struct with *no* fields is also more useful than you might think - e.g. to use a map as a sparse set.

        m := make(map[int]struct{})
        m[123] = struct{}{}
 
Reply all
Reply to author
Forward
0 new messages