Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. Zig Dictionary
  3. Sentinel Pointer ([*:0]u8)

Sentinel Pointer ([*:0]u8)

In Zig, the sentinel pointer [*:0]u8 lets you work with data compatible with C's null-terminated strings (char *). Unlike a regular slice, it does not store a separate length — instead, it marks the end of the string by placing a sentinel value (a terminator marker) at the end. This is required when interoperating with C (FFI) or calling system APIs such as std.os.

Syntax

// -----------------------------------------------
// [*:S]T — sentinel pointer type
// -----------------------------------------------

// S is the sentinel value (terminator). T is the element type.
// The most common form is [*:0]u8 (null-terminated u8 pointer).
const ptr: [*:0]u8 = ...;

// -----------------------------------------------
// Getting a sentinel pointer from a string literal
// -----------------------------------------------

// The type of a string literal is *const [N:0]u8 (sentinel-terminated array pointer).
// It can be implicitly cast to [*:0]const u8.
const name: [*:0]const u8 = "Goku";

// -----------------------------------------------
// std.mem.span() — converting a sentinel pointer to a slice
// -----------------------------------------------

const std = @import("std");
const slice: []const u8 = std.mem.span(ptr);
// std.mem.span() scans for the sentinel value and returns a []const u8 slice.

// -----------------------------------------------
// Passing to a C function (extern declaration example)
// -----------------------------------------------

// Example declaration equivalent to C's puts()
extern fn puts(s: [*:0]const u8) c_int;
// _ = puts("Vegeta");  // You can pass a sentinel pointer directly to C's puts().

Syntax reference

Syntax / keywordDescription
[*:0]u8A null-terminated (ends with 0) u8 sentinel pointer type. Equivalent to C's char *.
[*:0]const u8A read-only null-terminated sentinel pointer type. Equivalent to C's const char *.
[*:S]TA generic sentinel pointer type with sentinel value S and element type T.
"string literal"Has type *const [N:0]u8, which is implicitly castable to [*:0]const u8.
std.mem.span(ptr)Converts a sentinel pointer to a []const u8 slice. The length is determined by scanning for the sentinel.
std.mem.len(ptr)Returns the length (in bytes) of a sentinel pointer string, counting up to the null terminator.
ptr[i]Accesses a sentinel pointer by index. No bounds checking is performed.
extern fn f(s: [*:0]const u8)How to declare a C function that takes a null-terminated string argument in Zig.

Sample code

dragonball_sentinel.zig
// dragonball_sentinel.zig — sample demonstrating sentinel pointer [*:0]u8
// Uses Dragon Ball character names to show how to obtain sentinel pointers,
// convert them to slices, get their lengths, and access them by index.

// Import the standard library.
const std = @import("std");

// -----------------------------------------------
// Helper function that receives a null-terminated string and prints its details.
// Takes [*:0]const u8 as the argument, equivalent to C's char *.
// -----------------------------------------------
fn printCStr(label: []const u8, ptr: [*:0]const u8) !void {
    const stdout = std.io.getStdOut().writer();

    // Use std.mem.span() to convert the sentinel pointer to a []const u8 slice.
    // Once it is a slice, it can be printed with the {s} format specifier.
    const slice: []const u8 = std.mem.span(ptr);

    // Use std.mem.len() to get the number of bytes up to the null terminator.
    const len: usize = std.mem.len(ptr);

    try stdout.print("  {s}: \"{s}\" ({d} bytes)\n", .{ label, slice, len });
}

pub fn main() !void {
    // Get a Writer for standard output.
    const stdout = std.io.getStdOut().writer();

    try stdout.print("=== Dragon Ball Sentinel Pointer Demo ===\n\n", .{});

    // -----------------------------------------------
    // String literals have type *const [N:0]u8.
    // They can be implicitly cast to [*:0]const u8 and stored in a variable.
    // -----------------------------------------------

    // Declare each character name as [*:0]const u8.
    const goku: [*:0]const u8 = "悟空";
    const vegeta: [*:0]const u8 = "ベジータ";
    const piccolo: [*:0]const u8 = "ピッコロ";
    const trunks: [*:0]const u8 = "トランクス";
    const frieza: [*:0]const u8 = "フリーザ";

    try stdout.print("--- Character names and lengths ---\n", .{});

    // Print the sentinel pointer info for each name using the helper function.
    try printCStr("Goku", goku);
    try printCStr("Vegeta", vegeta);
    try printCStr("Piccolo", piccolo);
    try printCStr("Trunks", trunks);
    try printCStr("Frieza", frieza);

    // -----------------------------------------------
    // Index access — use ptr[i] to read individual bytes.
    // Japanese characters are multi-byte in UTF-8.
    // -----------------------------------------------

    try stdout.print("\n--- Index access (first bytes of Goku) ---\n", .{});

    // goku[0] is the u8 value of the first UTF-8 byte.
    try stdout.print("  goku[0] = 0x{x}\n", .{goku[0]});
    try stdout.print("  goku[1] = 0x{x}\n", .{goku[1]});
    try stdout.print("  goku[2] = 0x{x}\n", .{goku[2]});

    // -----------------------------------------------
    // Converting to slices with std.mem.span() for comparison.
    // Slices carry length information, so std.mem.eql() can compare contents.
    // -----------------------------------------------

    try stdout.print("\n--- Slice conversion and comparison ---\n", .{});

    const goku_slice: []const u8 = std.mem.span(goku);
    const frieza_slice: []const u8 = std.mem.span(frieza);

    // Compare the contents of two slices with std.mem.eql().
    const is_same = std.mem.eql(u8, goku_slice, frieza_slice);
    try stdout.print("  Goku == Frieza : {}\n", .{is_same});

    const goku2: [*:0]const u8 = "悟空";
    const goku2_slice: []const u8 = std.mem.span(goku2);
    const is_goku = std.mem.eql(u8, goku_slice, goku2_slice);
    try stdout.print("  Goku == Goku   : {}\n", .{is_goku});

    // -----------------------------------------------
    // Example of a mutable [*:0]u8.
    // Prepare a var array as a buffer and obtain a sentinel pointer to it.
    // -----------------------------------------------

    try stdout.print("\n--- Mutable sentinel pointer ---\n", .{});

    // Declare a mutable null-terminated buffer ([N:0]u8 type).
    var name_buf: [32:0]u8 = undefined;

    // Copy Frieza's name into the buffer as the initial value.
    @memset(&name_buf, 0);                          // Zero-fill the buffer.
    const frieza_bytes = std.mem.span(frieza);
    std.mem.copyForwards(u8, &name_buf, frieza_bytes);

    // Get a mutable sentinel pointer to the start of the buffer.
    const mutable_ptr: [*:0]u8 = &name_buf;
    const mutable_slice: []const u8 = std.mem.span(mutable_ptr);
    try stdout.print("  Before: \"{s}\"\n", .{mutable_slice});

    // Overwrite the buffer contents with Vegeta's name.
    @memset(&name_buf, 0);
    const vegeta_bytes = std.mem.span(vegeta);
    std.mem.copyForwards(u8, &name_buf, vegeta_bytes);

    const updated_slice: []const u8 = std.mem.span(mutable_ptr);
    try stdout.print("  After:  \"{s}\"\n", .{updated_slice});

    try stdout.print("\nDone.\n", .{});
}
zig run dragonball_sentinel.zig
=== Dragon Ball Sentinel Pointer Demo ===

--- Character names and lengths ---
  Goku: "悟空" (6 bytes)
  Vegeta: "ベジータ" (12 bytes)
  Piccolo: "ピッコロ" (12 bytes)
  Trunks: "トランクス" (15 bytes)
  Frieza: "フリーザ" (12 bytes)

--- Index access (first bytes of Goku) ---
  goku[0] = 0xe6
  goku[1] = 0x82
  goku[2] = 0x9f

--- Slice conversion and comparison ---
  Goku == Frieza : false
  Goku == Goku   : true

--- Mutable sentinel pointer ---
  Before: "フリーザ"
  After:  "ベジータ"

Done.

Overview

The sentinel pointer [*:0]u8 is a pointer type that marks the end of data by placing a specific sentinel value at the end of an array. When 0 is the sentinel value, it becomes a null-terminated string, which has the same meaning as C's char * / const char *. The actual type of a string literal is *const [N:0]u8 (a sentinel-terminated array pointer), but it can be implicitly cast to [*:0]const u8, so it can be passed directly as an argument to C functions. When you need the length, use std.mem.len() to get the byte count up to the sentinel, or use std.mem.span() to convert it to a []const u8 slice. Slices carry length information, which makes them easier to use with standard functions like std.mem.eql(). The common Zig style is to use slices ([]u8) for general string handling and to use sentinel pointers only where C interoperability is needed. For more on slices, see Slice. For basic pointer operations, see Pointer *T.

If you find any errors or copyright issues, please .