Understand pointers and how they differ from C++

Hi. I’m trying to learn Go. I first wrote this code on Modula-2, and then in C++, and now in Go.
I’m getting an unusual output for the first grouping of print statements. Whenever I printf AnEntryPointer, I get all zeros in the output. When I follow the link lists forwards and backwards, I get a long number for the value of the pointer fields. I think this number is too long to be a pointer.

What am I missing?


  3/12/15 From modula2.org tutorial.  This section deals with dynamic memory usage.  I will change it around so I better understand it.
              And I've made it a double linked list.
  3/13/15 Will add output of the pointers so I can compare this with the prev and next field contents.  And I changed the name
              of variable AnEntry to AnEntryPointer.
  3/15/15 Converting to C++, and changing more names to be clearer that they are pointers.
  3/18/15 Removed the char cast to see if it still works.  It does.
  3/18/15 Made AdrToHexStr also a function.
  6/7/16 Converted to Go

package main

import (
//        "strings"
//        "unsafe"

type FullName struct {
  PrevP *FullName;
  NextP *FullName;
  FirstName string;
  Initial byte;
  LastName string;
}; // struct FullName

    StartOfListP,EndofListP,CurrentPlaceInListP,PrevPlaceInListP,AnEntryPointer *FullName;
    I int;
    s,s0 string;

func AdrToHexStr(adr unsafe.Pointer) (OutStr1,OutStr2 string) {

  const ASCZERO = '0'; // int
  const ascA    = 'A'; // int
  var h int;
  var OutInt [16]int;

  Str20 := ""; // receives the hex characters, one by one, in reverse
  OutStr2 = "               ";  // 16 spaces to be filled in reverse
  i := 0;
  for j := 0; j < 16; j++ {
    OutInt[j] = ' ';

  L := int(uintptr(adr));

  for  {    //  until L = 0 
    h = L % 16; // % is MOD op
    if h <= 9 {
      Str20 += string(h + ASCZERO);
      OutInt[i] = h + ASCZERO;
      Str20 += string(h -10 + ascA);
      OutInt[i] = h -10 + ascA;
    }; // h <= 9
    L = L / 16;
    if L == 0 { break };
  } // until L = 0

  OutStr1 = " ";
  j := 0;
  for { // until i = 0 
    OutStr1[j] = Str20[i];
    if i == 0 { break };
  OutStr2 = string(OutInt);
  OutStr2 = strings.Trim(OutStr2," ");
} // AdrToHexStr            This entire function can be replaced by a call to Sprintf, but nevermind that

func main() {

  StartOfListP = nil;
  EndofListP = nil;
  CurrentPlaceInListP = nil;
  PrevPlaceInListP = nil;

                  /* Generate the first name in the list */
  AnEntryPointer = new(FullName);
  StartOfListP = AnEntryPointer;
  fmt.Print(" 1: ");
                                        //  s0,s = AdrToHexStr(AnEntryPointer);
  fmt.Printf("First pointer value %x, %#v\n",AnEntryPointer,AnEntryPointer);

  AnEntryPointer.PrevP = nil;           // do I need to dereference all of these?
  AnEntryPointer.FirstName = "John ";   // or is this covered by "syntactic sugar"?
  AnEntryPointer.Initial = 'Q';         // Seems it is covered by "syntactic sugar"
  AnEntryPointer.LastName = " Doe";     // The explicit dereferences are not needed.
  AnEntryPointer.NextP = nil;

               /* Generate 2nd name in the list */
  PrevPlaceInListP = AnEntryPointer;
  AnEntryPointer = new(FullName);
  fmt.Print(" 2: ");
                                        //  s,s0 = AdrToHexStr(AnEntryPointer);
  fmt.Printf("%x, %#V\n",AnEntryPointer,AnEntryPointer);
  CurrentPlaceInListP = AnEntryPointer;
  (*PrevPlaceInListP).NextP = CurrentPlaceInListP;  // This explicit dereference is not needed
  (*CurrentPlaceInListP).PrevP = PrevPlaceInListP;
  (*CurrentPlaceInListP).FirstName = "Mary ";
  (*CurrentPlaceInListP).Initial = 'R';
  (*CurrentPlaceInListP).LastName = " Johnson";
  (*CurrentPlaceInListP).NextP = nil;

                 /* Add 10 more names to complete the list */
  for I=1; I<=10; I++ {
    PrevPlaceInListP = CurrentPlaceInListP;
    AnEntryPointer = new(FullName);
                                                          //  s,s0 = AdrToHexStr(AnEntryPointer);
    if (I % 3) == 0 { fmt.Println() }
    CurrentPlaceInListP = AnEntryPointer;
    PrevPlaceInListP.NextP = CurrentPlaceInListP;
    CurrentPlaceInListP.PrevP = PrevPlaceInListP;
    CurrentPlaceInListP.FirstName = "Billy ";
    CurrentPlaceInListP.Initial = byte(I+64);   // 65 is cap A
    CurrentPlaceInListP.LastName = " Franklin";
    CurrentPlaceInListP.NextP = nil;
  }; /* FOR I */
  EndofListP = CurrentPlaceInListP;

                        /* Display the list on the monitor in forward direction */
  fmt.Println(" List in forward direction.");
  CurrentPlaceInListP = StartOfListP;
  for {
    if CurrentPlaceInListP == nil { break };
                             //    s,s0 = AdrToHexStr(CurrentPlaceInListP.PrevP);
    fmt.Printf("%x: ",CurrentPlaceInListP.PrevP);
    fmt.Printf("%s %c %s: ",CurrentPlaceInListP.FirstName,CurrentPlaceInListP.Initial,CurrentPlaceInListP.LastName)
                             //    s,s0 = AdrToHexStr(CurrentPlaceInListP.NextP);
    PrevPlaceInListP = CurrentPlaceInListP;
    CurrentPlaceInListP = CurrentPlaceInListP.NextP;

                        /* Display the list on the monitor in reverse direction */
  fmt.Println(" List in reverse direction. ");
  CurrentPlaceInListP = EndofListP;
  for {
    if  CurrentPlaceInListP == nil {break}
                       //     _,s0 = AdrToHexStr(CurrentPlaceInListP.PrevP);
    fmt.Printf("%x: ",CurrentPlaceInListP.PrevP);
    fmt.Printf("%s %c %s: ",CurrentPlaceInListP.FirstName,CurrentPlaceInListP.Initial,CurrentPlaceInListP.LastName)
                       //     s,_ = AdrToHexStr(CurrentPlaceInListP.NextP);
    PrevPlaceInListP = CurrentPlaceInListP;
    CurrentPlaceInListP = CurrentPlaceInListP.PrevP;

                             /* Deallocate is unnecessary in Go */

} // LinkList

I forgot to ask a related question.

I am unable to get my above routine AdrToHexStr to compile. The main stumbling block is my inability to have a pointer be treated as an integer. So I stopped trying to get it to work in Go. For now.

You can convert a pointer to an integer via the unsafe package. But you’ll see that go makes it unpleasant to do this.

Maybe fmt.Sprintf("%p", ptr) would be better.

That solved my first issue.


On to my 2nd issue

What’s your second issue?

Seeing if I can get AdrToHexStr working. Just for my own knowledge building.

I know this is just an exercise, but a linked list is rarely the right answer. i.e. https://play.golang.org/p/Si0bnjshaR

As for AddrToHex, it’s too complicated. You can write such func ~8 lines (spoiler).

I did get it to work. My version is not as short as yours. I need some explanation regarding the bit shifting

The function works by shifting the number right by a number of bits, then extracting the rightmost remaining hex digit using bitwise ‘and’, then turning that number into a character.

& 0xF is bitwise ‘and’, that’s what’s extracting the least significant hex digit from the number.

Each hex digit represents a value from 0…15, or 4 binary bits, which is where the i*4 comes from.

A Uint64 is 64 bits, so the largest amount you need to shift right to get the leftmost hex digit is 60 bits, which is where the 60 comes from. >> is the bitwise right shift operator. The uint() is just there to satisfy Go’s type safety.

So unwrapping the key line… uint(60-i*4) gives us a number of bits we need to shift by to get successive digits of the 64 bit number, starting with the leftmost digit. 60, 56, 52, … Let’s call that n, n = uint(60-i*4).

v>>n is then the value v shifted right by i hex digits. So if you started with 0xdecafbad and i was 3, you’d end up with 0xdecaf. Then & 0xf would give you 0xd or 13. Finally, that value is used to pull the appropriate digit from a string containing the digits in order.

1 Like

Impressively compact. Looks like that logic started life in C, and was easily ported to Go.

Anyway, it works.


I have one more question.

It looks like it should produce a string of leading zeros. Why does it not do that?

It does. Or which example are you referring to?

My code does not output leading zeros, Is that because another routine is supressing them?

func AdrToHexStr(adr unsafe.Pointer) string {
const hex=“0123456789ABCDEF”;
var buf [16]byte;
L := uint64(uintptr(adr));
for i := range buf {
buf[i] = hex[L>>uint(60-i*4)&0xF]

return string(buf[:]) ;
} // AdrToHexStr This entire function can be replaced by a call to Sprintf, but nevermind that


func main() {

StartOfListP,EndofListP,CurrentPlaceInListP,PrevPlaceInListP,AnEntryPointer *FullName;
unsafeP unsafe.Pointer

StartOfListP = nil;
EndofListP = nil;
CurrentPlaceInListP = nil;
PrevPlaceInListP = nil;

              /* Generate the first name in the list */

AnEntryPointer = new(FullName);
StartOfListP = AnEntryPointer;
fmt.Print(" 1: “);
unsafeP = unsafe.Pointer(AnEntryPointer);
s := AdrToHexStr(unsafeP);
fmt.Printf(“First pointer value %p, %#v\n”,AnEntryPointer,AnEntryPointer);
fmt.Println(” First pointer value as a string:",s);

The displayed value of s does not have leading zeros.

Please format your code like this

code code code

It certainly prints leading zeroes when I paste just your AdrToHexStr into the playground.


Of course, if your actual pointer address on your platform is a 16 digit value then there will be no zeroes to lead with.

I’ve noticed that on my system, the address is 10 digits, without leading zeros

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.