Il passaggio per indirizzo consente di passare a delle funzioni dei parametri non come una copia delle variabili passate, ma come un riferimento ad esse; in questa maniera la funzione può modificare i parametri, senza contare che il passaggio per riferimento, specie quando i parametri sono grosse strutture, può velocizzare la chiamata alla funzione e far risparmiare memoria.

Sullo stack è possibile allocare solo quantità di memoria determinate a compile-time; questo significa che (in linea di massima) se la dimensione di un buffer è nota solo a runtime non puoi allocarlo "normalmente" sullo stack. L'allocazione dinamica invece consente di allocare una quantità di memoria arbitraria (sempre ovviamente nei limiti della memoria - fisica o virtuale che sia - disponibile) definita a runtime.

I puntatori comunque hanno anche diversi altri utilizzi, ad esempio quelli relativi agli array.
... ma non dimentichiamoci dei puntatori a funzione...