Je crois comprendre que string
c'est un membre de l' std
espace de noms, alors pourquoi ce qui suit se produit-il?
#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;
}
Chaque fois que le programme s'exécute, myString
imprime une chaîne apparemment aléatoire de 3 caractères, comme dans la sortie ci-dessus.
Réponses:
Il est compilé car il
printf
n'est pas de type sûr, car il utilise des arguments variables au sens C 1 .printf
n'a pas d'option pourstd::string
, seulement une chaîne de style C. Utiliser autre chose à la place de ce à quoi il s'attend ne vous donnera certainement pas les résultats que vous souhaitez. C'est en fait un comportement indéfini, donc tout peut arriver.Le moyen le plus simple de résoudre ce problème, puisque vous utilisez C ++, est de l'imprimer normalement avec
std::cout
, car lestd::string
prend en charge via la surcharge d'opérateurs:Si, pour une raison quelconque, vous devez extraire la chaîne de style C, vous pouvez utiliser la
c_str()
méthode destd::string
pour obtenir uneconst char *
terminaison par null. En utilisant votre exemple:Si vous voulez une fonction similaire
printf
, mais de type sûr, regardez dans les modèles variadiques (C ++ 11, pris en charge sur tous les principaux compilateurs à partir de MSVC12). Vous pouvez en trouver un exemple ici . Il n'y a rien que je sache d'implémentation comme ça dans la bibliothèque standard, mais il pourrait y en avoir dans Boost, en particulierboost::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 avec des informations de type codées comme la%d
significationint
. 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 d'avertir lorsque vous mentez.la source
Veuillez ne pas utiliser
printf("%s", your_string.c_str());
Utilisez
cout << your_string;
plutôt. Court, simple et sûr. En fait, lorsque vous écrivez du C ++, vous voulez généralement éviterprintf
complètement - c'est un reste de C qui est rarement nécessaire ou utile en C ++.Quant à savoir pourquoi vous devriez utiliser à la
cout
place deprintf
, les raisons sont nombreuses. Voici un échantillon de quelques-uns des plus évidents:printf
n'est pas sûr de type. Si le type que vous passez diffère de celui donné dans le spécificateur de conversion,printf
essaiera d'utiliser tout ce qu'il trouve sur la pile comme s'il s'agissait du type spécifié, donnant un comportement non défini. Certains compilateurs peuvent avertir à ce sujet dans certaines circonstances, mais certains compilateurs ne le peuvent pas / ne le feront pas du tout, et aucun ne le peut dans toutes les circonstances.printf
n'est pas extensible. Vous ne pouvez lui transmettre 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'ajouter plus / others. La plupart des C ++ bien écrits devraient utiliser ces types principalement pour implémenter des types orientés vers le problème en cours de résolution.Cela rend le formatage décent beaucoup plus difficile. Pour un exemple évident, lorsque vous imprimez des nombres pour que les gens les lisent, vous voulez généralement insérer des milliers de séparateurs tous les quelques chiffres. Le nombre exact de chiffres et les caractères utilisés comme séparateurs varient, mais cela
cout
est également couvert. Par exemple:Le paramètre régional sans nom (le "") choisit un paramètre régional en fonction de la configuration de l'utilisateur. Par conséquent, sur ma machine (configurée pour l'anglais américain), cela s'imprime au format
123,456.78
. Pour quelqu'un qui a son ordinateur configuré pour (par exemple) l'Allemagne, il imprimerait quelque chose comme123.456,78
. Pour quelqu'un qui l'a configuré pour l'Inde, il serait imprimé comme1,23,456.78
(et bien sûr il y en a beaucoup d'autres). Avecprintf
j'obtenir exactement un résultat:123456.78
. C'est cohérent, mais c'est toujours faux pour tout le monde partout. Essentiellement, la seule façon de contourner ce problème est de faire le formatage séparément, puis de transmettre le résultat sous forme de chaîne àprintf
, carprintf
lui - même ne fera tout simplement pas le travail correctement.printf
chaînes de format peuvent être assez illisibles. Même parmi les programmeurs C qui utilisentprintf
pratiquement tous les jours, je suppose qu'au moins 99% auraient besoin de regarder les choses pour être sûr de ce que signifie#
in%#x
, et en quoi cela diffère de ce que signifie#
in%#f
(et oui, ils signifient des choses complètement différentes ).la source
#include <string>
. VC ++ a quelques bizarreries dans ses en-têtes qui vous permettront de définir une chaîne, mais pas de l'envoyercout
, sans inclure l'en-<string>
tête.cout
plus lent, c'est parce que vous avez utiliséstd::endl
là où vous ne devriez pas.utilisez
myString.c_str()
si vous voulez qu'une chaîne de type c (const char*
) à utiliser avec printfMerci
la source
Utilisez l'exemple std :: printf et c_str ():
la source
La raison principale est probablement qu'une chaîne C ++ est une structure qui inclut une valeur de longueur actuelle, pas seulement l'adresse d'une séquence de caractères terminée par un octet 0. Printf et ses parents s'attendent à trouver une telle séquence, pas une structure, et sont donc confus par les chaînes C ++.
Pour ma part, je crois que printf a une place qui ne peut pas être facilement remplie par les fonctionnalités syntaxiques C ++, tout comme les structures de table en html ont une place qui ne peut pas être facilement remplie par les divs. Comme Dykstra l'a écrit plus tard à propos du goto, il n'avait pas l'intention de créer une religion et ne faisait en réalité que s'opposer à son utilisation comme un kludge pour compenser un code mal conçu.
Ce serait plutôt bien si le projet GNU ajoutait la famille printf à leurs extensions g ++.
la source
Printf est en fait assez bon à utiliser si la taille compte. Cela signifie que si vous exécutez un programme où la mémoire est un problème, alors printf est en fait une très bonne solution et sous-évaluateur. Cout déplace essentiellement les bits pour faire de la place pour la chaîne, tandis que printf prend juste une sorte de paramètres et les imprime à l'écran. Si vous deviez compiler un simple programme hello world, printf serait capable de le compiler en moins de 60 000 bits par opposition à cout, il faudrait plus d'un million de bits à compiler.
Pour votre situation, id suggère d'utiliser cout simplement parce qu'il est beaucoup plus pratique à utiliser. Cependant, je dirais que printf est quelque chose de bon à savoir.
la source
printf
accepte un nombre variable d'arguments. Ceux-ci ne peuvent avoir que des types POD (Plain Old Data). Code qui transmet autre chose que POD àprintf
la compilation uniquement car le compilateur suppose que vous avez le bon format.%s
signifie que l'argument respectif est supposé être un pointeur vers achar
. Dans votre cas, c'est unstd::string
nonconst char*
.printf
ne le sait pas car le type d'argument est perdu et est censé être restauré à partir du paramètre format. Lorsque vous transformez cetstd::string
argument enconst char*
pointeur résultant, vous pointez vers une région de mémoire non pertinente au lieu de la chaîne C souhaitée. Pour cette raison, votre code imprime du charabia.Bien que ce
printf
soit un excellent choix pour imprimer du texte formaté (surtout si vous avez l'intention d'avoir un remplissage), cela peut être dangereux si vous n'avez pas activé les avertissements du compilateur. Activez toujours les avertissements, car de telles erreurs sont facilement évitables. Il n'y a aucune raison d'utiliser lestd::cout
mécanisme maladroit si laprintf
famille peut faire la même tâche d'une manière beaucoup plus rapide et plus jolie. Assurez-vous simplement que vous avez activé tous les avertissements (-Wall -Wextra
) et tout ira bien. Si vous utilisez votre propreprintf
implémentation personnalisée, vous devez la déclarer avec le__attribute__
mécanisme qui permet au compilateur de vérifier la chaîne de format par rapport aux paramètres fournis .la source