Es simple, te explico:
char inicio: este asigna una direccion de memoria reservada para esta variable, es decir, reservamos 8bits y lo nombramos como "inicio".
prueba(inicio): esto esta mal, estas diciendole el contenido de inicio, no la direccion de memoria, para ello se utiliza el & delante del nombre, lo que le das con el & delante es la direccion de memoria de "inicio", bien escrito seria prueba(&inicio);
void prueba(char *fin): estas diciendo que el parametro sera un puntero, nada del otro mundo.
fin=0x01: esto estaria tambien mal, lo que estas haciendo es decir que el parametro se refiera a la direccion 0x01, es decir, le asignas al puntero la direccion 0x01 no el valor, para ello tendrias que usar *fin=0x01;
*fin=0x01 (ya arreglado): con esto le estas diciendo que al contenido de la direccion de fin (en este caso contiene la direccion de "inicio") sea 0x01, es decir, al final char inicio contendria 0x01.
Tienes que tener en cuenta varias cosas, muchas veces los compiladores al optimizar el codigo trabajan con variables temporales y no con direcciones de memoria, para asignarle realmente una direccion de memoria ram lo debes poner en el global (fuera del main) o bien ponerle "static" delante de "char", asi le dices que la variable sera estatica.
Espero que te haya servido de ayuda, suele ser complicado al principio, pero una vez lo entiendes es muy sencillo. Un puntero almacena "una direccion de memoria" y ya con ella hacemos lo que queramos. Si por ejemplo inicio esta en la direccion 0x50 entonces al poner &inicio obtendras el valor 0x50. Ahora bien, "fin" contiene almacenado la direccion 0x50, al ponerle *fin=0x01 lo que le estamos diciendo que a la direccion 0x50 le ponga el valor 0x01.