En tant que programmeur C ++, j'ai parfois besoin de faire affaire avec des tampons de mémoire à l'aide de techniques de C. Par exemple:
TCHAR buffer[MAX_PATH+1]; // edit: +1 added ::GetCurrentDirectory(sizeof(buffer)/sizeof(TCHAR), &buffer[0]);
& tampon [0] code> meilleur style de programmation que de passer tampon code>? (Je préfère & tampon [0] code>.) Li>
- Y a-t-il une taille maximale considérée comme sûre pour les tampons alloués à la pile?
- Mise à jour: i> Je veux dire, par exemple, la valeur la plus élevée pouvant être considérée comme sûre pour les applications de bureau multiplate-forme sur Mac, Windows, Linux Desktops (pas mobile!). LI>
ul> li>
- est un tampon statique (
tampon de charme statique [N]; code>) plus rapidement? Y a-t-il d'autres arguments pour ou contre cela? LI>
- Lorsque vous utilisez des tampons statiques, vous pouvez utiliser le type de retour
const char * code>. Est-ce (généralement) une bonne ou une mauvaise idée? (Je me rends compte que l'appelant devra faire sa propre copie pour éviter que l'appel suivant modifierait la valeur de retour précédente.) LI>
- Qu'en est-il d'utiliser
Statique Char * tampon = Nouveau Char [N]; Code>, Ne jamais supprimer le tampon et la réutiliser sur chaque appel. Li>
- Je comprends que l'allocation de tas doit être utilisée lorsque (1) traite avec de gros tampons ou (2) la taille maximale de la mémoire tampon est inconnue au moment de la compilation. Existe-t-il d'autres facteurs qui jouent dans la décision de la pile / de l'allocation de tas? LI>
- Si vous préférez le
sprintf_s code>, memcpy_s code>, ... des variantes? (Visual Studio a essayé de me convaincre de cela depuis longtemps, mais je veux un deuxième avis: P) Li>
ul> p>
9 Réponses :
vecteur code>, vous auriez besoin de faire et de tampon [0] Code> Quoi qu'il en soit. Li>
- dépend de votre plate-forme prévue. LI>
- est-ce important? Avez-vous décidé que ce soit un problème? Écrivez le code qui est le plus facile à lire et à entretenir avant de vous inquiéter si vous pouvez l'obscurcer dans quelque chose de plus rapidement. Mais pour ce que cela vaut la peine, l'allocation sur la pile est très rapide (vous venez de changer la valeur du pointeur de la pile.) Li>
- Vous devriez utiliser
std :: string code>. Si la performance devient un problème, vous pourrez réduire les allocations dynamiques en retournant simplement le tampon interne. Mais l'interface std :: string code> est bien plus agréable et plus sûre, et la performance est votre dernière préoccupation. LI>
- c'est une fuite de mémoire. Beaucoup diront que ça va, puisque le système d'exploitation est de toute façon, mais je pense que c'est une pratique terrible pour simplement fuir les choses. Utilisez un
std :: vecteur code>, vous devez jamais em> faire une allocation brute! Si vous vous mettez dans une position où vous risquez de fuir (car cela doit être fait explicitement), vous le faites mal. Li>
- Je pense que votre (1) et (2) vient de le couvrir. L'allocation dynamique est presque toujours plus lente que la répartition des piles, mais vous devriez être plus préoccupé par le sens de votre situation. LI>
- Vous ne devriez pas utiliser celles du tout. Utilisez
std :: chaîne code>, std :: stringstream code>, std :: copie code>, etc. li>
ul> J'oublie toujours que je peux utiliser un std :: Vecteur A Création d'un tampon avec Malloc / Nouveau. Merci de me rappeler :) J'accepte que les constructions C ++ doivent être préférées, mais j'aime faire une exception pour Sprintf / Printf. Je trouve plus élégant que de jeter mon code avec des opérateurs de flux.
@Stact: Vous devez utiliser Boost.Format puis, ou envelopper des choses dans une fonction comme boost :: lexical_cast code>.
Vous avez beaucoup de questions! Je ferai de mon mieux pour répondre à un couple et vous donner un endroit pour rechercher les autres. P>
Y a-t-il une taille maximale considérée comme sûre pour les tampons de pile alloués? P> blockQuote>
Oui, mais la taille de la pile elle-même varie en fonction de la plate-forme sur laquelle vous travaillez. Voir Quand vous souciez-vous de la taille de la pile? pour une question très similaire . p>
est un tampon de charme statique [N]; plus rapide? Sommes il y a d'autres arguments pour ou contre cela? p> blockQuote>
Le sens de statique dépend de l'endroit où le tampon est déclaré, mais je suppose que vous parlez d'un
statique code> déclaré dans une fonction, il est donc initialisé une seule fois. Dans les fonctions appelées plusieurs fois, l'utilisation de tampons statiques peut être une bonne idée de prévenir le débordement de la pile, mais autrement gardez à l'esprit que l'allocation de tampons est une opération bon marché. En outre, les tampons statiques sont beaucoup plus difficiles à travailler lorsque vous traitez avec plusieurs threads. P>Pour réponses à la plupart de vos autres questions, voir Grands tampons VS Grands tampons statiques, y a-t-il un avantage? . P>
Éloignez-vous des tampons statiques si vous souhaitez utiliser votre code à nouveau. p> li>
Utilisez snprintf () au lieu de sprintf () afin que vous puissiez contrôler des dépassements tampons. P> li>
Vous ne savez jamais combien d'espace de pile est laissé dans le contexte de votre appel - aucune taille n'est donc techniquement «sûre». Vous avez beaucoup de poussée pour jouer avec la plupart du temps. Mais cela vous fera de bonnes. J'utilise une règle de base pour ne jamais mettre des tableaux sur la pile. P> li>
Demandez au client le tampon et le transmettez-le et sa taille à votre fonction. Cela le rend à nouveau entrant et ne laisse aucune ambiguïté quant à qui doit gérer la vie du tampon. P> Li>
Si vous traitez des données de chaîne, vérifiez deux fonctions de chaîne pour vous assurer qu'ils se terminent surtout lorsqu'ils touchent la fin du tampon. La bibliothèque C est très incompatible lorsqu'il s'agit de manipuler la résiliation des chaînes dans les différentes fonctions. P> LI> ol>
Ne peut pas être d'accord sur # 3. Apprenez à connaître votre code! Sans tampons de pile-alloués, vous devez toujours utiliser un mutex dans une application multi-threadée, et j'essaie personnellement aussi fort que possible pour éviter le mutex ou, en d'autres termes, écrivez un code sans verrouillage. Performance garantie. Si vous ne connaissez pas le comportement de votre application, vous êtes dans un problème (:
@Poni: Le verrouillage de Mutex n'est pas la seule alternative aux tampons de pile lors de la réintensif. Vous pouvez passer des tampons à partir de l'appelant, utilisez directement le tas ou utilisez des pools de tampons de tas pré-alloués.
Pourtant, tant que vous accédez à la même collection (que le tas est à la fin de la journée) de manière multi-filetée, vous aurez besoin de synchroniser. (Presque) aucun moyen de l'éviter.
@Poni: La propriété tampon n'a rien à voir avec le partage des données entre les threads. Si un contexte de fonction nécessite un tampon unique pour effectuer son travail, cela n'a pas d'importance d'un point de vue de synchronisation s'il provient de l'appelant, du tas ou de la pile. Si vous partageiez des tampons de pile entre les threads (mauvais pratique, mais certains le font), vous aurez toujours besoin de synchronisation. Si chaque fil est passé dans son propre tampon unique mais que je ne l'ai pas partagé, aucune synchronisation n'est nécessaire.
Vous avez pris le sujet dans une direction complète qui n'est pas pertinente pour le poste d'origine. Je vais le mettre dans ces mots: votre fonction doit lire 1 000 octets d'un fichier. À la fin de cette fonction, vous n'avez pas besoin de ces données. Où alloueriez-vous un tampon pour la lecture?
@Poni: OP parlait de créer un tampon sur la pile pour une utilisation locale. Mon point n ° 3 est qu'une fonction ne peut pas dire à l'avance si elle va épuiser la pile du fil d'appel lorsque cela le faisait. L'origine de ces 1 000 octets devrait être du tas ou un pointeur tampon transmis de l'appelant si vous souhaitez minimiser l'empreinte automatique variable.
Du tas! @ ?! ... Vous devez confuser mon ami!
@Amardeep: Techniquement parlant, si la pile est pleine, tout objet local i> peut trop déborder, peu importe la taille. Mais je conviens que plus l'objet local est grand, plus le débordement est de plus de chances.
code pour la pile dynamique / tampon de tas: p>
Passe le tampon comme tampon et [0] meilleur style de programmation que de passer tampon? (Je préfère et solutions tampons [0].) blockQuote>
& tampon [0] code> rend le code moins lisible pour moi. Je dois faire une pause pour une seconde et je me demande pourquoi quelqu'un a utilisé au lieu de simplement passer tamponcode>. Parfois, vous devez utiliseret solutions tampons [0] code> (si le tamponcode> est unstd :: vector code>), mais sinon, bâton avec le style standard C . p>Y at-il une taille maximale qui est considéré comme sûr pour les tampons de pile alloué? blockQuote>Je doute qu'il y ait une limite pratique, aussi longtemps que vous utilisez la pile raisonnablement. Je ne l'ai jamais eu aucun problème dans mon développement. P>
Si je lis MSDN a > correctement, les discussions sur Windows par défaut 1 Mo de taille de la pile. Ceci est configurable. D'autres plates-formes ont d'autres limites. P>
Est-tampon statique char [N]; plus rapide? Y at-il d'autres arguments pour ou contre? blockQuote>D'une part, il pourrait réduire la nécessité d'engager des pages de mémoire pour la pile, de sorte que votre application peut courir plus vite. D'autre part, en allant au segment BSS ou équivalent pourrait réduire la localité de cache par rapport à la pile, de sorte que votre application peut fonctionner plus lentement. Je doute sérieusement que vous auriez remarqué la différence dans les deux cas. P>
Utilisation
statique code> ne sont pas threadsafe, tout en utilisant la pile est. C'est un énorme avantage pour la pile. (Même si vous ne pensez pas que vous allez multithread, pourquoi rendre la vie plus difficile que si des changements dans l'avenir?) P>Lorsque vous utilisez des tampons statiques vous pouvez avoir votre retour de la fonction du const char * type de retour. Est-ce une bonne idée? (Je me rends compte que l'appelant devra faire sa propre copie pour éviter que le prochain appel changerait la valeur de retour précédente.) blockQuote>Const correct est toujours une bonne chose. < / p>
Le retour des pointeurs vers des tampons statiques est sujette à l'erreur; un appel plus tard pourrait le modifier, un autre thread peut modifier, utiliser
std :: string code> au lieu ou tout autre lieu mémoire allouée automatique (même si vos besoins fonctionnels pour traiter en interne avec des tampons char tels que votreGetCurrentDirectory code> par exemple.) p>Qu'en est-il en utilisant char statique * buffer = new char [N]; et ne jamais supprimer le tampon? (Réutilisation le même tampon chaque appel.) blockQuote>Moins efficace que d'utiliser simplement
char buffer statique [N] code>, puisque vous avez besoin d'une allocation de tas. P>Je comprends que l'allocation de tas doit être utilisé lorsque (1) traitant de grands tampons ou (2) la taille de la mémoire tampon maximale est inconnue au moment de la compilation. Y a-t-il d'autres facteurs qui jouent dans la pile / décision d'attribution tas? blockQuote>Voir la réponse de Justin Ardini. P>
Si vous préférez les sprintf_s, memcpy_s, ... Des variantes? (Visual Studio a essayé de me convaincre de cela depuis longtemps, mais je veux un deuxième avis: p) blockQuote>Ceci est une question de débat. Personnellement, je pense que ces fonctions sont une bonne idée, et si vous ciblez de Windows exclusivement, alors il y a un certain avantage à prendre l'approche de Windows préférée et l'utilisation de ces fonctions. (Et ils sont assez simples à réimplémentez si vous avez besoin plus tard pour cible autre chose que Windows, aussi longtemps que vous ne comptez pas sur leur comportement de gestion des erreurs). D'autres pensent que les fonctions CRT sécurisés sont plus sûrs que correctement utilisé C et introduire d'autres inconvénients; liens Wikipédia à quelques arguments contre eux. p>
Cela dépend des normes de codage. Je préfère personnellement: Cela dépend de la taille de la pile. Si la quantité de pile nécessaire à votre tampon dépasse le montant disponible sur la pile, il résulte d'un Stack-Overflow < /a>. Oui, il devrait être plus rapide. Je ne sais pas quels statiques signifie dans ce cas mais: < / p> Si la variable est déclarée sur pile ( Si la variable est déclarée sous forme statique C'est une possibilité, bien que non recommandée car il rend votre code non réentrant . P> La taille de la pile du thread en marche est trop petite pour s'adapter à la déclaration de pile (mentionnée précédemment). P> Si vous voulez que votre code soit portable: Non. En créant une macro portable est assez faible dans ce cas: p> passe le tampon comme et tampon [0] Meilleur style de programmation que le tampon de passage? (Je préfère et tampon [0].) Code> tampon + index code> au lieu de & tampon [index] code> mais c'est une question de goût. P> y a-t-il une taille maximale qui est considéré comme sûr pour la pile des tampons alloués? code> p>
> Tampon de charme statique [N]; plus rapide? Y a-t-il d'autres arguments pour ou contre cela? Code> P> Voir aussi cette question: Est-il une mauvaise pratique de déclarer une fonction matrice moyenne strik> p> lorsque vous utilisez des tampons statiques, vous pouvez avoir votre retour de fonction dispose du type de retour de Cons-Char *. Est-ce une bonne idée? (Je me rends compte que l'appelant devra faire sa propre copie pour éviter que l'appel suivant modifierait la valeur de retour précédente.) Code> p>
char buf [100] code>):
Vous devriez statique code> il rendra votre code non réentrant. Strtok est un exemple dans ce cas. P> li>
ol> Qu'en est-il de l'utilisation de la mémoire tampon Static Char * = Nouveau Char [N]; et ne jamais supprimer le tampon? (Réutiliser le même tampon chaque appel.) Code> p> Je comprends que l'allocation de tas doit être utilisée lorsque (1) traitant de gros tampons ou (2 ) La taille maximale de la mémoire tampon est inconnue au moment de la compilation. Y a-t-il d'autres facteurs qui jouent dans la décision de la pile / de l'allocation de tas? Code> p> Devriez-vous préférer le sprintf_s, memcpy_s, ... Variantes? (Visual Studio a essayé de me convaincre de cela pendant une longue période, mais je veux une seconde opinion: p) code> p>
Votre idée de MCAO pour la portabilité ne fonctionnera pas, car la plupart des versions _s prennent des arguments supplémentaires.
Si une fonction vous donne une méthode de savoir combien de caractères il retournera, utilisez-le. Votre exemple GetCurrentDirectory est un bon exemple:
std::vector<TCHAR> buffer(length, 0); // assert(buffer.capacity() >= length); // should always be true GetCurrentDirectory(length, &buffer[0]);
Je suppose que votre intérêt vient principalement d'une perspective de performance, car des solutions telles que le vecteur, la chaîne, le wstring, etc. fonctionneront généralement même pour interagir avec C API. Je recommande d'apprendre à utiliser ceux-ci et comment les utiliser efficacement. Si vous en avez vraiment besoin, vous pouvez même écrire votre propre allocateur de mémoire pour les rendre super rapides. Si vous êtes sûr qu'ils ne sont sûrs qu'ils ne sont pas ce dont vous avez besoin, il n'y a toujours aucune excuse pour que vous n'écrivez pas à ne pas écrire une simple enveloppe pour gérer ces tampons de cordes avec Raii pour les cas dynamiques. P>
avec ça hors de la route: p>
passe le tampon comme et tampon [0] Meilleur style de programmation que de passer amortir? (Je préfère et tampon [0].) P> blockQuote>
non. Je considérerais que ce style sera légèrement moins utile (certes être subjectif ici) car vous ne pouvez pas l'utiliser pour passer un tampon NULL et qu'il faudrait donc faire des exceptions à votre style pour passer des pointeurs à des tableaux qui peuvent être nuls. Il est requis si vous passez dans des données de STD :: Vecteur vers une API C S'attendant à un pointeur, cependant. P>
Y a-t-il une taille maximale qui est considéré comme sûr pour la pile alloué tampons? p> blockQuote>
Cela dépend de vos paramètres de plate-forme et de votre compilateur. Règle simple: Si vous doutez de savoir si votre code déborde de la pile, écrivez-le d'une manière qui ne peut pas. P>
est un tampon statique (charcuterie statique tampon [n];) plus rapide? Y a-t-il autres arguments pour ou contre cela? P> blockQuote>
Oui, il y a un grand argument contre celui-ci, et c'est que cela rend votre fonction ne ré-entrant plus. Si votre application devient multipliée, ces fonctions ne seront pas en sécurité. Même dans une application à une seule-filetée, partager le même tampon lorsque ces fonctions sont appelées de manière récursive peut entraîner des problèmes. P>
Qu'en est-il de l'utilisation du tampon de char * statique = nouveau char [n]; et ne jamais supprimer le tampon? (Réutiliser le même tampon chacun appel.) p> blockQuote>
Nous avons toujours les mêmes problèmes avec la réintensif. P>
Je comprends cette allocation de tas devrait être utilisé quand (1) traitant de grands tampons ou (2) tampon maximum la taille est inconnue au moment de la compilation. Sommes Il y a d'autres facteurs qui jouent dans La décision de la pile / de l'allocation de tas? p> blockQuote>
La pile déroulant détruit les objets sur la pile. Ceci est particulièrement important pour la sécurité des exceptions. Ainsi, même si vous allouez la mémoire sur le tas dans une fonction, elle devrait généralement être gérée par un objet sur la pile (ex: pointeur intelligent). //// @ Voir Raii. P>
Si vous préférez le sprintf_s, memcpy_s, ... Variantes? (Visual Studio a essayé de me convaincre de cela Pendant longtemps, mais je veux une seconde AVIS: P) P> blockQuote>
MS a eu raison à propos de ces fonctions étant des alternatives plus sûres, car elles n'ont pas de problèmes de dépassement de tampon, mais si vous écrivez ce code comme (sans écriture de variantes pour d'autres plates-formes), votre code sera marié à Microsoft puisque elle être non portable. p>
Lorsque vous utilisez des tampons statiques, vous pouvez utiliser Type de retour Const de caractères *. Est-ce (généralement) une bonne ou une mauvaise idée? (JE se rend compte que l'appelant aura besoin faire sa propre copie pour éviter que le appel suivant changerait le précédent Valeur de retour.) p> blockQuote>
Je dirais dans presque tous les cas, vous souhaitez utiliser Const Char * pour les types de retour pour une fonction renvoyant un pointeur sur un tampon de caractères. Pour une fonction de retourner un caractère mutable * est généralement déroutant et problématique. Soit il renvoie une adresse à des données globales / statiques qu'il ne devrait pas utiliser en premier lieu (voir Recettentrance ci-dessus), les données locales d'une classe (s'il s'agit d'une méthode) dans laquelle le renvoi de la capacité de la classe à Maintenir les invariants en permettant aux clients de l'altérer, mais ils aiment (ex: la chaîne stockée doivent toujours être valides) ou la mémoire de retour spécifiée par un pointeur transmise à la fonction (le seul cas où l'on pourrait raisonnablement affirmer que le charnel mutable * devrait être retourné). p>
1) 2) Les limites de la taille de la pile dépendront de votre plate-forme. Pour la plupart des fonctions simples, ma règle de base personnelle est quelque chose de plus de ~ 256kb est déclarée dynamiquement; Il n'y a pas de véritable rime ni raison de ce nombre, cependant, c'est juste ma propre convention et il est actuellement dans les tailles de pile par défaut pour toutes les plates-formes que je développe. P>
3) Les tampons statiques ne sont pas plus rapides ou plus lents (à toutes fins utiles). La seule différence est le mécanisme de contrôle d'accès. Le compilateur place généralement des données statiques dans une section distincte du fichier binaire que des données non statiques, mais il n'existe aucun avantage notable / performance significatif ou pénalité impliqué. Le seul moyen réel de dire certitude est d'écrire le programme à la fois et de les utiliser (car de nombreux aspects de vitesse impliqués ici dépendent de votre plate-forme / compilateur). P>
4) Ne renvoyez pas un pointeur 5) La réutilisation d'un tampon peut entraîner une amélioration des performances des plus gros tampons dues à contourner la surcharge impliquée dans l'appelant 6) Un autre facteur d'allocation de pile / de tas est de la scoraison. Une variable de pile dépasse de la portée lorsque la fonction qu'il vit en retour, mais une variable allouée de manière dynamique sur le tas peut être renvoyée à l'appelant en toute sécurité ou accédée à la prochaine fois que la fonction est appelée (A strtok ). P>
7) Je recommanderais à l'utilisation de tampon code> et & tampon [0] code> devrait être équivalent. P>
const code> si l'appelant devra le modifier (qui défait le point de const code>). Utilisez const code> pour les paramètres de fonction et les types de retour si et uniquement s'ils ne sont pas conçus pour être modifiés. Si l'appelant devra modifier la valeur, votre meilleur pari est que l'appelant passe la fonction un pointeur sur un tampon pré-alloué (ainsi que la taille du tampon) et pour la fonction d'écrire les données dans ce tampon. p>
Malloc code> / ou neuf < / code> / Supprimer code> à chaque fois. Cependant, vous risquez d'utiliser des données anciennes si vous oubliez d'effacer le tampon à chaque fois ou que vous essayez d'exécuter deux copies de la fonction en parallèle. Encore une fois, la seule façon réelle de savoir, bien sûr, c'est d'essayer les deux manières et de mesurer la durée pendant laquelle le code prend pour courir. P>
sprintf_s code>, memcpy_s code> et amis. Ils ne font pas partie de la bibliothèque standard et ne sont pas portables. Plus vous utilisez ces fonctions, plus vous aurez de travail supplémentaire lorsque vous souhaitez exécuter votre code sur une autre plate-forme ou utiliser un compilateur différent. P>
Bien sûr, le commentaire dans N ° 6 ne s'applique pas aux variables de pile déclarées comme statique code>.
Quel est le problème avec Boost :: System?
Vous avez oublié quelques lignes:
nom d'utilisateur.Resize (1024, 'A'); nom d'utilisateur.insert (0, "World of Buffer Overflows!"); code>