Skip to main content
C△ inherits all C11 primitive types and adds several modern first-class types designed for safe, expressive systems programming. You can use every C11 type exactly as you would in standard C, and layer in C△‘s new types wherever they simplify your code. This page covers every type you can reach for in C△, with examples drawn directly from the language.

Primitive Types

The following types are inherited unchanged from C11. Their sizes, signedness, and semantics are identical to what you already know.
TypeSizeDescription
int4 bytesSigned 32-bit integer
unsigned int4 bytesUnsigned 32-bit integer
short2 bytesSigned 16-bit integer
long8 bytesSigned 64-bit integer
char1 byteSingle byte / ASCII character
float4 bytesSingle-precision floating-point
double8 bytesDouble-precision floating-point
voidNo type / no return value

Pointers

C△ supports standard C-style pointers with the same * and & syntax you already know from C11. No new syntax is introduced.
// Pointer declarations
int*   ptr;     // Pointer to int
int**  ptr2;    // Pointer to pointer to int
char*  str;     // Pointer to char (C-string)
void*  vptr;    // Generic (untyped) pointer
// Address-of and dereference
int x    = 42;
int* ptr = &x;   // & yields the address of x
int y    = *ptr; // * dereferences ptr — y is now 42
C△‘s autoremove allocate feature is designed to manage heap-allocated pointer lifetimes automatically. It is not yet implemented in the current compiler — see Compiler Status.

string — First-Class String

string is a built-in, automatically memory-managed type for text. Unlike char*, you do not manage the underlying buffer yourself — the compiler handles allocation and deallocation.
string greeting = "Hello, world!";
string name     = "Hypotenuse";

// Concatenate with the + operator
string full = greeting + " " + name;

// Equality and inequality — compile to strcmp calls under the hood
if (greeting == "Hello, world!") { /* true */ }
if (name != "Other") { /* true */ }
Use string instead of char* for any text that doesn’t need to cross a C ABI boundary. Reserve char* for interop with external C libraries.

auto — Type Inference

Declare a variable with auto and the compiler infers its type from the initializer. The type is resolved during the simulation pass and is fully type-aware at runtime — auto does not mean “untyped”.
auto x = 42;       // inferred as int
auto s = "hello";  // inferred as string
auto f = 3.14;     // inferred as double
You can also pack multiple variables into a single auto declaration when they share the same initial value:
auto x, z, w = 10;   // all three initialized to 10, each inferred as int
auto in C△ is not the C11 storage-class specifier — that meaning has been removed entirely. Writing auto with C11 intent will raise a SyntaxError.

dynam — Dynamic Array

A dynam array is a typed, resizable array that grows and shrinks at runtime. You declare it with a bracketed initializer list and modify it with built-in methods.
dynam int numbers = [1, 2, 3, 4, 5];

numbers.push(6);      // append 6 to the end
numbers.pop();        // remove and discard the last element
numbers.remove(0);    // remove the element at index 0

int size = len(numbers);   // current number of elements
dynam arrays have no fixed capacity. The runtime expands or contracts the backing allocation automatically — you never call realloc manually.

tuple — Heterogeneous List

A tuple is a dynamically-sized list that holds values of mixed types. Access elements by zero-based index, append with .push(), and query length with len().
tuple t = [1, "hello", 3.14, 'x'];

auto first  = t[0];    // 1       (int)
auto second = t[1];    // "hello" (string)

t.push("added");       // append a new element of any type

int t_len = len(t);    // 5 — includes the newly pushed element
Tuples are the backbone of C△‘s variadic function support. When you declare a function with tuple args*, the compiler packs all call-site arguments into a tuple that you can iterate over at runtime.
Struct constructors/destructors (init/end), member functions, and typed struct inheritance are not yet implemented. Plain C-style structs (fields only) work via the C11 baseline. See Compiler Status.

Struct Types

C△ provides two struct variants. Choose plain struct for data types that need constructors and methods but not polymorphism. Choose typed struct when you need inheritance and want the type to be recognized natively by the compiler.
Plain structs support an init constructor lifecycle, an end destructor lifecycle, and member functions. They do not support inheritance.
struct Vec2(float x, float y) {
    init {
        self.x = x;
        self.y = y;
    }

    end { /* cleanup on scope exit */ }

    float length() {
        return sqrt(x * x + y * y);
    }
}

Vec2 v = Vec2(3.0, 4.0);
printd("%f", v.length());   // 5.0
The self keyword gives member functions access to the struct’s own fields. It is optional but recommended for clarity when field names shadow parameter names.
A typed struct becomes a native type in the compiler and supports single and multiple inheritance. Declare parent types with &ParentName immediately after the struct name.
typed struct Animal(string name) {
    init { string self.name = name; }
    string speak() { return "..."; }
    end { /* cleanup */ }
}

// Single inheritance
typed struct Dog&Animal(string name) {
    init { /* initialize Dog */ }
    string speak() { return "Woof!"; }
    end { /* cleanup */ }
}

Dog d = Dog("Rex");
printd("%s", d.speak());   // Woof!
Multiple inheritance chains additional parents left to right. Constructors execute in declaration order (left to right). When two parents define the same method name, qualify the call with the parent struct name to resolve the conflict:
typed struct PoliceDog&Dog&Animal(string name, int badge) {
    init { /* initialize PoliceDog */ }
    // Constructor order: Animal → Dog → PoliceDog
    // Disambiguate conflicting methods:
    //   obj.Animal.speak()   — calls Animal's speak
    //   obj.Dog.speak()      — calls Dog's speak
    end { /* cleanup */ }
}
When multiple parent types define the same method, calling that method directly on the child is ambiguous and will not compile. Always resolve conflicts by qualifying with the parent name: obj.Dog.speak().

Type Format Specifiers

Use these specifiers with printd and other formatted-output functions to match each C△ type to its correct format token. The %k specifier is unique to C△ and handles auto variables whose concrete type is only known at runtime.
SpecifierType
%dint
%ldlong
%lldlong long
%uunsigned
%ooctal
%x / %Xhex (lowercase / uppercase)
%ppointer
%ffloat / double
%sstring / char*
%cchar
%kauto (dynamic type — resolved at runtime)
When printing an auto variable whose type you don’t know at compile time, use %k. The compiler’s simulation pass resolves the underlying type and formats the value correctly.