Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Séquence des élèments d'un vector

36 réponses
Avatar
hyronn
Bonsoir,

Est-on assur=E9 que les =E9l=E9ments d'un std::vector se suivent en m=E9moi=
re?
Je veux utiliser un vector pour me d=E9charger de l'allocation dynamique
t'un tableau temporaire de caract=E8res.

#include <cstdio>
#include <vector>

void parse1(const char* const txt_buffer)
{
vector<char> str;
str.reserve(FILENAME_MAX);
sscanf(txt_buffer, " %*s \" %s ", (char*) &str[0] );
}

Ca marche sous VC++ 2003.
Mais est-ce standard et/ou portable?

J'ai aussi essay=E9 ceci, mais =E7a ne marche pas dans tous les cas et
=E9clanche parfois des exceptions. Je soupsonne l'impl=E9mentation de ne
pas vraiment effectuer le reserve().

void parse2(const char* const txt_buffer)
{
string s;
s.reserve(FILENAME_MAX);
sscanf(txt_buffer, " %*s \" %s ", const_cast<char*>(s.data()) );
}

Rq: je ne peux pas me permettre d'utiliser les iostreams pour des
raisons de performance. La fonction parser doit interpr=E9ter des
fichiers de plusieurs centaines de ko de texte.

Merci

10 réponses

1 2 3 4
Avatar
Fabien LE LEZ
On 17 Mar 2007 11:44:23 -0700, "" :

Est-on assuré que les éléments d'un std::vector se suivent en mémoire?


Oui.

Depuis assez récemment, c'est garanti par la norme.
Avant, c'était à peu près garanti aussi, car il était impossible
d'implémenter std::vector<> autrement.



J'ai aussi essayé ceci, mais ça ne marche pas dans tous les cas

[...] const_cast [...]


Faut pas chercher plus loin la source du problème...

Et je confirme que dans ton cas, c'est std::vector<char> qu'il faut
utiliser.

Avatar
Loïc Joly
void parse1(const char* const txt_buffer)
{
vector<char> str;
str.reserve(FILENAME_MAX);
sscanf(txt_buffer, " %*s " %s ", (char*) &str[0] );
}

Ca marche sous VC++ 2003.


J'en suis bien surpris... Après ce code, str.size() vaut encore 0. Il
faut remplacer le reserve par resize. Avec le problème que dans ce cas,
on initialise forcément la zone mémoire en question, pour rien.

--
Loïc

Avatar
Mathias Gaunard
Bonsoir,

Est-on assuré que les éléments d'un std::vector se suivent en mémoire?
Je veux utiliser un vector pour me décharger de l'allocation dynamique
t'un tableau temporaire de caractères.

#include <cstdio>
#include <vector>

void parse1(const char* const txt_buffer)
{
vector<char> str;
str.reserve(FILENAME_MAX);
sscanf(txt_buffer, " %*s " %s ", (char*) &str[0] );
}


Ce serait plutôt

void parse1(const char* txt_buffer);
{
vector<char> str(FILENAME_MAX);
sscanf(txt_buffer, " %*s " %s ", &str[0] );
}

1) le cast est inutile. Les casts étant une chose dangereuse à éviter
(particulièrement les casts C), si en plus c'est inutile, c'est quand
même con d'en mettre.

2) std::vector ne garantit rien pour l'accès à la mémoire réservée.
Il te faut des vrais objets.
Donc n'utilise pas reserve mais resize. Ici, on peut tout simplement
utiliser le constructeur

3) FILENAME_MAX est probablement une constante fixe du préprocesseur.
Par conséquent, pourquoi ne pas utiliser un tableau normal sur la pile ?

4) Ta fonction ne fait rien d'utile avec str.

Avatar
Mathias Gaunard

void parse1(const char* const txt_buffer)


Ah oui, et le second const est sans intérêt, puisque les arguments sont
passés ici par valeur.

Avatar
hyronn
2) std::vector ne garantit rien pour l'accès à la mémoire réserv ée.
Il te faut des vrais objets.
Donc n'utilise pas reserve mais resize. Ici, on peut tout simplement
utiliser le constructeur
ok


3) FILENAME_MAX est probablement une constante fixe du préprocesseur.
Par conséquent, pourquoi ne pas utiliser un tableau normal sur la pile ?
Je m'attendais à ce que FILENAME_MAX (de <cstdio>) soit plus grand que

la pile.
Un pile usuelle fait dans les 8k et MAX_PATH (constante windowsienne
similaire) fait 32k
Je craignais un stack overflow.
Mais sur mon implementation (VC++2003) FILENAME_MAX = 260
Donc la pile fera l'affaire.
Mais question portabilité, c bof..
Auriez-vous l'aimabilité de rapporter la valeur de FILENAME_MAX sur
vos implémentation ;-) Merci


4) Ta fonction ne fait rien d'utile avec str.
C'était un exemple.


Avatar
hyronn
void parse1(const char* const txt_buffer)


Ah oui, et le second const est sans intérêt, puisque les arguments so nt
passés ici par valeur.


Je ne comprend pas.
J'indique au compilateur que txt_buffer ne sera pas modifié afin qu'il
puisse effectuer une optimisation dont je n'ai peut être pas
connaissance.
Quel est le pb?
Merci


Avatar
hyronn
On 17 mar, 21:39, Loïc Joly wrote:

void parse1(const char* const txt_buffer)
{
vector<char> str;
str.reserve(FILENAME_MAX);
sscanf(txt_buffer, " %*s " %s ", (char*) &str[0] );
}

Ca marche sous VC++ 2003.


J'en suis bien surpris... Après ce code, str.size() vaut encore 0. Il
faut remplacer le reserve par resize. Avec le problème que dans ce cas,
on initialise forcément la zone mémoire en question, pour rien.


Juste par chance en fait!
Reserve() ou le constructeur a bien alloué de la mémoire.
J'écris sur cette emplacement, sans invoquer les procédures du
vector,
il est donc normal que size() == 0.

Je suis conscient du gaspillage de mémoire. L'ordre de grandeur est de
33k, ce qui est convenable au vu de mon "cahier des charges".


Avatar
James Kanze
On Mar 17, 7:44 pm, "" wrote:

Est-on assuré que les éléments d'un std::vector se suivent en mém oire?
Je veux utiliser un vector pour me décharger de l'allocation dynamique
t'un tableau temporaire de caractères.

#include <cstdio>
#include <vector>

void parse1(const char* const txt_buffer)
{
vector<char> str;
str.reserve(FILENAME_MAX);
sscanf(txt_buffer, " %*s " %s ", (char*) &str[0] );
}

Ca marche sous VC++ 2003.


Ça ne marche pas du tout. Reserve() ne change pas la taille du
vecteur. Ce qu'il te faut, c'est resize(), non réserve.

Mais est-ce standard et/ou portable?


Avec resize(), oui. Et tu n'as pas besoin du cast pour le
dernier parametre.

J'ai aussi essayé ceci, mais ça ne marche pas dans tous les cas et
éclanche parfois des exceptions. Je soupsonne l'implémentation de ne
pas vraiment effectuer le reserve().

void parse2(const char* const txt_buffer)
{
string s;
s.reserve(FILENAME_MAX);
sscanf(txt_buffer, " %*s " %s ", const_cast<char*>(s.data()) );
}


La norme n'exige pas que reserve() fasse quoique ce soit dans le
cas de std::string. Elle n'exige pas (encore) non plus que le
pointeur renvoyé par data() correspond au contenu de la chaîne.
Dans la pratique, si tu utilises resize() à la place de
reserve(), ça doit marcher avec toutes les implémentations
existantes. Il y a une proposition devant le comité (acceptée en
principe, je crois) pour exiger la contiguïté du buffer dans
basic_string aussi, et de fournir une version non-const de
data().

Rq: je ne peux pas me permettre d'utiliser les iostreams pour des
raisons de performance. La fonction parser doit interpréter des
fichiers de plusieurs centaines de ko de texte.


Et qu'est-ce qui te dit que sscanf soit plus performant ? Ça
doit dépendre beaucoup de l'implémentation.

Si c'est vraiment une question de performance, strchr, etc. sur
la chaîne initiale est probablement ce qui serait la plus
rapide. Ensuite, la construction des chaînes dont tu as besoin
à partir des pointeurs trouvés.

--
James Kanze (Gabi Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
James Kanze
On Mar 18, 9:54 am, "" wrote:
void parse1(const char* const txt_buffer)


Ah oui, et le second const est sans intérêt, puisque les arguments sont
passés ici par valeur.


Je ne comprend pas.
J'indique au compilateur que txt_buffer ne sera pas modifié afin qu'il
puisse effectuer une optimisation dont je n'ai peut être pas
connaissance.
Quel est le pb?


Aucun problème, mais il apporte en fait assez peu, vue qu'il ne
conditionne qu'une variable locale, dont tous les accès sont
visibles, et par le compilateur, et par le lecteur.

Son utilisation est, à mon avis, une question de style. La
déclaration de la fonction (que voit les autres utilisateurs) ne
doit pas le comporter -- là, il ne signifie rien. Alors,
certains préfère que les paramètres dans la définition
s'écrivent de façon exactement identiques à ceux de la
déclaration, quitte à rénouncer à l'avantage d'une const locale.
D'autres préfère les avantages du const, même au prix que la
déclaration et la définition apparaissent légèrement
différentes.

Personnellement, je n'utilise pas le const dans ce cas-ci, mais
comme j'ai dit, c'est vraiment une question de style, et je ne
rejetterais certainement pas du code dans une revue de code pour
cette raison. Si tu te sens plus à l'aise avec le const, il n'y
a aucune raison de changer.

--
James Kanze (Gabi Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



Avatar
Mathias Gaunard

Un pile usuelle fait dans les 8k


Une pile usuelle de 1980 ?
Un compilateur pourrait a priori détecter, modulo l'usage de la
récursivité, de quelle taille la pile doit être et t'avertir dans le cas
où la pile nécessaire serait trop grande.

et MAX_PATH (constante windowsienne
similaire) fait 32k


Un chemin et un nom de fichier c'est pas exactement la même chose.


Auriez-vous l'aimabilité de rapporter la valeur de FILENAME_MAX sur
vos implémentation ;-)


Le plus long nom possible que j'ai trouvé en regardant les
caractéristiques des systèmes de fichiers c'est 4032 octets.

Mais 255 est quand même une valeur récurrente.

1 2 3 4