051_values.zig 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. //
  2. // If you thought the last exercise was a deep dive, hold onto your
  3. // hat because we are about to descend into the computer's molten
  4. // core.
  5. //
  6. // (Shouting) DOWN HERE, THE BITS AND BYTES FLOW FROM RAM TO THE CPU
  7. // LIKE A HOT, DENSE FLUID. THE FORCES ARE INCREDIBLE. BUT HOW DOES
  8. // ALL OF THIS RELATE TO THE DATA IN OUR ZIG PROGRAMS? LET'S HEAD
  9. // BACK UP TO THE TEXT EDITOR AND FIND OUT.
  10. //
  11. // Ah, that's better. Now we can look at some familiar Zig code.
  12. //
  13. // @import() adds the imported code to your own. In this case, code
  14. // from the standard library is added to your program and compiled
  15. // with it. All of this will be loaded into RAM when it runs. Oh, and
  16. // that thing we name "const std"? That's a struct!
  17. //
  18. const std = @import("std");
  19. // Remember our old RPG Character struct? A struct is really just a
  20. // very convenient way to deal with memory. These fields (gold,
  21. // health, experience) are all values of a particular size. Add them
  22. // together and you have the size of the struct as a whole.
  23. const Character = struct {
  24. gold: u32 = 0,
  25. health: u8 = 100,
  26. experience: u32 = 0,
  27. };
  28. // Here we create a character called "the_narrator" that is a constant
  29. // (immutable) instance of a Character struct. It is stored in your
  30. // program as data, and like the instruction code, it is loaded into
  31. // RAM when your program runs. The relative location of this data in
  32. // memory is hard-coded and neither the address nor the value changes.
  33. const the_narrator = Character{
  34. .gold = 12,
  35. .health = 99,
  36. .experience = 9000,
  37. };
  38. // This "global_wizard" character is very similar. The address for
  39. // this data won't change, but the data itself can since this is a var
  40. // and not a const.
  41. var global_wizard = Character{};
  42. // A function is instruction code at a particular address. Function
  43. // parameters in Zig are always immutable. They are stored in "the
  44. // stack". A stack is a type of data structure and "the stack" is a
  45. // specific bit of RAM reserved for your program. The CPU has special
  46. // support for adding and removing things from "the stack", so it is
  47. // an extremely efficient place for memory storage.
  48. //
  49. // Also, when a function executes, the input arguments are often
  50. // loaded into the beating heart of the CPU itself in registers.
  51. //
  52. // Our main() function here has no input parameters, but it will have
  53. // a stack entry (called a "frame").
  54. pub fn main() void {
  55. // Here, the "glorp" character will be allocated on the stack
  56. // because each instance of glorp is mutable and therefore unique
  57. // to the invocation of this function.
  58. var glorp = Character{
  59. .gold = 30,
  60. };
  61. // However, this "skull_farmer" character will be put in the
  62. // global immutable data even though it's defined in a function.
  63. // Since it's immutable, all invocations of the function can share
  64. // this one value.
  65. const skull_farmer = Character{};
  66. // The "reward_xp" value is interesting. It's a constant value, so
  67. // it could go with other global data. But being such a small
  68. // value, it may also simply be inlined as a literal value in your
  69. // instruction code where it is used. It's up to the compiler.
  70. const reward_xp: u32 = 200;
  71. // Now let's circle back around to that "std" struct we imported
  72. // at the top. Since it's just a regular Zig value once it's
  73. // imported, we can also assign new names for its fields and
  74. // declarations. "debug" refers to another struct and "print" is a
  75. // public function namespaced within THAT struct.
  76. //
  77. // Let's assign the std.debug.print function to a const named
  78. // "print" so that we can use this new name later!
  79. const print = ???;
  80. // Now let's look at assigning and pointing to values in Zig.
  81. //
  82. // We'll try three different ways of making a new name to access
  83. // our glorp Character and change one of its values.
  84. //
  85. // "glorp_access1" is incorrectly named! We asked Zig to set aside
  86. // memory for another Character struct. So when we assign glorp to
  87. // glorp_access1 here, we're actually assigning all of the fields
  88. // to make a copy! Now we have two separate characters.
  89. //
  90. // You don't need to fix this. But notice what gets printed in
  91. // your program's output for this one compared to the other two
  92. // assignments below!
  93. var glorp_access1: Character = glorp;
  94. glorp_access1.gold = 111;
  95. print("1:{}!. ", .{glorp.gold == glorp_access1.gold});
  96. // NOTE:
  97. //
  98. // If we tried to do this with a const Character instead of a
  99. // var, changing the gold field would give us a compiler error
  100. // because const values are immutable!
  101. //
  102. // "glorp_access2" will do what we want. It points to the original
  103. // glorp's address. Also remember that we get one implicit
  104. // dereference with struct fields, so accessing the "gold" field
  105. // from glorp_access2 looks just like accessing it from glorp
  106. // itself.
  107. var glorp_access2: *Character = &glorp;
  108. glorp_access2.gold = 222;
  109. print("2:{}!. ", .{glorp.gold == glorp_access2.gold});
  110. // "glorp_access3" is interesting. It's also a pointer, but it's a
  111. // const. Won't that disallow changing the gold value? No! As you
  112. // may recall from our earlier pointer experiments, a constant
  113. // pointer can't change what it's POINTING AT, but the value at
  114. // the address it points to is still mutable! So we CAN change it.
  115. const glorp_access3: *Character = &glorp;
  116. glorp_access3.gold = 333;
  117. print("3:{}!. ", .{glorp.gold == glorp_access3.gold});
  118. // NOTE:
  119. //
  120. // If we tried to do this with a *const Character pointer,
  121. // that would NOT work and we would get a compiler error
  122. // because the VALUE becomes immutable!
  123. //
  124. // Moving along...
  125. //
  126. // Passing arguments to functions is pretty much exactly like
  127. // making an assignment to a const (since Zig enforces that ALL
  128. // function parameters are const).
  129. //
  130. // Knowing this, see if you can make levelUp() work as expected -
  131. // it should add the specified amount to the supplied character's
  132. // experience points.
  133. //
  134. print("XP before:{}, ", .{glorp.experience});
  135. // Fix 1 of 2 goes here:
  136. levelUp(glorp, reward_xp);
  137. print("after:{}.\n", .{glorp.experience});
  138. }
  139. // Fix 2 of 2 goes here:
  140. fn levelUp(character_access: Character, xp: u32) void {
  141. character_access.experience += xp;
  142. }
  143. // And there's more!
  144. //
  145. // Data segments (allocated at compile time) and "the stack"
  146. // (allocated at run time) aren't the only places where program data
  147. // can be stored in memory. They're just the most efficient. Sometimes
  148. // we don't know how much memory our program will need until the
  149. // program is running. Also, there is a limit to the size of stack
  150. // memory allotted to programs (often set by your operating system).
  151. // For these occasions, we have "the heap".
  152. //
  153. // You can use as much heap memory as you like (within physical
  154. // limitations, of course), but it's much less efficient to manage
  155. // because there is no built-in CPU support for adding and removing
  156. // items as we have with the stack. Also, depending on the type of
  157. // allocation, your program MAY have to do expensive work to manage
  158. // the use of heap memory. We'll learn about heap allocators later.
  159. //
  160. // Whew! This has been a lot of information. You'll be pleased to know
  161. // that the next exercise gets us back to learning Zig language
  162. // features we can use right away to do more things!