Problema en C: fichero como argumento de funciones

La verdad es que no sé si este es el lugar adecuado del foro, si no es así movedlo, por favor.

Veréis, tengo un problema con una práctica (bajo Linux), y el problema no tiene que ver con la práctica sino con el lenguaje de programación elegido (C, la otra opción es ADA).

El problema es que tengo que abrir un fichero con una función (no puedo hacer fopen directamente en el main), por lo que tengo que pasarlo como parámetro, y después tengo que leer de él a través de otra función, y creo que hay algo que no entiendo, porque no funciona.

Por ejemplo, simplificando lo máximo que he podido:
#include <stdio.h>

void abrir(f)
FILE *f;
{
   f = fopen( "fichero.dat", "r" );
}

void leer(f)
FILE *f;
{
   printf("%c  \n", fgetc(f));
}

main(){
   FILE *g;

   abrir(g);
   leer(g);
}


La función principal abriría el fichero a través de la función "abrir", después "leer" debería leer un caracter de dicho fichero y mostrarlo por pantalla, pero no es así puesto que muestra un caracter al azar.

En el código de la práctica el problema es mayor, pues da un fallo de segmentación.

He hablado con un profesor de otra asignatura y me ha dicho que, seguramente, se deba a que al abrir el fichero dentro de una función, se crea la estructura dentro de la función y, al terminar ésta, desaparece, por lo que acabo teniendo el fichero como un puntero a nada... De ahí que al usar el fichero lea caracteres aleatorios o me dé un fallo de segmentación.

Buff, siento el tostón, siempre me extiendo demasiado intentando detallarlo bien. El caso es que no encuentro nada y no sé cómo solucionarlo, ¿alguna ayudita? Espero no tener que rehacer todo esto en ADA, que llevo 400 líneas escritas [qmparto]

Pues eso, un saludo y muchas gracias.
Ahora no recuerdo mucho de ficheros, pero ¿has probado de declarar las funciones como tipo puntero y devolver el valor (es decir, devolver el fichero abierto)? usas void y por eso "muere" al terminar la función.

No estoy seguro pero por probar...
¿Y si le pasas la dirección del puntero f?

void abrir(FILE **f)
{
*f = fopen( "fichero.dat", "r" );
}
En la función abrir estás pasando un puntero a fichero, pero si dentro de la función asignas un fichero a ese puntero, este cambio no se verá fuera de la función en el main, ya que a la función le pasas un puntero por valor. Como ya han dicho pasa la dirección del puntero, o bien directamente haz que la función devuelva el puntero que generas dentro. Hay que tener bien claro los pasos por valor y referencia ;)

Salu2!
Gracias a los 2 por contestar tan rápido :)

No puedo declarar la función como tipo puntero, tiene que devolver un char. En realidad es algo más compleja, tengo que pasarle el modo de acceso y el nombre y comprobar si se abre bien, por eso devuelve un char.

Y pasando la dirección del puntero... ¡funciona! :D

Me olvidaba decir que las funciones van en una librería, pero amos, con esa solución creo que no hay nada más que tener en cuenta.

Voy a probar en el código de la práctica y si no sirve volveré a dar la lata :P

Muchas gracias de nuevo a los 2 :)

Edito:

Gracias por la explicación Dagaren, creo que no me volverá a pasar :)

Edito de nuevo:

Comprobado en la práctica, va muy bien y me ha quedado muy claro. Gracias de nuevo. [tadoramo]
Como truco para un futuro, en vez de usar flujos (streams) para abrir ficheros, usa descriptores, que no son más que variables enteras normales y corrientes, se abren con open() en lugar de fopen, se leen con write() en vez de fwrite() y se escriben con read() en lugar de fread().

Consulta las páginas de manual de open(), write() y read() a ver cómo lo ves, y si quieres más info o algún ejemplo, pregunta.

Suerte!!!
Apoyo la moción del doble apuntador :P

amuchamu escribió:No puedo declarar la función como tipo puntero, tiene que devolver un char. En realidad es algo más compleja, tengo que pasarle el modo de acceso y el nombre y comprobar si se abre bien, por eso devuelve un char.



En este caso, no sería mejor devolver un bool?
E incluso devolver un valor util como el fichero abierto y NULL en caso de error ...
Sertinell escribió:E incluso devolver un valor util como el fichero abierto y NULL en caso de error ...


Buena opción tb :)
Xar escribió:En este caso, no sería mejor devolver un bool?

En ANSI C no hay booleanos, ¿no?

Sertinell escribió: E incluso devolver un valor util como el fichero abierto y NULL en caso de error ...

No puedo, tiene que devolver Cierto o Falso.
Enunciado escribió:Asociar (E/S f: tpFCotizaciones; E nomFich: tpNomFich; E modAccF: tpAccF; S exito: boolean);
{establece la conexión lógica o asociación con un fichero externo de nombre nomFich; modAccF representa el
modo de acceso al fichero (lectura L, o escritura E). Devuelve Cierto si se ha podido realizar con exito, y Falso
en cualquier otro caso. Por ejemplo, devolverá Falso si se ejecuta Asociar en modo lectura sobre un fichero
externo inexistente. }


Muchas gracias a todos, con los punterillos me he arreglado :)
No puedo, tiene que devolver Cierto o Falso.


Y devolver NULL es la forma de devolver 'Falso' en C. Porque, como bien has dicho, en C89 no hay booleanos (si los hay en C99).

- ferdy
amuchamu escribió:En ANSI C no hay booleanos, ¿no?


ç
puede ser puede ser... estoy ya tan acostumbrado a q los compiladores acepten c++ que me se olvidan esas cosas [tomaaa]
Ferdy escribió:
Y devolver NULL es la forma de devolver 'Falso' en C. Porque, como bien has dicho, en C89 no hay booleanos (si los hay en C99).

- ferdy


Ciertamente, de hecho, NULL es una macro que vale 0, el valor "falso" en condicionales se interpreta como "0", y el valor verdadero como !=0.

Si no puedes devolver el puntero, por lo menos utiliza un int en vez de char, (aunk en realidad da igual), le dará muchísima más flexibilidad al código, además de que puedes establecer una codificación de error, en plan:
Si la función devuelve...
0: Todo correcto, adelante,
1: Imposible asociar en modo lectura sobre un fichero externo inexistente.
2: error de usuario, introduzca uno que no sea incompetente.
etc, etc.

De éste modo con un simple if podrías tanto llamar a la función, saber si ha ido bien, y si es que no, saber por qué ha fallado.
No sabía todo eso del NULL, algo más aprendido.

Lo de devolver un int o un char, la verdad es que no veo la diferencia, puesto que puedo hacer lo mismo de si devuelve 0 bla, si devuelve 1 ble, si devuelve 2 bli... Y de hecho lo hago en alguna otra función. Es decir, que no entiendo muy bien por qué me dices que devuelva un int en vez de un char, 4s|m3tr|ko0.

Gracias de nuevo.
Ciertamente, de hecho, NULL es una macro que vale 0, el valor "falso" en condicionales se interpreta como "0", y el valor verdadero como !=0.


De hecho:

#define NULL ((void*)0)


Sobre lo del int y el char... dado que el char promociona a int, y te da 256 estados distintos... yo creo que te sobra :)

Y aunque el char ocupa menos que el int... tampoco creo que vayas a notar la diferencia jeje

- ferdy
la diferencia es que debido a la equivalencia entre 0, NULL y false, puedes hacer directamente:
if(asociar(blablabla)){
    printf("ERROR");
}else{
    prtinf("todo correcto");
}

o incluso, si deseas guardar el estado en una variable:
if(estado = asociar(blablabla)){
   printf("Error %d", estado);
}else{
   printf("todo correcto");
}

Además, es "convención no escrita" utilizar enteros para devolver códigos de estado. (De todos modos, un char no deja de ser un int "enmascarado", c = 65; es lo mismo que c= 'A', porque 'A' es 65 en la tabla ascii, y por eso también se pueden evaluar chars en sentencias switch).

EDIT: Todo esto te lo digo a título informativo, no tiene trascendencia alguna en que el programa funcione mejor o peor, solo que me parece que simplemente para buscar respuestas ya está google y si puedo ayudar con mi experiencia o por lo menos que la gente salga del hilo sabiendo más que lo estrictamente necesario, pues mejor para todos ^_^
15 respuestas