readf print library cstdlib cplusplus c++ c stream iostream cstdio

c++ - print - ¿Cstdio streams vs iostream streams?



fgets c++ (5)

Acabo de enterarme de la existencia de la función ios_base::sync_with_stdio , que básicamente le permite desactivar (o activarla si ya lo ha hecho) la sincronización entre flujos de iostream que se usan en C ++ y los flujos de cstdio que son parte de Standard DO.

Ahora, siempre pensé que stdout , stderr y stdin en C estaban esencialmente envueltos en un conjunto de objetos en C ++ en las clases de iostreams. Pero si tienen que estar sincronizados entre sí, esto indicaría que las clases de iostream C ++ no son una envoltura alrededor del stdin de C, etc.

Estoy bastante confundido por esto? ¿Puede alguien aclarar cómo el iostream de C ++ y el stdio de C son cosas diferentes que hacen exactamente lo mismo, solo en un nivel diferente de abstracción? Pensé que eran lo mismo !

¿Cómo es que tienen que estar sincronizados? Siempre pensé que eran lo mismo, uno que envuelve al otro, esencialmente.


Bien, esto es lo que he encontrado.

En realidad, la E / S se realiza en última instancia mediante funciones y llamadas nativas del sistema.

Ahora, tome Microsoft Windows por ejemplo. En realidad, hay controladores disponibles para STDIN , STDIO , etc. (consulte here ). Básicamente, tanto el iostream C ++ como el C stdio llaman a las funciones nativas del sistema, el iostream C ++ no envuelve las funciones de E / S de C (en las implementaciones modernas). Llama a los métodos del sistema nativo directamente.

Además, encontré esto:

Una vez que se redireccionan stdin, stdout y stderr, se pueden utilizar funciones C estándar como printf () y get (), sin cambios, para comunicarse con la consola Win32. Pero ¿qué pasa con las corrientes de I / O de C ++? Como cin, cout, cerr y clog están estrechamente ligados a stdin, stdout y stderr de C, esperarías que se comporten de manera similar. Esto es mitad correcto.

Las secuencias de E / S de C ++ en realidad vienen en dos tipos: plantilla y no plantilla. La versión anterior sin plantillas de flujos de E / S se está reemplazando lentamente por un estilo de plantillas más nuevo de flujos definidos primero por la Biblioteca de plantillas estándar (STL) y que ahora están siendo absorbidos por el estándar ANSI C ++. Visual C ++ v5 proporciona ambos tipos y le permite elegir entre los dos incluyendo diferentes archivos de encabezado. Las secuencias de E / S de STL funcionan como cabría esperar, utilizando automáticamente los controladores de stdio recién redirigidos. Sin embargo, las secuencias de E / S que no son de plantilla no funcionan como se esperaba. Para descubrir por qué, miré el código fuente, proporcionado convenientemente en el CD-ROM de Visual C ++.

El problema es que las secuencias de E / S anteriores se diseñaron para usar "descriptores de archivos" de estilo UNIX, donde se utilizan enteros en lugar de identificadores (0 para stdin, 1 para stdout, etc.). Eso es conveniente para las implementaciones de UNIX, pero los compiladores de Win32 C tienen que proporcionar otra capa de E / S para representar ese estilo de E / S, ya que Win32 no proporciona un conjunto de funciones compatibles. En cualquier caso, cuando llama a _open_osfhandle () para asociar un nuevo controlador Win32 a (por ejemplo) stdout, no tiene efecto en la otra capa de código de E / S. Por lo tanto, el descriptor de archivos 1 continuará utilizando el mismo identificador Win32 subyacente que antes, y el envío de resultados a cout no producirá el efecto deseado.

Afortunadamente, los diseñadores del paquete original de flujo de E / S previeron este problema y proporcionaron una solución limpia y útil. La clase base ios proporciona una función estática, sync_with_stdio (), que hace que la biblioteca cambie sus descriptores de archivos subyacentes para reflejar cualquier cambio en la capa de E / S estándar. Aunque esto no es estrictamente necesario para las secuencias de E / S STL, no hace daño y me permite escribir código que funciona correctamente con la forma nueva o antigua de secuencias de E / S.

( source )

Por lo tanto, llamar a sync_with_stdio() realidad cambia los descriptores de archivo subyacentes. De hecho, los diseñadores lo agregaron para garantizar la compatibilidad de la E / S de C ++ más antigua con sistemas como Windows-32 que utilizaban identificadores en lugar de números enteros.

Tenga en cuenta que el uso de sync_with_stdio() no es necesario con las E / S STL basadas en plantillas modernas de C ++.


En realidad, stdout , stderr y stdin son los controladores de archivos del sistema operativo. Y la estructura de FILE de C, así como las clases de iostream de C ++ son envoltorios de esos manejadores de archivos. Tanto las clases de iostream como la estructura de ARCHIVO pueden tener sus propios buffers o alguna otra cosa que deba sincronizarse entre sí para asegurarse de que la entrada del archivo o la salida al archivo se realice correctamente.


Los estándares C y C ++ no establecen requisitos sobre cómo se implementan las cosas, solo sobre el efecto de ciertas operaciones. Para la funcionalidad <stdio> vs. <iostream> esto significa que uno podría envolver al otro, ambos podrían ser esencialmente iguales, o que son totalmente independientes. Técnicamente, usar una implementación común sería ideal por varias razones (por ejemplo, no habría necesidad de una sincronización explícita y habría un mecanismo definido para extender el FILE* para sistemas definidos por el usuario) pero no conozco ningún sistema que realmente lo haga. esta. Es posible que una implementación sea una envoltura de la otra y la implementación de <iostream> s en términos de <stdio> fue una opción de implementación típica, aunque tiene el inconveniente de que introduce un costo adicional para ciertas operaciones y la mayoría de las bibliotecas estándar de C ++ han avanzado. Para utilizar implementaciones totalmente separadas.

Desafortunadamente, tanto la implementación envuelta como la independiente comparten un problema común: la E / S es horriblemente ineficiente cuando se realiza un nivel de carácter. Por lo tanto, es esencialmente obligatorio almacenar caracteres en búfer y leer o escribir en un búfer. Esto funciona muy bien para las corrientes que son independientes entre sí. Las capturas son los streams estándar C stdin , stdout , stderr y sus contrapartes de carácter estrecho C ++ std::cin , std::cout , std::cerr / std::clog std::cerr y contrapartes de carácter ancho C ++ std::wcin , std::wcout , std::wcerr / std::wclog , respectivamente: ¿qué sucede cuando un usuario lee tanto stdin como std::cin ? Si cualquiera de estas secuencias lee un búfer de caracteres de la secuencia del sistema operativo subyacente, las lecturas aparecerán fuera de orden. De manera similar, si tanto stdout como std::cout usen búferes independientes, los caracteres aparecerán en un orden inesperado cuando un usuario escriba ambos en ambas secuencias. Como resultado, hay reglas especiales en los objetos de flujo estándar de C ++ (es decir, std::cin , std::cout , std::cerr , y std::clog y sus contrapartes de caracteres anchos) que obligan a que se sincronicen con sus respectivos <stdio> contraparte. Efectivamente, esto significa que específicamente estos objetos C ++ usan una implementación común directamente o que se implementan en términos de <stdio> y no almacenan ningún carácter en el búfer.

Se dio cuenta de que el costo de esta sincronización es bastante considerable si las implementaciones no comparten una base común y puede ser innecesario para algunos usuarios: si un usuario solo usa <iostream> no quiere pagar por la dirección indirecta adicional y Y, lo que es más importante, no quiere pagar los costos adicionales impuestos por no usar un búfer. Para implementaciones cuidadosas, el costo de no usar un búfer puede ser bastante importante porque significa que ciertas operaciones terminan teniendo que hacer una verificación y posiblemente una llamada de función virtual en cada iteración en lugar de solo de vez en cuando. Por lo tanto, std::sync_with_stdio() puede usarse para desactivar esta sincronización, lo que puede significar que los objetos de flujo estándar cambian su implementación interna más o menos completamente. Como los buffers de flujo de los objetos de flujo estándar pueden ser reemplazados por un usuario, desafortunadamente, los buffers de flujo no pueden ser reemplazados pero la implementación interna del buffer de flujo puede ser cambiada.

En buenas implementaciones de la biblioteca <iostream> todo esto solo afecta a los objetos de flujo estándar. Es decir, las secuencias de archivos no deberían verse afectadas por esto. Sin embargo, si desea utilizar los objetos de flujo estándar y desea obtener un buen rendimiento, claramente no desea mezclar <stdio> y <iostream> y desea desactivar la sincronización. Especialmente, al comparar el rendimiento de E / S entre <stdio> y <iostream> debe tener esto en cuenta.


Son la misma cosa, pero también pueden ser almacenados separadamente. Esto podría afectar el código que mezcla el uso de C y C ++ I / O, como este

std::cout << "Hello "; printf("%s", "world"); std::cout << "!/n";

Para que esto funcione, las secuencias subyacentes deben estar sincronizadas de alguna manera. En algunos sistemas, esto podría significar que el rendimiento podría verse afectado.

Por lo tanto, el estándar le permite llamar a std::sync_with_stdio(false) para decir que no le importa este código, pero preferiría que las transmisiones estándar funcionen lo más rápido posible si esto marca una diferencia . En muchos sistemas no hay diferencia.


Uno puede ser un envoltorio alrededor del otro (y eso funciona en ambos sentidos. Puede implementar stdio funciones de stdio usando iostream y viceversa. O puede escribirlas de forma completamente independiente.

Y sync_with_stdio garantiza que las dos secuencias se sincronizarán si está habilitada. Pero también pueden sincronizarse cuando está deshabilitado, si así lo desean.

Pero incluso si uno es un envoltorio alrededor del otro, uno podría tener un búfer que el otro no comparte, por ejemplo, por lo que la sincronización sigue siendo necesaria.