Просмотр исходного кода

Added Ex. 38-43 for pointers, updated README

Added topics beyond the language basics from ziglearn.org
to the README. That's a lot of exercises. I'd like to keep
it under 100, though!
Dave Gauer 4 лет назад
Родитель
Сommit
cf0920de31
8 измененных файлов с 252 добавлено и 12 удалено
  1. 0 2
      38_structs2.zig
  2. 36 0
      39_pointers.zig
  3. 27 0
      40_pointers2.zig
  4. 41 0
      41_pointers3.zig
  5. 33 0
      42_pointers4.zig
  6. 84 0
      43_pointers5.zig
  7. 25 9
      README.md
  8. 6 1
      ziglings

+ 0 - 2
38_structs2.zig

@@ -48,6 +48,4 @@ pub fn main() void {
         std.debug.print("Character {} - G:{} H:{} XP:{}\n",
                         .{num+1, c.gold, c.health, c.experience});
     }
-
-    std.debug.print("\n", .{});
 }

+ 36 - 0
39_pointers.zig

@@ -0,0 +1,36 @@
+//
+// Check this out:
+//
+//     var foo: u8 = 5;      // foo is 5
+//     var bar: *u8 = &foo;  // bar is a pointer
+// 
+// What is a pointer? It's a reference to a value. In this example
+// bar is a reference to the memory space that current contains the
+// value 5.
+//
+// A cheatsheet given the above declarations:
+//
+//     u8         the type of a u8 value
+//     foo        the value 5
+//     *u8        the type of a pointer to a u8 value
+//     &foo       a reference to foo
+//     bar        a pointer to the value at foo
+//     bar.*      the value 5 (the dereferenced value "at" bar)
+//     
+// We'll see why pointers are useful in a moment. For now, see if you
+// can make this example work!
+//
+const std = @import("std");
+
+pub fn main() void {
+    var num1: u8 = 5;
+    var num1_pointer: *u8 = &num1;
+
+    var num2: u8 = undefined;
+
+    // Please make num2 equal 5 using num1_pointer!
+    // (See the "cheatsheet" above for ideas.)
+    num2 = ???;
+
+    std.debug.print("num1: {}, num2: {}\n", .{num1, num2});
+}

+ 27 - 0
40_pointers2.zig

@@ -0,0 +1,27 @@
+//
+// It's important to note that variable pointers and constant pointers
+// are different types.
+//
+// Given:
+//
+//     var foo: u8 = 5;
+//     const bar: u8 = 5;
+// 
+// Then:
+//
+//     &foo is of type "*u8"
+//     &bar is of type "*const u8"
+//
+// You can always make a constant pointer to a variable, but you cannot
+// make a variable pointer to a constant. This sounds like a logic puzzle,
+// but it just means that once data is declared immutable, you can't
+// coerce it to a mutable type. It's a safety thing (to prevent mistakes).
+//
+const std = @import("std");
+
+pub fn main() void {
+    const a: u8 = 12;
+    const b: *u8 = &a; // fix this!
+
+    std.debug.print("a: {}, b: {}\n", .{a, b.*});
+}

+ 41 - 0
41_pointers3.zig

@@ -0,0 +1,41 @@
+//
+// The tricky part is that the pointer's mutability (var vs const) refers
+// to the ability to change what the pointer POINTS TO, not the ability
+// to change the VALUE at that location!
+//
+//     const locked: u8 = 5;
+//     var unlocked: u8 = 10;
+//
+//     const p1: *const u8 = &locked;
+//     var   p2: *const u8 = &locked;
+//
+// Both p1 and p2 point to constant values which cannot change. However,
+// p2 can be changed to point to something else and p1 cannot!
+//
+//     const p3: *u8 = &unlocked;
+//     var   p4: *u8 = &unlocked;
+//     const p5: *const u8 = &unlocked;
+//     var   p6: *const u8 = &unlocked;
+//      
+// Here p3 and p4 can both be used to change the value they point to but
+// p3 cannot point at anything else.
+// What's interesting is that p5 and p6 act like p1 and p2, but point to
+// the value at "unlocked". This is what we mean when we say that we can
+// make a constant reference to any value!
+//
+const std = @import("std");
+
+pub fn main() void {
+    var foo: u8 = 5;
+    var bar: u8 = 10;
+
+    // Please define pointer "p" so that it can point to EITHER foo or
+    // bar AND change the value it points to!
+    ??? p: ??? = undefined;
+    
+    p = &foo;
+    p.* += 1;
+    p = &bar;
+    p.* += 1;
+    std.debug.print("foo={}, bar={}\n", .{foo, bar});
+}

+ 33 - 0
42_pointers4.zig

@@ -0,0 +1,33 @@
+//
+// Now let's use pointers to do something we haven't been
+// able to do before: pass a value by reference to a function!
+//
+const std = @import("std");
+
+pub fn main() void {
+    var num: u8 = 1;
+    var more_nums = [_]u8{ 1, 1, 1, 1 };
+
+    // Let's pass a reference to num to our function and print it:
+    makeFive(&num);
+    std.debug.print("num: {}, ", .{num});
+
+
+    // Now something interesting. Let's pass a reference to a
+    // specific array value:
+    makeFive(&more_nums[2]);
+
+    // And print the array:
+    std.debug.print("more_nums: ", .{});
+    for (more_nums) |n| {
+        std.debug.print("{} ", .{n});
+    }
+
+    std.debug.print("\n", .{});
+}
+
+// This function should take a reference to a u8 value and set it
+// to 5.
+fn makeFive(x: *u8) void {
+    ??? = 5; // fix me!
+}

+ 84 - 0
43_pointers5.zig

@@ -0,0 +1,84 @@
+//
+// Passing integer pointers around is generally not something you're going
+// to do. Integers are cheap to copy.
+//
+// But you know what IS useful? Pointers to structs:
+//
+//     const Vertex = struct{ x: u32, y: u32, z: u32 };
+//
+//     var v1 = Vertex{ .x=3, .y=2, .z=5 };
+//
+//     var pv: *Vertex = &v1;   // <-- a pointer to our struct
+//
+// Note that you don't need to dereference the "pv" pointer to access
+// the struct's fields:
+//
+//     YES: pv.x
+//     NO:  pv.*.x
+//
+// We can write functions that take pointer arguments:
+//
+//     fn foo(v: *Vertex) void {
+//         v.x += 2;
+//         v.y += 3;
+//         v.z += 7;
+//     }
+//
+// And pass references to them:
+//
+//     foo(&v1);
+//
+//
+// Let's revisit our RPG example and make a printCharacter() function
+// that takes a Character pointer.
+//
+const std = @import("std");
+
+const Class = enum{
+    wizard,
+    thief,
+    bard,
+    warrior,
+};
+
+const Character = struct{
+    class: Class,
+    gold: u32,
+    health: u8,
+    experience: u32,
+};
+
+pub fn main() void {
+    var glorp = Character{
+        .class      = Class.wizard,
+        .gold       = 10,
+        .health     = 100,
+        .experience = 20,
+    };
+
+    // FIX ME!
+    // Please pass our Character "glorp" to printCharacter():
+    printCharacter( ??? );
+}
+
+
+// Note how this function's "c" parameter is a pointer to a Character struct.
+fn printCharacter(c: *Character) void {
+
+    // Here's something you haven't seen before: when switching an enum, you
+    // don't have to write the full enum name. Zig understands that ".wizard"
+    // means "Class.wizard" when we switch on a Class enum value:
+    const class_name = switch (c.class) {
+        .wizard  => "Wizard",
+        .thief   => "Thief",
+        .bard    => "Bard",
+        .warrior => "Warrior",
+    };
+
+    std.debug.print("{s} (G:{} H:{} XP:{})", .{
+        class_name,
+        c.gold,
+        c.health,
+        c.experience,
+    });
+}

+ 25 - 9
README.md

@@ -84,24 +84,40 @@ Planned exercises:
 * [x] Switch
 * [x] Unreachable
 * [x] Enums
-* [ ] Structs
-* [ ] Unions
-* [ ] Pointers
-* [ ] Pointer sized integers
+* [x] Structs
+* [x] Pointers
 * [ ] Multi pointers
 * [ ] Slices
-* [ ] Integer rules
-* [ ] Floats
-* [ ] Labelled blocks
-* [ ] Labelled loops
+* [ ] Unions
+* [ ] Numeric types (integers, floats)
+* [ ] Labelled blocks and loops
 * [ ] Loops as expressions
 * [ ] Optionals
 * [ ] Comptime
-* [ ] Inline loops
+* [ ] Inline loops (how to DEMO this?)
 * [ ] Anonymous structs
 * [ ] Sentinel termination
 * [ ] Vectors
 * [ ] Imports
+* [ ] Allocators
+* [ ] Arraylist
+* [ ] Filesystem
+* [ ] Readers and Writers
+* [ ] Formatting
+* [ ] JSON
+* [ ] Random Numbers
+* [ ] Crypto
+* [ ] Threads
+* [ ] Hash Maps
+* [ ] Stacks
+* [ ] Sorting
+* [ ] Iterators
+* [ ] Formatting specifiers
+* [ ] Advanced Formatting
+* [ ] Suspend / Resume
+* [ ] Async / Await
+* [ ] Nosuspend
+* [ ] Async Frames, Suspend Blocks
 
 The initial topics for these exercises were unabashedly cribbed from
 [ziglearn.org](https://ziglearn.org/). I've since moved things around

+ 6 - 1
ziglings

@@ -70,7 +70,7 @@ function check_it {
 
 # I've chosen to explicitly number AND list each exercise rather than rely
 # on sorting. Though it does mean manually renaming things to remove/insert,
-# it's worked out well so far because its explicit and foolproof.
+# it's worked out well so far.
 
 check_it 01_hello.zig "Hello world" "Note the error: the source file has a hint for fixing 'main'."
 check_it 02_std.zig "Standard Library"
@@ -110,6 +110,11 @@ check_it 35_enums.zig "1 2 3 9 8 7" "This problem seems familiar..."
 check_it 36_enums2.zig "#0000ff" "I'm feeling blue about this."
 check_it 37_structs.zig "Your wizard has 90 health and 25 gold."
 check_it 38_structs2.zig "Character 2 - G:10 H:100 XP:20"
+check_it 39_pointers.zig "num1: 5, num2: 5" "Pointers aren't so bad."
+check_it 40_pointers2.zig "a: 12, b: 12"
+check_it 41_pointers3.zig "foo=6, bar=11"
+check_it 42_pointers4.zig "num: 5, more_nums: 1 1 5 1"
+check_it 43_pointers5.zig "Wizard (G:10 H:100 XP:20)"
 
 echo
 echo "    __   __          _ "