Hello World

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}
  • No hay ; al final (el compilador los pone solo).
  • package main + func main() = tu int main().
  • No header files, no #include. import trae paquetes.

Variables y tipos

var x int = 10
y := 20          // inferencia de tipos, esto es lo más común
var nombre string = "Carlos"

const Pi = 3.14159

:= es la forma idiomática. var se usa cuando necesitas el tipo explícito o valor cero.

Funciones (retornos múltiples — esto NO existe en C)

func dividir(a, b int) (int, int) {
    return a / b, a % b
}

func main() {
    cociente, resto := dividir(17, 5)
    fmt.Println(cociente, resto) // 3 2
}

Esto reemplaza el patrón de C de pasar punteros de salida.

Manejo de errores (no hay excepciones, ni try/catch)

func dividir(a, b int) (int, error) {
    if b == 0 {
        return 0, fmt.Errorf("división por cero")
    }
    return a / b, nil
}

func main() {
    resultado, err := dividir(10, 0)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Println(resultado)
}

if err != nil es EL patrón en Go. Lo vas a escribir cientos de veces.

Punteros (igual que C, pero sin aritmética de punteros)

func incrementar(x *int) {
    *x = *x + 1
}

func main() {
    n := 5
    incrementar(&n)
    fmt.Println(n) // 6
}

Mismo * y &, pero no puedes hacer p++ ni p + 1. Go te protege de eso.

Structs (tu struct de C, sin typedef necesario)

type Punto struct {
    X, Y int
}

func main() {
    p := Punto{X: 3, Y: 4}
    fmt.Println(p.X, p.Y)
}

“Métodos” en structs (no hay clases, pero hay esto)

func (p Punto) Distancia() float64 {
    return math.Sqrt(float64(p.X*p.X + p.Y*p.Y))
}

func main() {
    p := Punto{3, 4}
    fmt.Println(p.Distancia()) // 5
}

El único loop: for

// como un for de C
for i := 0; i < 5; i++ {
    fmt.Println(i)
}

// como un while de C
i := 0
for i < 5 {
    fmt.Println(i)
    i++
}

// infinito
for {
    break
}

Slices (tu array dinámico, como un malloc/realloc manejado por el lenguaje)

nums := []int{1, 2, 3}
nums = append(nums, 4)
fmt.Println(nums) // [1 2 3 4]

for i, v := range nums {
    fmt.Println(i, v) // índice, valor
}

Concurrencia (la razón #1 por la que la gente usa Go)

func decir(msg string) {
    fmt.Println(msg)
}

func main() {
    go decir("en otra goroutine") // como un thread, pero ultra liviano
    decir("en main")
}

Esto es como crear un pthread pero sin la ceremonia de pthread_create.


Diferencias clave respecto a C que te van a chocar al principio:

  1. No hay punteros nulos peligrosos sueltos por todas partes (nil existe pero el compilador es más estricto).
  2. El garbage collector se encarga de la memoria — no hay malloc/free manual.
  3. No hay herencia. Hay interfaces (comportamiento, no estado).
  4. gofmt formatea tu código automáticamente, no hay debate de estilo.

Los mismos ejemplos pero en C

Dale, acá tenés los mismos ejemplos pero en C puro, para que compares directo.

Hello World

#include <stdio.h>

int main() {
    printf("Hello, World!\n");
    return 0;
}

Variables y tipos

int x = 10;
int y = 20;
char *nombre = "Carlos";

#define PI 3.14159

En C no hay inferencia de tipos como :=, siempre tenés que declarar el tipo.

“Retornos múltiples” (en C esto se simula con punteros de salida)

void dividir(int a, int b, int *cociente, int *resto) {
    *cociente = a / b;
    *resto = a % b;
}

int main() {
    int q, r;
    dividir(17, 5, &q, &r);
    printf("%d %d\n", q, r); // 3 2
}

Esto es justo lo que te mencioné: en C usás punteros para “devolver” varios valores; en Go es nativo del lenguaje.

Manejo de errores (con código de retorno + errno, o un patrón propio)

#include <errno.h>

int dividir(int a, int b, int *resultado) {
    if (b == 0) {
        return -1; // código de error
    }
    *resultado = a / b;
    return 0; // éxito
}

int main() {
    int resultado;
    if (dividir(10, 0, &resultado) != 0) {
        printf("Error: división por cero\n");
        return 1;
    }
    printf("%d\n", resultado);
}

No hay un tipo error como en Go; en C es convención (return -1, errno, etc).

Punteros (acá sí tenés aritmética de punteros, a diferencia de Go)

void incrementar(int *x) {
    *x = *x + 1;
}

int main() {
    int n = 5;
    incrementar(&n);
    printf("%d\n", n); // 6
}

Structs (necesitás typedef para evitar escribir struct siempre)

typedef struct {
    int x, y;
} Punto;

int main() {
    Punto p = {.x = 3, .y = 4};
    printf("%d %d\n", p.x, p.y);
}

“Métodos” en structs (en C no existen, se simulan con funciones que toman el struct)

#include <math.h>

double distancia(Punto p) {
    return sqrt(p.x * p.x + p.y * p.y);
}

int main() {
    Punto p = {3, 4};
    printf("%f\n", distancia(p)); // 5.000000
}

C no tiene azúcar sintáctica de p.Distancia(); siempre es distancia(p).

Los loops: for y while por separado

// for
for (int i = 0; i < 5; i++) {
    printf("%d\n", i);
}

// while
int i = 0;
while (i < 5) {
    printf("%d\n", i);
    i++;
}

// infinito
for (;;) {
    break;
}

Acá está la diferencia que mencionaste al principio: C te da for Y while como cosas separadas.

“Slices” (en C: malloc + realloc manual, sin garbage collector)

#include <stdlib.h>

int main() {
    int *nums = malloc(3 * sizeof(int));
    nums[0] = 1; nums[1] = 2; nums[2] = 3;

    // "append" manual
    nums = realloc(nums, 4 * sizeof(int));
    nums[3] = 4;

    for (int i = 0; i < 4; i++) {
        printf("%d %d\n", i, nums[i]);
    }

    free(nums); // tenés que liberar la memoria vos mismo
}

Concurrencia (pthreads — mucho más ceremonia que go func())

#include <pthread.h>
#include <stdio.h>

void *decir(void *msg) {
    printf("%s\n", (char *)msg);
    return NULL;
}

int main() {
    pthread_t hilo;
    pthread_create(&hilo, NULL, decir, "en otro hilo");
    decir("en main");
    pthread_join(hilo, NULL); // esperar que termine
}

Lo que más debería saltarte a la vista comparando ambos lados:

  • En C gestionás memoria (malloc/free) — en Go el GC lo hace por vos.
  • En C simulás cosas que en Go son nativas (multi-retorno, métodos).
  • En C tenés for y while separados — en Go es solo for.
  • pthreads vs goroutines: la diferencia de “ceremonia” es brutal, por eso Go es tan popular para concurrencia.