---
title: "Sintaxe Básica de Zig: Variáveis, Tipos e Funções — Guia Completo"
url: "https://ziglang.com.br/tutoriais/zig-sintaxe-basica-variaveis-tipos-funcoes/"
markdown_url: "https://ziglang.com.br/tutoriais/zig-sintaxe-basica-variaveis-tipos-funcoes.MD"
description: "Domine a sintaxe básica de Zig. Aprenda tudo sobre variáveis, tipos de dados, funções e escopo. Tutorial detalhado em português para iniciantes em Zig."
date: "2026-02-09"
author: ""
---

# Sintaxe Básica de Zig: Variáveis, Tipos e Funções — Guia Completo

Domine a sintaxe básica de Zig. Aprenda tudo sobre variáveis, tipos de dados, funções e escopo. Tutorial detalhado em português para iniciantes em Zig.


Este é o **segundo artigo** da série ["Zig para Iniciantes"](/tutoriais/zig-para-iniciantes/). Se você ainda não leu o primeiro, comece por [Zig para Iniciantes: Primeiros Passos](/tutoriais/zig-para-iniciantes/artigo-1-primeiros-passos/).

Neste guia, vamos **aprofundar na sintaxe de Zig** — tudo que você precisa saber sobre variáveis, tipos de dados, funções e escopo. Ao final, você terá domínio completo dos blocos de construção fundamentais da linguagem.

> 🎯 **Objetivo:** Entender profundamente como Zig lida com dados e organização de código.

---

## Variáveis em Zig: const vs var

A distinção entre constantes e variáveis é um dos pilares da programação segura em Zig.

### const (Constantes)

Valores que **nunca mudam** após serem definidos:

```zig
const PI: f64 = 3.14159265359;
const NOME_DO_PROJETO = "MeuApp";
const MAX_USUARIOS: u32 = 1000;

// ❌ ERRO: não pode modificar const
// PI = 3.14;
```

### var (Variáveis Mutáveis)

Valores que **podem ser modificados**:

```zig
var contador: u32 = 0;
contador = 1;        // ✅ OK
contador += 1;       // ✅ OK (agora é 2)
```

### Por Que Preferir const?

```zig
const std = @import("std");

pub fn main() void {
    // Código com const é mais fácil de entender
    const raio: f64 = 5.0;
    const area = PI * raio * raio;
    
    // Você SABE que 'area' não vai mudar
    std.debug.print("Área: {d:.2}\n", .{area});
}
```

**Benefícios de usar const:**
- 🧠 Menos coisas para se preocupar
- 🐛 Menos bugs (valores não mudam inesperadamente)
- ⚡ Melhor otimização pelo compilador
- 📖 Código mais fácil de ler

### Tipos de Variáveis Especiais

#### threadlocal

Variáveis que existem uma por thread:

```zig
threadlocal var contador_thread: u32 = 0;
```

#### volatile

Para acesso a hardware ou memória compartilhada:

```zig
const registrador_hardware: *volatile u32 = @ptrFromInt(0x1000_0000);
```

---

## Sistema de Tipos do Zig

Zig tem um sistema de tipos **explícito e completo**. Vamos explorar cada categoria.

### Inteiros

| Tipo | Bits | Valor Mínimo | Valor Máximo |
|------|------|--------------|--------------|
| `i8` | 8 | -128 | 127 |
| `u8` | 8 | 0 | 255 |
| `i16` | 16 | -32.768 | 32.767 |
| `u16` | 16 | 0 | 65.535 |
| `i32` | 32 | -2.147.483.648 | 2.147.483.647 |
| `u32` | 32 | 0 | 4.294.967.295 |
| `i64` | 64 | ~-9 quintilhões | ~9 quintilhões |
| `u64` | 64 | 0 | ~18 quintilhões |
| `i128` | 128 | Muito grande | Muito grande |
| `u128` | 128 | 0 | Enorme |

**Regras práticas:**
- Use `u8` para bytes e caracteres ASCII
- Use `i32`/`u32` para números gerais
- Use `i64`/`u64` para IDs, timestamps, contadores grandes
- Use `usize` para tamanhos de memória e índices

```zig
const std = @import("std");

pub fn main() void {
    // Idade humana: 0-150, cabe em u8
    const idade: u8 = 25;
    
    // População mundial: cabe em u32
    const populacao_mundial: u32 = 8_000_000_000;
    
    // Timestamp Unix: precisa de i64
    const timestamp: i64 = 1_707_686_400;
    
    std.debug.print("Idade: {d} anos\n", .{idade});
    std.debug.print("População: {d} pessoas\n", .{populacao_mundial});
    std.debug.print("Timestamp: {d}\n", .{timestamp});
}
```

### Tipos de Tamanho Arbitrário

Você pode criar inteiros de qualquer tamanho:

```zig
const a: u7 = 100;      // 7 bits sem sinal
const b: i23 = -42;     // 23 bits com sinal
const c: u1 = 1;        // 1 bit (0 ou 1)
```

### Tipos de Ponto Flutuante

| Tipo | Precisão | Bytes | Use Quando... |
|------|----------|-------|---------------|
| `f16` | Meia | 2 | Pouca memória, precisão não crítica |
| `f32` | Simples | 4 | Padrão para gráficos, jogos |
| `f64` | Dupla | 8 | **Padrão geral**, cálculos científicos |
| `f80` | Estendida | 10 | Cálculos x87 (raro) |
| `f128` | Quadrupla | 16 | Precisão extrema (raro) |

```zig
const std = @import("std");

pub fn main() void {
    const temperatura: f64 = 23.5;
    const pi: f32 = 3.14159;
    const nota_cientifica: f64 = 1.5e10;  // 15 bilhões
    
    std.debug.print("Temperatura: {d:.1}°C\n", .{temperatura});
    std.debug.print("PI (f32): {d:.5}\n", .{pi});
    std.debug.print("Notação científica: {e}\n", .{nota_cientifica});
}
```

### Booleanos

```zig
const ativo: bool = true;
const desligado: bool = false;

// Operações lógicas
const a = true;
const b = false;

const e_logico = a and b;    // false
const ou_logico = a or b;    // true
const nao_logico = !a;       // false
```

### Caracteres (u8, u21, u32)

```zig
const letra_ascii: u8 = 'A';        // ASCII simples
const emoji: u21 = '🎉';            // Unicode (21 bits)
const qualquer_unicode: u32 = '中'; // Qualquer codepoint Unicode
```

---

## Arrays e Slices

### Arrays (Tamanho Fixo)

```zig
const std = @import("std");

pub fn main() void {
    // Array de inteiros (tamanho conhecido em compile-time)
    const numeros = [_]i32{ 10, 20, 30, 40, 50 };
    
    // [_] significa "inferir o tamanho"
    // É o mesmo que: const numeros: [5]i32 = ...
    
    std.debug.print("Primeiro: {d}\n", .{numeros[0]});
    std.debug.print("Tamanho: {d}\n", .{numeros.len});
    
    // Array de strings
    const nomes = [_][]const u8{ "Ana", "Bruno", "Carla" };
    
    for (nomes) |nome| {
        std.debug.print("Nome: {s}\n", .{nome});
    }
}
```

### Slices ("Fatias" de Arrays)

Slices são referências para uma sequência de elementos:

```zig
const std = @import("std");

pub fn main() void {
    const numeros = [_]i32{ 10, 20, 30, 40, 50 };
    
    // Slice dos elementos 1 a 3 (índices 1, 2)
    const fatia: []const i32 = numeros[1..3];
    
    std.debug.print("Fatia: ", .{});
    for (fatia) |n| {
        std.debug.print("{d} ", .{n});
    }
    std.debug.print("\n", .{});
    // Saída: Fatia: 20 30
}
```

### Arrays Multidimensionais

```zig
const std = @import("std");

pub fn main() void {
    // Matriz 3x3
    const matriz = [3][3]i32{
        [_]i32{ 1, 2, 3 },
        [_]i32{ 4, 5, 6 },
        [_]i32{ 7, 8, 9 },
    };
    
    std.debug.print("Elemento [1][2]: {d}\n", .{matriz[1][2]});  // 6
}
```

---

## Strings em Zig

Strings em Zig são **arrays de bytes (u8)** com codificação UTF-8.

### String Literais

```zig
const std = @import("std");

pub fn main() void {
    // String literal: []const u8
    const mensagem = "Olá, Zig!";
    
    std.debug.print("Mensagem: {s}\n", .{mensagem});
    std.debug.print("Tamanho: {d} bytes\n", .{mensagem.len});
    
    // Caracteres individuais
    const primeira_letra = mensagem[0];  // 'O' (79 em ASCII)
    std.debug.print("Primeira letra: {c}\n", .{primeira_letra});
}
```

### Concatenação e Manipulação

```zig
const std = @import("std");

pub fn main() !void {
    // Para manipulação dinâmica, precisamos de um allocator
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    
    // Concatenar strings
    const parte1 = "Olá, ";
    const parte2 = "Mundo!";
    
    const resultado = try std.fmt.allocPrint(allocator, "{s}{s}", .{ parte1, parte2 });
    defer allocator.free(resultado);
    
    std.debug.print("{s}\n", .{resultado});
}
```

> 💡 **Nota:** Strings dinâmicas exigem alocação de memória. Veremos isso em detalhes no artigo sobre [Gerenciamento de Memória](/tutoriais/gerenciamento-de-memoria-zig/).

---

## Funções em Profundidade

### Estrutura Básica

```zig
// Função sem parâmetros e sem retorno
fn dizOla() void {
    std.debug.print("Olá!\n", .{});
}

// Função com parâmetros
fn soma(a: i32, b: i32) i32 {
    return a + b;
}

// Função com múltiplos parâmetros de tipos diferentes
fn apresentar(nome: []const u8, idade: u8) void {
    std.debug.print("{s} tem {d} anos\n", .{ nome, idade });
}
```

### Retorno de Múltiplos Valores

Zig permite retornar múltiplos valores usando **structs anônimos**:

```zig
const std = @import("std");

fn divideComResto(dividendo: i32, divisor: i32) struct { quociente: i32, resto: i32 } {
    return .{
        .quociente = @divTrunc(dividendo, divisor),
        .resto = @rem(dividendo, divisor),
    };
}

pub fn main() void {
    const resultado = divideComResto(17, 5);
    
    std.debug.print("17 / 5 = {d} (resto {d})\n", .{
        resultado.quociente,
        resultado.resto,
    });
    // Saída: 17 / 5 = 3 (resto 2)
}
```

### Funções com Error Union

```zig
const std = @import("std");

const ErroDivisao = error{ DivisaoPorZero };

fn divideSegura(a: f64, b: f64) ErroDivisao!f64 {
    if (b == 0) {
        return ErroDivisao.DivisaoPorZero;
    }
    return a / b;
}

pub fn main() void {
    const resultado = divideSegura(10, 2) catch |err| {
        std.debug.print("Erro: {}\n", .{err});
        return;
    };
    
    std.debug.print("Resultado: {d}\n", .{resultado});
}
```

### Funções Anônimas (Inline)

```zig
const std = @import("std");

pub fn main() void {
    // Definir uma struct com função de callback
    const Operacao = struct {
        fn executar(a: i32, b: i32, operacao: fn (i32, i32) i32) i32 {
            return operacao(a, b);
        }
    };
    
    const soma = struct {
        fn call(x: i32, y: i32) i32 {
            return x + y;
        }
    }.call;
    
    const resultado = Operacao.executar(5, 3, soma);
    std.debug.print("5 + 3 = {d}\n", .{resultado});
}
```

### Funções com comptime (Avançado)

```zig
// Função que aceita qualquer tipo em compile-time
fn maior(comptime T: type, a: T, b: T) T {
    return if (a > b) a else b;
}

pub fn main() void {
    const max_int = maior(i32, 10, 20);     // 20
    const max_float = maior(f64, 3.14, 2.71); // 3.14
    
    std.debug.print("Maior inteiro: {d}\n", .{max_int});
    std.debug.print("Maior float: {d}\n", .{max_float});
}
```

---

## Escopo e Visibilidade

### Blocos e Escopo

```zig
const std = @import("std");

pub fn main() void {
    const x = 10;  // Escopo da função main
    
    {
        const y = 20;  // Escopo do bloco interno
        std.debug.print("x = {d}, y = {d}\n", .{ x, y });
    }
    
    // y não existe mais aqui
    std.debug.print("x = {d}\n", .{x});
    // std.debug.print("y = {d}\n", .{y});  // ❌ ERRO: y não declarado
}
```

### Shadowing (Sobreposição)

```zig
const std = @import("std");

pub fn main() void {
    const x: i32 = 10;
    std.debug.print("x externo: {d}\n", .{x});
    
    {
        const x: f64 = 3.14;  // Shadowing permitido
        std.debug.print("x interno: {d}\n", .{x});
    }
    
    std.debug.print("x externo novamente: {d}\n", .{x});
}
```

### pub (Visibilidade Pública)

```zig
// Este módulo
const math = struct {
    // Público: acessível de fora
    pub fn soma(a: i32, b: i32) i32 {
        return a + b;
    }
    
    // Privado: só acessível dentro deste struct
    fn subtracao(a: i32, b: i32) i32 {
        return a - b;
    }
    
    // Pode chamar função privada de pública
    pub fn operacaoSecreta(a: i32, b: i32) i32 {
        return subtracao(a, b);
    }
};

pub fn main() void {
    const resultado1 = math.soma(5, 3);           // ✅ OK
    // const resultado2 = math.subtracao(5, 3);   // ❌ ERRO: função privada
    const resultado3 = math.operacaoSecreta(5, 3); // ✅ OK
    
    std.debug.print("Soma: {d}\n", .{resultado1});
    std.debug.print("Operação secreta: {d}\n", .{resultado3});
}
```

---

## Conversão de Tipos

### Casting Explícito

Zig **não converte tipos implicitamente**. Você deve ser explícito:

```zig
const std = @import("std");

pub fn main() void {
    const a: i32 = 100;
    const b: f64 = @floatFromInt(a);  // i32 -> f64
    
    const c: f64 = 3.14;
    const d: i32 = @intFromFloat(c);  // f64 -> i32 (trunca)
    
    const e: u8 = 255;
    const f: i16 = @intCast(e);       // u8 -> i16
    
    std.debug.print("a={d}, b={d}, c={d}, d={d}\n", .{ a, b, c, d });
}
```

### @truncate vs @intCast

```zig
const std = @import("std");

pub fn main() void {
    // @truncate: corta bits (pode perder dados)
    const grande: u16 = 1000;
    const pequeno_truncado: u8 = @truncate(grande);  // 1000 % 256 = 232
    
    // @intCast: converde com verificação em debug mode
    const valor: i32 = 100;
    const convertido: i64 = @intCast(valor);  // Sempre seguro: i32 cabe em i64
    
    std.debug.print("Truncado: {d}\n", .{pequeno_truncado});
    std.debug.print("Convertido: {d}\n", .{convertido});
}
```

### @as (Casting Explícito)

```zig
const std = @import("std");

pub fn main() void {
    // Força o tipo de um literal
    const x = @as(u32, 100);  // 100 é tratado como u32
    const y = @as(f32, 3.14); // 3.14 é tratado como f32
    
    std.debug.print("x: {d} ({s})\n", .{ x, @typeName(@TypeOf(x)) });
}
```

---

## Exercícios Práticos

### Exercício 1: Calculadora Completa

Crie uma calculadora com funções separadas para cada operação:

<details>
<summary>Ver Solução</summary>

```zig
const std = @import("std");

fn soma(a: f64, b: f64) f64 { return a + b; }
fn subtracao(a: f64, b: f64) f64 { return a - b; }
fn multiplicacao(a: f64, b: f64) f64 { return a * b; }
fn divisao(a: f64, b: f64) f64 { return a / b; }

pub fn main() void {
    const x: f64 = 15.5;
    const y: f64 = 4.5;
    
    std.debug.print("{d:.1} + {d:.1} = {d:.1}\n", .{ x, y, soma(x, y) });
    std.debug.print("{d:.1} - {d:.1} = {d:.1}\n", .{ x, y, subtracao(x, y) });
    std.debug.print("{d:.1} * {d:.1} = {d:.1}\n", .{ x, y, multiplicacao(x, y) });
    std.debug.print("{d:.1} / {d:.1} = {d:.1}\n", .{ x, y, divisao(x, y) });
}
```

</details>

### Exercício 2: Conversor de Temperatura

Converta Celsius para Fahrenheit e Kelvin:

<details>
<summary>Ver Solução</summary>

```zig
const std = @import("std");

fn celsiusParaFahrenheit(c: f64) f64 {
    return (c * 9.0 / 5.0) + 32.0;
}

fn celsiusParaKelvin(c: f64) f64 {
    return c + 273.15;
}

pub fn main() void {
    const celsius: f64 = 25.0;
    
    const fahrenheit = celsiusParaFahrenheit(celsius);
    const kelvin = celsiusParaKelvin(celsius);
    
    std.debug.print("{d:.1}°C = {d:.1}°F = {d:.2}K\n", .{
        celsius, fahrenheit, kelvin
    });
}
```

</details>

### Exercício 3: Estatísticas de Array

Calcule média, máximo e mínimo de um array:

<details>
<summary>Ver Solução</summary>

```zig
const std = @import("std");

fn media(numeros: []const f64) f64 {
    var soma: f64 = 0;
    for (numeros) |n| {
        soma += n;
    }
    return soma / @as(f64, @floatFromInt(numeros.len));
}

fn maximo(numeros: []const f64) f64 {
    var max = numeros[0];
    for (numeros[1..]) |n| {
        if (n > max) max = n;
    }
    return max;
}

fn minimo(numeros: []const f64) f64 {
    var min = numeros[0];
    for (numeros[1..]) |n| {
        if (n < min) min = n;
    }
    return min;
}

pub fn main() void {
    const notas = [_]f64{ 8.5, 9.0, 7.5, 10.0, 6.5 };
    
    std.debug.print("Estatísticas das Notas:\n", .{});
    std.debug.print("  Média: {d:.2}\n", .{media(&notas)});
    std.debug.print("  Máximo: {d:.1}\n", .{maximo(&notas)});
    std.debug.print("  Mínimo: {d:.1}\n", .{minimo(&notas)});
}
```

</details>

---

## FAQ — Perguntas Frequentes

### Qual a diferença entre []u8 e [5]u8?

- `[5]u8`: Array de exatamente 5 bytes (tamanho conhecido em compile-time)
- `[]u8`: Slice — referência para uma sequência de bytes (tamanho em runtime)

### Por que Zig não tem string como tipo nativo?

Strings são semanticamente arrays de bytes. Zig evita tipos "mágicos" — tudo é explícito.

### Posso retornar um array de uma função?

Sim, se o tamanho for conhecido em compile-time:

```zig
fn retornaArray() [3]i32 {
    return .{ 1, 2, 3 };
}
```

Para arrays dinâmicos, use alocação ou retorne um slice.

### Como faço cast de inteiros de forma segura?

Use `@intCast` para conversões que podem falhar — Zig verifica em modo debug:

```zig
const pequeno: u8 = @intCast(grande);  // Panic em debug se não couber
```

Use `@truncate` quando quer cortar bits explicitamente.

---

## Resumo

Neste artigo, você aprendeu:

- ✅ Diferença entre `const` e `var`
- ✅ Todos os tipos numéricos (inteiros e floats)
- ✅ Arrays, slices e strings
- ✅ Como criar e usar funções
- ✅ Escopo e visibilidade
- ✅ Conversão de tipos

### 📚 Continue Aprendendo

- ⏳ **Artigo 3:** Controle de Fluxo em Zig: if, switch, loops — Estruturas de decisão e repetição
- 🔗 [Tratamento de Erros em Zig](/tutoriais/tratamento-de-erros-em-zig/) — Error unions e recover
- 🔗 [Gerenciamento de Memória](/tutoriais/gerenciamento-de-memoria-zig/) — Allocators e ponteiros
- 🔗 [Zig Build System](/tutoriais/zig-build-system/) — Organização de projetos

---

*Este artigo faz parte da série ["Zig para Iniciantes"](/tutoriais/zig-para-iniciantes/). Tem dúvidas? Deixe um comentário!*

**Happy coding! 🦎**