Structs & Records
A struct (or record) groups named fields into one composite value, laid out as a contiguous block of memory. The fields sit side by side at fixed offsets (subject to alignment padding), so a Point{x, y} is just two ints back to back - no header, no hidden pointers. The task is the same in all seven languages: declare a Point{x, y: int}, build one, translate it by (3, 4), and print the result. Watch where the struct lives - by value on the stack (the common case), or boxed on the heap when you allocate and free it explicitly.
#include <stdio.h>
#include <stdlib.h>
struct Point { int x, y; }; /* two ints, contiguous (+ any alignment padding) */
int main(void) {
/* By value on the stack: no allocation, nothing to free. */
struct Point p = { .x = 1, .y = 2 }; /* designated initializer */
p.x += 3; /* dot accesses a field by name */
p.y += 4; /* translate by (3,4) -> (4,6) */
printf("Point(%d, %d)\n", p.x, p.y); /* Point(4, 6) */
/* Heap form: -> dereferences through a pointer; pair malloc with free. */
struct Point *hp = malloc(sizeof *hp);
if (!hp) return 1;
*hp = p; /* copy the whole struct by value */
printf("Point(%d, %d)\n", hp->x, hp->y);
free(hp); /* one malloc, one free */
return 0;
}A C struct is a plain contiguous block of fields; p.x accesses by value and hp->x through a pointer. Stack structs need no cleanup, but a heap struct is sized with sizeof *hp and the single malloc is matched by a single free. Assigning one struct to another (*hp = p) copies all fields bit-for-bit.
#include <iostream>
#include <memory>
struct Point {
int x{}, y{}; // value-initialized to 0
// A member function bundles behavior with the data.
void translate(int dx, int dy) { x += dx; y += dy; }
};
int main() {
Point p{1, 2}; // aggregate init, on the stack (RAII)
p.translate(3, 4); // (1,2) -> (4,6)
std::cout << "Point(" << p.x << ", " << p.y << ")\n";
// Heap-owned by a unique_ptr: freed automatically at scope exit.
auto hp = std::make_unique<Point>(p); // copy-construct on the heap
std::cout << "Point(" << hp->x << ", " << hp->y << ")\n";
// ~unique_ptr deletes the heap Point here; no manual delete.
}In modern C++ a struct is just a class with public defaults, so it can carry member functions like translate. Stack Points clean themselves up, and std::make_unique<Point>(p) heap-allocates a copy whose destructor runs exactly once (RAII) - no new/delete to balance by hand.
// Top-level code runs in TempleOS -- no main() needed.
class Point { // HolyC 'class' == C struct (no methods)
I64 x, y;
};
Point p; // by value on the stack/locals
p.x = 1;
p.y = 2;
p.x += 3; // translate by (3,4) -> (4,6)
p.y += 4;
Print("Point(%d, %d)\n", p.x, p.y); // Point(4, 6)
// Heap form: MAlloc a Point, access through -> , then Free.
Point *hp = MAlloc(sizeof(Point)); // 16 bytes from this task's heap
*hp = p; // copy all fields
Print("Point(%d, %d)\n", hp->x, hp->y);
Free(hp); // return it; Free(NULL) is a safe no-op
HolyC spells a record as class (it has no methods - it is structurally a C struct of two I64s, 16 bytes). Locals access fields with ., heap pointers with ->. MAlloc carves the struct from the per-task data heap and Free returns it; with no memory protection in ring-0 TempleOS, a stray write or double-free can corrupt the system.
const std = @import("std");
const Point = struct {
x: i32,
y: i32,
// Methods live in the struct; the first param is the receiver.
fn translate(self: *Point, dx: i32, dy: i32) void {
self.x += dx;
self.y += dy;
}
};
pub fn main() !void {
var p = Point{ .x = 1, .y = 2 }; // by value, on the stack
p.translate(3, 4); // (1,2) -> (4,6)
std.debug.print("Point({d}, {d})\n", .{ p.x, p.y });
// Heap form: the allocator is explicit -- no hidden allocations.
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit(); // reports leaks on exit
const allocator = gpa.allocator();
const hp = try allocator.create(Point); // *Point, may fail -> try
defer allocator.destroy(hp); // paired free at scope exit
hp.* = p; // copy the struct value
std.debug.print("Point({d}, {d})\n", .{ hp.x, hp.y });
}A Zig struct is a value type holding its fields inline; methods declared inside take self explicitly. Stack Points need no allocator, while a heap one uses allocator.create(Point) (returns !*Point, hence try) paired with defer allocator.destroy(hp) so the free always runs - and the GeneralPurposeAllocator flags any leak.
use fmt;
type Point = struct {
x: int,
y: int,
};
export fn main() void = {
// By value, on the stack -- no alloc, nothing to free.
let p = Point { x = 1, y = 2 };
p.x += 3; // translate by (3,4) -> (4,6)
p.y += 4;
fmt::printfln("Point({}, {})", p.x, p.y)!;
// Heap form: alloc returns *Point; defer the matching free.
let hp: *Point = alloc(p); // copy p onto the heap
defer free(hp); // release at scope exit
fmt::printfln("Point({}, {})", hp.x, hp.y)!;
};Hare declares a record with type Point = struct { ... } and accesses fields with . for both values and pointers (it auto-derefs). Stack structs are free of cleanup; alloc(p) copies the struct onto the heap and yields *Point, with defer free(hp) guaranteeing the single alloc is matched by a single free (Hare has no GC).
package main
import "core:fmt"
Point :: struct {
x, y: int,
}
main :: proc() {
// By value, on the stack.
p := Point{1, 2}
p.x += 3 // translate by (3,4) -> (4,6)
p.y += 4
fmt.println(p) // Point{x = 4, y = 6}
// Heap form: new(Point) uses the implicit context.allocator; returns ^Point.
hp := new(Point)
defer free(hp) // freed via the same context allocator at scope end
hp^ = p // copy the struct through the pointer
fmt.printf("Point(%d, %d)\n", hp.x, hp.y)
}Odin defines a record with Point :: struct { ... } and accesses fields with . (pointers auto-deref, so hp.x works). new(Point) allocates through the implicit context.allocator and returns ^Point; free(hp), scheduled with defer, returns it to that same allocator - swap in an arena and you could drop the per-object free entirely.
\ Classic Forth has no struct type: a record is just a block of memory,
\ and you name the field OFFSETS yourself (here, 2 cells = x then y).
0 CELLS CONSTANT P.X \ field x lives at offset 0
1 CELLS CONSTANT P.Y \ field y lives at offset 1 cell
2 CELLS CONSTANT /POINT \ total size of a Point ( '/' = size-of idiom )
CREATE p /POINT ALLOT \ reserve one Point's worth of cells (static)
1 p P.X + ! \ p.x = 1 ( addr = base + offset, then store )
2 p P.Y + ! \ p.y = 2
3 p P.X + +! \ p.x += 3 ( +! adds in place )
4 p P.Y + +! \ p.y += 4 -> (4,6)
." Point(" p P.X + @ . ( fetch x, print )
." , " p P.Y + @ . ( fetch y, print )
." )" CRForth has no record type, so the closest idiom is a hand-laid memory block with named field offsets (P.X, P.Y) added to a base address: base offset + ! stores and base offset + @ fetches. CREATE ... ALLOT reserves the bytes statically in the dictionary (never freed); the optional ALLOCATE/FREE word set would heap-allocate /POINT bytes the same way you'd malloc a struct in C.