C++ printf con std:: string?


Entiendo 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;
}

introduzca la descripción de la imagen aquí

Cada vez que se ejecuta el programa, myString imprime una cadena aparentemente aleatoria de 3 caracteres, como en la salida anterior.

Author: Vertexwahn, 2012-06-03

7 answers

Está compilando porque printf no es seguro de tipo, ya que utiliza argumentos variables en el sentido C1. printf no tiene opción para std::string, solo una cadena de estilo C. Usar algo más en lugar de lo que espera definitivamente no le dará los resultados que desea. En realidad es un comportamiento indefinido, así que cualquier cosa podría suceder.

La forma más fácil de arreglar esto, ya que está usando C++, es imprimirlo normalmente con std::cout, ya que std::string soporta eso a través del operador sobrecarga:

std::cout << "Follow this command: " << myString;

Si, por alguna razón, necesita extraer la cadena de estilo C, puede usar el método c_str() de std::string para obtener un const char * terminado en null. Usando su 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 que sea como printf, pero escriba safe, busque en plantillas variádicas (C++11, compatible con todos los compiladores principales a partir de MSVC12). Puedes encontrar un ejemplo de uno aquí. No hay nada que yo sepa implementado como eso en la biblioteca estándar, pero podría haber en Boost, específicamente boost::format.


[1]: Esto significa que puede pasar cualquier número de argumentos, pero la función depende de usted para decirle el número y los tipos de esos argumentos. En el caso de printf, eso significa una cadena con información de tipo codificado 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 verificar y dar advertencias cuando mientes.

 178
Author: chris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-05 08:21:12

Por favor no use printf("%s", your_string.c_str());

Use cout << your_string; en su lugar. Corto, simple y seguro. De hecho, cuando estás escribiendo C++, generalmente quieres evitar printf por completo's es un sobrante de C que rara vez se necesita o es útil en C++.

En cuanto a por qué debe usar cout en lugar de printf, las razones son numerosas. He aquí una muestra de algunos de los más obvios:

  1. Como muestra la pregunta, printf no es seguro para el tipo. Si el tipo que pasa difiere del indicado en el 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 bajo algunas circunstancias, pero algunos compiladores no pueden/no lo harán en absoluto, y ninguno puede bajo todas las circunstancias.
  2. printf no es extensible. Solo puedes pasarle tipos primitivos. El conjunto de especificadores de conversión que entiende está codificado en su implementación, y no hay forma de agregar más/otros. Mas C++ bien escrito debe usar estos tipos principalmente para implementar tipos orientados hacia el problema que se está resolviendo.
  3. Hace que el formato decente sea mucho más difícil. Para un ejemplo obvio, cuando imprime números para que la gente los lea, normalmente desea insertar miles de separadores cada pocos dígitos. El número exacto de dígitos y los caracteres utilizados como separadores varía, pero cout también lo tiene cubierto. Por ejemplo:

    std::locale loc("");
    std::cout.imbue(loc);
    
    std::cout << 123456.78;
    

    El local sin nombre (el "") elige una configuración regional basada en la configuración del usuario. Por lo tanto, en mi máquina (configurada para US English) esto se imprime como 123,456.78. Para alguien que tiene su computadora configurada para (digamos) Alemania, imprimiría algo como 123.456,78. Para alguien con esto 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 incorrecto para todos en todas partes. Esencialmente la única manera de trabajar alrededor de él es hacer el formato por separado, luego pasar el resultado como una cadena a printf, porque printf simplemente no hacer el trabajo correctamente.

  4. Aunque son bastante compactas, las cadenas de formato printf pueden ser bastante ilegibles. Incluso entre los programadores de C que usan printf prácticamente todos los días, supongo que al menos el 99% tendría que buscar cosas para estar seguro de lo que significa # en %#x, y cómo difiere de lo que significa # en %#f (y sí, significan cosas completamente diferentes).
 35
Author: Jerry Coffin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-31 15:22:08

Utilice myString.c_str () si desea que una cadena similar a c (const char*) se use con printf

 23
Author: Alessandro Pezzato,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-02 21:08:56

Use std:: printf y c_str() ejemplo:

std::printf("Follow this command: %s", myString.c_str());
 5
Author: Adel Ben Hamadi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-24 11:19:43

Printf es bastante bueno para usar si el tamaño importa. Lo que significa que si está ejecutando un programa donde la memoria es un problema, entonces printf es en realidad una solución muy buena y bajo evaluador. Cout esencialmente cambia los bits para hacer espacio para la cadena, mientras que printf solo toma algún tipo de parámetros y lo imprime en la pantalla. Si compilara un simple programa hello world, printf podría compilarlo en menos de 60.000 bits en lugar de cout, tomaría más de 1 millón de bits compilar.

Para su situación, id sugiere usar cout simplemente porque es mucho más conveniente de usar. Aunque, yo diría que printf es algo bueno para saber.

 2
Author: howard howard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-17 05:51:09

La razón principal es probablemente que una cadena de C++ es una estructura que incluye un valor de longitud actual, no solo la dirección de una secuencia de caracteres terminada por un byte 0. Printf y sus parientes esperan encontrar tal secuencia, no una estructura, y por lo tanto se confunden con cadenas de C++.

Hablando por mí mismo, creo que printf tiene un lugar que no se puede llenar fácilmente con características sintácticas de C++, al igual que las estructuras de tablas en html tienen un lugar que no se puede llenar fácilmente con divs. Como Dykstra escribió más tarde sobre el goto, no tenía la intención de iniciar una religión y en realidad solo estaba argumentando en contra de usarlo como un kludge para compensar el código mal diseñado.

Sería muy bueno que el proyecto GNU añadiera la familia printf a sus extensiones g++.

 1
Author: MMacD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-15 20:36:06

printf acepta un número variable de argumentos. Estos solo pueden tener tipos de Datos Antiguos (POD). El código que pasa cualquier cosa que no sea POD a printf solo compila porque el compilador asume que tienes el formato correcto. %s significa que el argumento respectivo se supone que es un puntero a un char. En su caso es un std::string no const char*. printf no lo sabe porque el tipo de argumento se pierde y se supone que debe restaurarse desde el parámetro format. Al convertir ese argumento std::string en const char* el puntero resultante apuntará a alguna región irrelevante de la memoria en lugar de la cadena C deseada. Por esa razón su código imprime galimatías.

Mientras que printf es una excelente opción para imprimir texto formateado, (especialmente si tiene la intención de tener relleno), puede ser peligroso si no ha activado las advertencias del compilador. Siempre habilite las advertencias porque entonces errores como este son fácilmente evitables. No hay razón para usar el torpe mecanismo std::cout si la familia printf puede hacer la misma tarea de una manera mucho más rápida y bonita. Solo asegúrate de haber activado todas las advertencias (-Wall -Wextra) y estarás bien. En caso de que utilice su propia implementación personalizada printf debe declararla con el mecanismo __attribute__ que habilita al compilador para verificar la cadena de formato contra los parámetros proporcionados.

 1
Author: Hyena,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-18 10:52:03