printf con std::cadena?

Resuelto Chunky Chunk asked hace 12 años • 9 respuestas

Según tengo entendido, stringes miembro del stdespacio 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;
}

ingrese la descripción de la imagen aquí

Cada vez que se ejecuta el programa, myStringimprime una cadena aparentemente aleatoria de 3 caracteres, como en el resultado anterior.

Chunky Chunk avatar Jun 03 '12 04:06 Chunky Chunk
Aceptado

Actualización de C++23

Ahora finalmente tenemos std::printuna forma de utilizarlo std::formatpara 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 printfno es seguro para escribir, ya que utiliza argumentos variables en el sentido C 1 . printfno 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::stringlo 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::stringpara 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 %dsignificado 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.

chris avatar Jun 02 '2012 21:06 chris

Úselo myString.c_str()si desea una cadena tipo C ( const char*) para usar con printf

gracias

Alessandro Pezzato avatar Jun 02 '2012 21:06 Alessandro Pezzato

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 printfpor completo: es un resto de C que rara vez es necesario o útil en C++.

En cuanto a por qué debería utilizar couten lugar de printf, las razones son numerosas. He aquí una muestra de algunos de los más obvios:

  1. Como muestra la pregunta, printfno es seguro para escribir. Si el tipo que pasa difiere del especificado en el especificador de conversión, printfintentará 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.
  2. printfno 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.
  3. 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 couteso 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 como 123.456,78. Para alguien que lo tenga configurado para India, se imprimirá como 1,23,456.78(y por supuesto hay muchos otros). Con printfobtengo 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 a printf, porque printfpor sí solo simplemente no hará el trabajo correctamente.

  4. Aunque son bastante compactas, printflas cadenas de formato pueden resultar bastante ilegibles. Incluso entre los programadores de C que usan printfprácticamente todos los días, supongo que al menos el 99% necesitaría buscar cosas para estar seguros de lo que significa #in %#xy en qué se diferencia de lo que significa #in %#f(y sí, significan cosas completamente diferentes). ).
Jerry Coffin avatar Jun 02 '2012 21:06 Jerry Coffin

Utilice el ejemplo de std::printf y c_str():

std::printf("Follow this command: %s", myString.c_str());
Adel Ben Hamadi avatar Nov 26 '2015 17:11 Adel Ben Hamadi