Pásate por codingame.com : pondrás a prueba todo tipo de algoritmos y los podrás comparar con los que ha escrito el resto de la comunidad. También hay desafíos dónde deberás programar la lógica de autómatas que lucharan con otros también programados por humanos.
Pero si te gustan las matemáticas y estás más interesado en la resolución de este tipo de problemas puedes optar por projecteuler.net
Cómo usas conio pues no lo puedo probar, y ahora mismo me da pereza cambiar código, pero ya que kiko es un puntero deberías usar el operador -> en vez de . para acceder a su miembro.
Esto quiere decir que estás trabajando los archivos en modo texto y cuando C encuentra el valor 1A (26 en decimal) lo considera fin de archivo. Cambia el acceso a los archivos para que sean accesos binarios incluyendo b al final: en vez de "a" debe ser "ab", en vez de "r" debe ser "rb".
Para que entiendas: NULL es lo mismo que 0, por tanto char *puntero = NULL es lo mismo que char *puntero = 0, es decir, puntero apuntará a la dirección 0 de memoria. C determina que la posición 0 (NULL) siempre será una posición no válida y saltará el error. Se usa por parte del programador para evitar que un puntero apunte a cualquier sitio y crear problemas de seguridad. Mejor que el programa muera a que te quedes sin dinero en el banco porque no has inicializado un puntero.
Las cadenas en C empiezan en una posición x de la memoria y son secuenciales, es decir, sus datos se encuentran en x+0, x+1, x+2, ... hasta que el dato de x+n valga '\0' (eso es 0), notar que he dicho el dato, el valor de la posición de memoria y no la posición de memoria en sí. Por tanto una función que trabaje con cadenas empezará dónde se le indique el puntero que devuelve la cadena y va recorriendo la memoria de forma secuencial hasta encontrar el carácter de fin de cadena, haciendo lo que deba hacer en cada carácter encontrado.
Una cadena no devuelve su contenido sino su dirección de memoria. Pues eso mismo hace la función: regresa la dirección de memoria de la primera letra de la cadena. Como es una dirección de memoria lo debe adquirir el puntero (por tanto no se va a pasar su contenido).
printf ya sabe que con %s el parámetro va a ser una cadena e internamente va a hacer lo necesario para mostrarla. Pero otra vez a una cadena se le accede por medio de su dirección de memoria del primer elemento, no por su contenido, así que no necesita el *.
NULL es 0, cuando C ve que un puntero apunta a la posición 0 o NULL sabe que no está apuntando a nada.
Esto le sirve al programador para saber si el puntero está inicializado o no, por ejemplo cuando haces uso de fopen está devuelve un puntero. Este puntero puede ser a una estructura FILE correctamente inicializada o a NULL indicando que no se pudo abrir el archivo. O strstr devolverá NULL si una cadena ni está contenida en la otra.