summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorDave Gauer <dave@ratfactor.com>2021-02-08 20:35:28 -0500
committerDave Gauer <dave@ratfactor.com>2021-02-08 20:35:28 -0500
commitcf0920de31e9b5f3c5ba6de19a1b4c8d0c58b907 (patch)
treed0c1af19e12e503a6d720b8e8ef487966e4c7d75
parentadf5ddb27df7f5a22b0b7d3321dfc8bca1e7937a (diff)
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!
-rw-r--r--38_structs2.zig2
-rw-r--r--39_pointers.zig36
-rw-r--r--40_pointers2.zig27
-rw-r--r--41_pointers3.zig41
-rw-r--r--42_pointers4.zig33
-rw-r--r--43_pointers5.zig84
-rw-r--r--README.md34
-rwxr-xr-xziglings7
8 files changed, 252 insertions, 12 deletions
diff --git a/38_structs2.zig b/38_structs2.zig
index 9a67c25..b6def93 100644
--- a/38_structs2.zig
+++ b/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", .{});
}
diff --git a/39_pointers.zig b/39_pointers.zig
new file mode 100644
index 0000000..25b56c6
--- /dev/null
+++ b/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});
+}
diff --git a/40_pointers2.zig b/40_pointers2.zig
new file mode 100644
index 0000000..b046dc1
--- /dev/null
+++ b/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.*});
+}
diff --git a/41_pointers3.zig b/41_pointers3.zig
new file mode 100644
index 0000000..21a43bd
--- /dev/null
+++ b/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});
+}
diff --git a/42_pointers4.zig b/42_pointers4.zig
new file mode 100644
index 0000000..e6b8964
--- /dev/null
+++ b/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!
+}
diff --git a/43_pointers5.zig b/43_pointers5.zig
new file mode 100644
index 0000000..adfaea1
--- /dev/null
+++ b/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,
+ });
+}
diff --git a/README.md b/README.md
index 61d02fb..880b385 100644
--- a/README.md
+++ b/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
diff --git a/ziglings b/ziglings
index b328b81..402d867 100755
--- a/ziglings
+++ b/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 " __ __ _ "