Tengo entendido que string
es un miembro del espacio de nombres std
, así que ¿por qué ocurre lo siguiente?
#include <iostream>
int main()
{
using namespace std;
string myString = "Press ENTER to quit program!";
cout << "Come up and C++ me some time." << endl;
printf("Follow this command: %s", myString);
cin.get();
return 0;
}
Cada vez que el programa se ejecuta, miCadena
imprime una cadena aparentemente aleatoria de 3 caracteres, como en la salida anterior.
Está compilando porque printf
no es seguro de tipo, ya que utiliza argumentos variables en el sentido de C1. printf
no tiene una opción para std::string
, sólo una cadena de estilo C. Usar otra cosa en lugar de lo que espera definitivamente no le dará los resultados que desea. En realidad es un comportamiento indefinido, así que podría pasar cualquier cosa.
La forma más fácil de arreglar esto, ya que estás usando C++, es imprimirlo normalmente con std::cout
, ya que std::string
lo soporta a través de la sobrecarga de operadores:
std::cout << "Follow this command: " << myString;
Si, por alguna razón, necesitas extraer la cadena estilo C, puedes usar el método c_str()
de std::string
para obtener un const char *
que esté terminado en cero. Usando tu ejemplo:
#include <iostream>
#include <string>
#include <stdio.h>
int main()
{
using namespace std;
string myString = "Press ENTER to quit program!";
cout << "Come up and C++ me some time." << endl;
printf("Follow this command: %s", myString.c_str()); //note the use of c_str
cin.get();
return 0;
}
Si quieres una función que sea como printf
, pero de tipo seguro, busca en las plantillas variádicas (C++11, soportadas en los principales compiladores a partir de MSVC12). Puedes encontrar un ejemplo de una aquí. No hay nada que conozca implementado así en la biblioteca estándar, pero podría haber en Boost, específicamente boost::format
.
[1]: Esto significa que puedes pasar cualquier número de argumentos, pero la función depende de que le digas el número y tipo de esos argumentos. En el caso de printf
, eso significa una cadena con información de tipo codificada como %d
que significa int
. Si mientes sobre el tipo o el número, la función no tiene una forma estándar de saberlo, aunque algunos compiladores tienen la capacidad de comprobar y dar advertencias cuando mientes.
Por favor, no utilice printf("%s", su_cadena.c_str());
.
Utilice cout << su_cadena;
en su lugar. Corto, simple y seguro. De hecho, cuando escriba en C++, generalmente querrá evitar printf
por completo - es un residuo de C que raramente es necesario o útil en C++.
En cuanto a por qué deberías usar cout
en lugar de printf
, las razones son numerosas. He aquí una muestra de algunas de las más obvias:
Como muestra la pregunta, printf
no es seguro. Si el tipo que pasas difiere del dado en el especificador de conversión, printf
intentará usar lo que encuentre en la pila como si fuera el tipo especificado, dando un comportamiento indefinido. Algunos compiladores pueden advertir de esto en algunas circunstancias, pero algunos compiladores no pueden/quieren en absoluto, y ninguno puede en todas las circunstancias.
printf
no es extensible. Sólo se le pueden pasar tipos primitivos. El conjunto de especificadores de conversión que entiende está codificado en su implementación, y no hay forma de añadir más u otros. La mayoría de los C++ bien escritos deberían usar estos tipos principalmente para implementar tipos orientados al problema que se está resolviendo.Hace que un formato decente sea mucho más difícil. Para un ejemplo obvio, cuando se imprimen números para que la gente los lea, normalmente se quieren insertar separadores de miles cada pocos dígitos. El número exacto de dígitos y los caracteres utilizados como separadores varían, pero cout
tiene eso cubierto también. Por ejemplo:
std::locale loc("");
std::cout.imbue(loc);
std::cout << 123456.78;
El locale sin nombre (el "") escoge un locale basado en la configuración del usuario'. Por lo tanto, en mi máquina (configurada para el inglés de EE.UU.) esto se imprime como 123,456.78
. Para alguien que tenga su ordenador configurado para (digamos) Alemania, se imprimiría algo como 123.456,78
. Para alguien que lo tenga configurado para la India, se imprimiría como 1,23,456.78
(y por supuesto hay muchos otros). Con printf
obtengo exactamente un resultado: 123456,78
. Es consistente, pero es consistentemente equivocado para todos en todas partes. Esencialmente, la única forma de solucionarlo es hacer el formato por separado, y luego pasar el resultado como una cadena a printf
, porque printf
por sí mismo simplemente no hará el trabajo correctamente.
Aunque son bastante compactas, las cadenas con formato printf
pueden ser bastante ilegibles. Incluso entre los programadores de C que usan printf
prácticamente a diario, creo que al menos el 99% necesitaría buscar cosas para estar seguro de lo que significa el #
en %#x
, y en qué se diferencia de lo que significa el #
en %#f
(y sí, significan cosas totalmente diferentes).
utiliza miCadena.c_str()
si quieres una cadena tipo c (const char*
) para utilizarla con printf
gracias