printf con std::cadena?
Según tengo entendido, string
es miembro del std
espacio de nombres, entonces, ¿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 se ejecuta el programa, myString
imprime una cadena aparentemente aleatoria de 3 caracteres, como en el resultado anterior.
Actualización de C++23
Ahora finalmente tenemos std::print
una forma de utilizarlo std::format
para la salida directamente:
#include <print>
#include <string>
int main() {
// ...
std::print("Follow this command: {}", myString);
// ...
}
Esto combina lo mejor de ambos enfoques.
Respuesta original
Se está compilando porque printf
no es seguro para escribir, ya que utiliza argumentos variables en el sentido C 1 . printf
no tiene opción para std::string
, solo una cadena 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, por lo que podría pasar cualquier cosa.
La forma más sencilla de solucionar este problema, ya que estás usando C++, es imprimirlo normalmente con std::cout
, ya que std::string
lo admite mediante la sobrecarga del operador:
std::cout << "Follow this command: " << myString;
Si, por alguna razón, necesita extraer la cadena de estilo C, puede usar el c_str()
método de std::string
para obtener una const char *
cadena terminada en nulo. 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 desea una función similar a printf
, pero con escritura segura, busque plantillas variadas (C++11, compatible con todos los compiladores principales a partir de MSVC12). Puedes encontrar un ejemplo de uno aquí . No conozco nada implementado de esa manera en la biblioteca estándar, pero podría haberlo en Boost, específicamente boost::format
.
[1]: Esto significa que puede pasar cualquier cantidad de argumentos, pero la función depende de usted para indicarle la cantidad y los tipos de esos argumentos. En el caso de printf
, eso significa una cadena con información de tipo codificada como %d
significado int
. Si mientes sobre el tipo o número, la función no tiene una forma estándar de saberlo, aunque algunos compiladores tienen la capacidad de verificar y dar advertencias cuando mientes.
Úselo myString.c_str()
si desea una cadena tipo C ( const char*
) para usar con printf
gracias
Por favor no usesprintf("%s", your_string.c_str());
Úselo cout << your_string;
en su lugar. Corto, simple y seguro para escribir. De hecho, cuando escribes C++, generalmente quieres evitarlo printf
por completo: es un resto de C que rara vez es necesario o útil en C++.
En cuanto a por qué debería utilizar cout
en lugar de printf
, las razones son numerosas. He aquí una muestra de algunos de los más obvios:
- Como muestra la pregunta,
printf
no es seguro para escribir. Si el tipo que pasa difiere del especificado 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 sobre esto en algunas circunstancias, pero algunos compiladores no pueden o no quieren en absoluto, y ninguno puede hacerlo en todas las circunstancias. printf
no es extensible. Solo puedes pasarle tipos primitivos. El conjunto de especificadores de conversión que comprende está codificado en su implementación y no hay forma de agregar más/otros. El C++ más bien escrito debería utilizar estos tipos principalmente para implementar tipos orientados al problema que se está resolviendo.Hace que el formateo decente sea mucho más difícil. Por ejemplo, cuando imprime números para que las personas los lean, normalmente desea insertar separadores de miles cada pocos dígitos. El número exacto de dígitos y los caracteres utilizados como separadores varía, pero
cout
eso también está cubierto. Por ejemplo:std::locale loc(""); std::cout.imbue(loc); std::cout << 123456.78;
La configuración regional sin nombre (el "") elige una configuración regional según la configuración del usuario. Por lo tanto, en mi máquina (configurada para inglés de EE. UU.) esto se imprime como
123,456.78
. Para alguien que tenga su computadora configurada para (digamos) Alemania, imprimiría algo como123.456,78
. Para alguien que lo tenga configurado para India, se imprimirá como1,23,456.78
(y por supuesto hay muchos otros). Conprintf
obtengo exactamente un resultado:123456.78
. Es consistente, pero consistentemente incorrecto para todos en todas partes. Básicamente, la única forma de solucionarlo es formatear por separado y luego pasar el resultado como una cadena aprintf
, porqueprintf
por sí solo simplemente no hará el trabajo correctamente.- Aunque son bastante compactas,
printf
las cadenas de formato pueden resultar bastante ilegibles. Incluso entre los programadores de C que usanprintf
prácticamente todos los días, supongo que al menos el 99% necesitaría buscar cosas para estar seguros de lo que significa#
in%#x
y en qué se diferencia de lo que significa#
in%#f
(y sí, significan cosas completamente diferentes). ).
Utilice el ejemplo de std::printf y c_str():
std::printf("Follow this command: %s", myString.c_str());