c c99

c - ¿Por qué el tipo de retorno para ftell no es fpos_t?



c99 (4)

Según C99, el prototipo de ftell es:

long int ftell(FILE *stream);

Por lo que entiendo, debería ser lo siguiente:

fpos_t ftell(FILE *stream);

¿Porqué es eso?

De §7.19.1-2

fpos_t que es un tipo de objeto distinto de un tipo de matriz capaz de registrar toda la información necesaria para especificar de forma única cada posición dentro de un archivo.

Entiendo que fpos_t debe usarse para registrar una posición dentro de un archivo. Entonces ftell que devuelve una posición dentro de un archivo debe ser de ese tipo. En cambio es:

  • signed
  • de tipo long que puede ser demasiado pequeño o demasiado grande para acceder a un archivo en ciertas arquitecturas.

Desde la página de fgetpos()/fsetpos() de fgetpos()/fsetpos() :

En algunos sistemas que no son UNIX, un objeto fpos_t puede ser un objeto complejo y estas rutinas pueden ser la única forma de reposicionar de forma portátil un flujo de texto.

mientras que ftell() es necesario para devolver el desplazamiento del puntero del archivo en el archivo. Estas son interfaces completamente diferentes.


El uso más probable de eso es permitir valores de retorno de error como números negativos. Es lo mismo que la familia de funciones printf , que devuelve ssize_t lugar de size_t , que resulta ser una versión signed de size_t .

El primero de estos trucos ocurrió con getchar() , que devuelve int lugar de char , para permitir que el valor devuelto al final de la condición de archivo ( EOF ), que normalmente es un valor negativo, contrasta con el conjunto completo de posibles caracteres devueltos (en el rango de 0 a 255 , todos los enteros positivos)

¿Por qué no define una extensión firmada del mismo tipo para permitir -1 ? En realidad no lo sé :)


Razones históricas.

fseek y ftell son funciones muy antiguas, anteriores a la estandarización de C. Asumen que el long es lo suficientemente grande como para representar una posición en cualquier archivo, una suposición que probablemente era válida en ese momento. long es de al menos 32 bits, y obviamente no podría tener un solo archivo mayor de 2 gigabytes (o incluso 1.21 gigabytes ).

Cuando se publicó el primer estándar C (ANSI C, 1989), se estaba haciendo evidente que esta suposición ya no era válida, pero cambiar las definiciones de fseek y ftell habría roto el código existente. Además, todavía no había un tipo entero más ancho que long ( long long no se introdujo hasta C99).

El comité ANSI C decidió que fseek y ftell seguían siendo útiles, pero introdujeron nuevas funciones de posicionamiento de archivos fsetpos y fgetpos . Estas funciones utilizan un tipo opaco no numérico fpos_t lugar de long , lo que los hace más y menos flexibles que fseek y ftell . Una implementación puede definir fpos_t para que pueda representar cualquier posible desplazamiento de archivo, pero dado que es un tipo no numérico, fsetpos y fgetpos no proporcionan la función SEEK_SET / SEEK_CUR / SEEK_END . Por ejemplo, no hay forma de usar fsetpos para colocar un archivo hasta el final.

Algo de esto se aborda en el ANSI C Justificación, sección 4.9.9 :

Dadas estas restricciones, el Comité aún consideró que esta función [ fseek ] tiene suficiente utilidad y se utiliza en un código existente suficiente para garantizar su retención en la Norma. Se han agregado fgetpos y fsetpos para manejar archivos que son demasiado grandes para manejar con fseek y ftell .

Si esto se definiera desde cero hoy en día, probablemente habría un solo par de funciones que cubrirían toda la funcionalidad de las cuatro funciones actuales, probablemente utilizando un tipo entero tipo typedef ed requerido para ser lo suficientemente grande como para representar cualquier posible desplazamiento de archivo. (Con los sistemas actuales, es probable que 64 bits sean suficientes, pero no me sorprendería ver archivos de 8 exabytes antes de demasiado tiempo en sistemas grandes).


Tenga en cuenta que fpos_t es

[...] un tipo de objeto completo que no sea un tipo de matriz capaz de registrar toda la información necesaria para especificar de forma única cada posición dentro de un archivo.

¡Entonces puede ser incluso una estructura, totalmente inutilizable para cualquier otra cosa además de llamar a fsetpos !

Por otro lado, el valor de retorno de ftell es un escalar que se garantiza su uso para indicar la posición exacta de bytes en un archivo binario:

Para una secuencia binaria, el valor es el número de caracteres desde el comienzo del archivo.

Aparte de eso, la razón es la compatibilidad con versiones anteriores . ftell debutó en C89, y quizás entonces la expectativa era que el long se escalaría lo suficientemente rápido como para contener todos los tamaños de archivo, algo que no siempre es cierto hoy en día. Desafortunadamente, no es posible cambiar el tipo devuelto por ftell pero es demasiado tarde para cambiar eso ahora, incluso aquellas plataformas que admiten archivos más grandes ahora tienen funciones con otro nombre, como ftello .

se requiere la firma porque la función devuelve -1 en caso de error.