Si j'ai bien compris, string
est un membre de l'espace de noms std
, alors pourquoi la situation suivante se produit-elle ?
#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;
}
![entrer la description de l'image ici][2]
Chaque fois que le programme s'exécute, myString
imprime une chaîne de 3 caractères apparemment aléatoire, comme dans la sortie ci-dessus.
Il compile parce que printf
n'est pas sûr au niveau du type, puisqu'il utilise des arguments variables au sens du C1. printf
n'a pas d'option pour std::string
, seulement une chaîne de style C. Utiliser quelque chose d'autre à la place de ce qu'il attend ne vous donnera certainement pas les résultats que vous souhaitez. Il s'agit en fait d'un comportement indéfini, donc tout peut arriver.
La façon la plus simple de résoudre ce problème, puisque vous utilisez le C++, est d'imprimer la chaîne normalement avec std::cout
, puisque std::string
le supporte grâce à la surcharge des opérateurs :
std::cout << "Follow this command: " << myString;
Si, pour une raison quelconque, vous avez besoin d'extraire la chaîne de style C, vous pouvez utiliser la méthode c_str()
de std::string
pour obtenir un const char *
qui est terminé par un caractère nul. En utilisant votre exemple :
#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 vous voulez une fonction qui ressemble à printf
, mais sans danger pour le type, regardez dans les modèles variadiques (C++11, supporté par tous les compilateurs majeurs à partir de MSVC12). Vous pouvez en trouver un exemple [ici] (https://web.archive.org/web/20131018185034/http://www.generic-programming.org/~dgregor/cpp/variadic-templates.html). A ma connaissance, il n'y a pas d'implémentation de ce type dans la bibliothèque standard, mais il pourrait y en avoir une dans Boost, spécifiquement boost::format
.
[1] : Cela signifie que vous pouvez passer n'importe quel nombre d'arguments, mais la fonction compte sur vous pour lui indiquer le nombre et les types de ces arguments. Dans le cas de printf
, cela signifie une chaîne de caractères avec des informations de type codées comme %d
qui signifie int
. Si vous mentez sur le type ou le nombre, la fonction n'a aucun moyen standard de le savoir, bien que certains compilateurs aient la capacité de vérifier et de donner des avertissements lorsque vous mentez.
N'utilisez pas printf("%s" ;, your_string.c_str());
.
Utilisez plutôt cout << your_string;
. C'est court, simple et sûr. En fait, lorsque vous écrivez en C++, vous voulez généralement éviter complètement printf
- c'est un vestige du C qui est rarement nécessaire ou utile en C++.
Quant à savoir pourquoi vous devriez utiliser cout
au lieu de printf
, les raisons sont nombreuses. Voici un échantillon de quelques-unes des plus évidentes :
Comme le montre la question, printf
n'est pas sûr du type. Si le type que vous passez diffère de celui donné dans le spécificateur de conversion, printf
essaiera d'utiliser ce qu'il trouve sur la pile comme si c'était le type spécifié, donnant un comportement non défini. Certains compilateurs peuvent avertir de ce problème dans certaines circonstances, mais certains compilateurs ne le peuvent pas/ne le veulent pas du tout, et aucun ne le peut dans toutes les circonstances.
`printf' n'est pas extensible. Vous ne pouvez lui passer que des types primitifs. L'ensemble des spécificateurs de conversion qu'il comprend est codé en dur dans son implémentation, et il n'y a aucun moyen pour vous d'en ajouter d'autres. La plupart des C++ bien écrits devraient utiliser ces types principalement pour implémenter des types orientés vers le problème à résoudre.
Il rend le formatage décent beaucoup plus difficile. Pour prendre un exemple évident, lorsque vous imprimez des chiffres pour que les gens les lisent, vous voulez généralement insérer des séparateurs de milliers tous les quelques chiffres. Le nombre exact de chiffres et les caractères utilisés comme séparateurs varient, mais cout
s'en charge également. Par exemple :
std::locale loc("" ;);
std::cout.imbue(loc) ;
std::cout << 123456.78 ;
La locale sans nom (le "" ;) choisit une locale en fonction de la configuration de l'utilisateur. Par conséquent, sur ma machine (configurée pour l'anglais américain), cela s'imprime comme 123,456.78
. Pour quelqu'un dont l'ordinateur est configuré pour (disons) l'Allemagne, cela s'imprimera comme 123.456,78
. Pour quelqu'un dont l'ordinateur est configuré pour l'Inde, il s'agirait de 1,23,456.78
(et bien sûr, il y en a beaucoup d'autres). Avec printf
, j'obtiens exactement un seul résultat : 123456.78
. C'est cohérent, mais c'est constamment faux pour tout le monde et partout. La seule façon de contourner ce problème est de faire le formatage séparément, puis de passer le résultat sous forme de chaîne à printf
, parce que printf
lui-même ne fera tout simplement pas le travail correctement.
Bien qu'elles soient assez compactes, les chaînes au format printf
peuvent être assez illisibles. Même parmi les programmeurs C qui utilisent printf
pratiquement tous les jours, je suppose qu'au moins 99% d'entre eux ont besoin de vérifier pour être sûrs de ce que signifie le #
de %#x
, et en quoi cela diffère de ce que signifie le #
de %#f
(et oui, ils signifient des choses complètement différentes).
Utilisez myString.c_str()
si vous voulez une chaîne de caractères de type c (const char*
) à utiliser avec printf.
Merci