Memory: Address

Pointers

  • A pointer  is an abstraction of an address , a numberic value representing the location of an object in memory. That object is said to be pointed to by the pointer. To obtain the address of a pointer, cast it to uintptr .

  • When an object's values are read through a pointer, that operation is called a load  operation. When memory is written to through a pointer, that operation is called a store  operation. Both of these operations can be called a memory access operation .

Implementation
  • Symbol ^ .

  • No "pointer arithmetic".

  • The zero value of a pointer: nil .

Multi-pointer
  • A multi-pointer is a pointer that points to multiple objects. Unlike a pointer, a multi-pointer can be indexed, but does not have a definite length.

Slice
  • A slice is a pointer that points to multiple objects equipped with the length, specifying the amount of objects a slice points to.

Implicit Dereference
  • Pointer to a struct :

    v := Vector2{1, 2}
    p := &v
    p.x = 1335
    fmt.println(v)
    
    • We could write p^.x , however, it is nice not to have to explicitly dereference the pointer.

    • This is very useful when refactoring code to use a pointer rather than a value, and vice versa.

  • Pointer to an array :

    ptr_to_array[index] == ptr_to_array^[index]
    
Implicit pointer to the stack
a := &My_Struct{}

// Is equivalent to

_a := My_Struct{} // not actually named, just for the example's sake
a := &_a

Zero by default

  • Whenever new memory is allocated, via an allocator, or on the stack, by default Odin will zero-initialize that memory, even if it wasn't explicitly initialized. This allows for some convenience in certain scenarios and ease of debugging, which will not be described in detail here.

  • However zero-initialization can be a cause of slowdowns, when allocating large buffers. For this reason, allocators have *_non_zeroed  modes of allocation that allow the user to request for uninitialized memory and will avoid a relatively expensive zero-filling of the buffer.

Alignment

  • align_of(typeid) -> int .

    • This is evaluated at compile-time .

    • Takes an expression or type, and returns the alignment in bytes of the type of the expression if it was hypothetically instantiated as a variable v .

      • I guess this means

    • It is the largest value m  such that the address of v  is always 0 mod m .

      • All this effectively means "the address of v  is always a multiple of m "; so uintptr(&t) % align_of(t) == 0 .

      • This also implies size_of(T) % align_of(T) == 0 , which means that "the size is a multiple of the alignment".

      • Notation a ≡ r (mod m) :

        • ≡ (mod m)  means equality up to a multiple of m .

        • Is read as: a  is congruent to r modulo m .

          • Two numbers are congruent modulo m  if they give the same remainder  when divided by m .

        • Formally, it means: m  divides (a - r) .

        • Or equivalently: a − r = k⋅m  for some integer  k .

        • Ex :

          • 17 ≡ 5 (mod 12)

            • 17−5=12 , which is a multiple of 12 .

          • a ≡ 0 (mod m)

            • a − 0 = a , so a  must be divisible by m .

  • reflect.align_of_typeid(typeid) -> int .

    • This is evaluated at runtime .

    • Returns the alignment of the type that the passed typeid represents.