Quando chiami la funzione 'triangolo' passi il parametro per valore non per riferimento, questo vuol dire che la funzione chiamata puo modificare a piacere il parametro senza che questo abbia effetto sulla variabile nella funzione chiamante.
Il fatto che la funzione chiamata e quella chiamante siano entrambe la funzione 'triangolo' non cambia niente, ognuna ha il suo stack e le sue variabili locali.
void triangolo(int num){
if(num > 0)
{
// se qui ad esempio num vale 5
triangolo(num - 1);
// anche dopo la chiamata num continua a valere 5
for(int i=0; i<num; i++)
printf("*");
printf("\n");
}
}
Per cui prima vengono chiamate le funzioni più annidate, poi si risale lo stack delle chiamate, vengono ristabiliti i valori delle variabili locali ed effettuate le stampe e il tutto funziona magicamente. Le prime volte questo meccanismo può disorientare, riflettici un poco.