Comment diffère-t-il de std :: string ?
Une chaîne terminée par un caractère nul est une séquence contiguë de caractères, dont le dernier contient le modèle de bit binaire tout en zéros. Je ne suis pas sûr de comprendre ce que vous entendez par "chaîne habituelle", mais si vous parlez de std::string
, alors un std::string
n'est pas requis ( jusqu'à ce que C++ 11 ) soit contigu, et n'est pas obligé d'avoir un terminateur. . De plus, les données de chaîne d'un std::string
sont toujours allouées et gérées par l'objet std::string
qui les contient; pour une chaîne terminée par un caractère null, il n'existe aucun conteneur de ce type et vous faites généralement référence à ces chaînes et les gérez à l'aide de pointeurs nus.
Tout cela devrait vraiment être couvert dans n'importe quel manuel C++ décent - Je recommande de mettre la main sur Accelerated C++ , l'un des meilleurs d'entre eux.
Une "chaîne" est en réalité juste un tableau de char
s; une chaîne terminée par un caractère nul est celle où un caractère nul '\0'
marque la fin de la chaîne (pas nécessairement la fin du tableau). Toutes les chaînes dans le code (délimitées par des guillemets doubles ""
) sont automatiquement terminées par zéro par le compilateur.
Ainsi, par exemple, "hi"
est identique à {'h', 'i', '\0'}
.
Il y a deux manières principales de représenter une chaîne:
1) Une séquence de caractères avec un caractère ASCII nul (nul), 0, à la fin. Vous pouvez dire combien de temps il est en recherchant le terminateur. Cela s'appelle une chaîne à zéro terminal, ou parfois à zéro.
2) Une séquence de caractères, plus un champ séparé (soit une longueur entière, soit un pointeur vers la fin de la chaîne), pour vous indiquer sa longueur.
Je ne suis pas sûr de la "chaîne habituelle", mais ce qui arrive assez souvent est que, lorsque l'on parle d'une langue particulière, le mot "chaîne" désigne la représentation standard de cette langue. Donc, en Java, Java.lang.String est une chaîne de type 2, c'est donc ce que "chaîne" signifie. En C, "chaîne" signifie probablement une chaîne de type 1. La norme est assez verbeuse pour être précis, mais les gens veulent toujours laisser de côté ce qui est "évident".
En C++, malheureusement, les deux types sont standard. std :: string est une chaîne de type 2 [*], mais les fonctions de bibliothèque standard héritées de C fonctionnent sur des chaînes de type 1.
[*] En fait, std :: string est souvent implémenté sous la forme d'un tableau de caractères, avec un champ de longueur séparé et un terminateur nul. Ainsi, la fonction c_str()
peut être implémentée sans avoir à copier ou à réallouer les données de chaîne. Je ne me souviens pas immédiatement s'il est légal d'implémenter std :: string sans stocker de champ de longueur: la question est de savoir quelles garanties de complexité sont requises par la norme. Pour les conteneurs en général, size()
est recommandé comme étant O (1), mais n'est pas réellement requis. Ainsi, même si cela est légal, une implémentation de std :: string utilisant uniquement des terminateurs nuls serait surprenante.
'\0'
est un caractère ASCII avec le code 0, un terminateur nul, un caractère nul, NUL. En langage C, il sert de caractère réservé utilisé pour signifier la fin d'une chaîne. De nombreuses fonctions standard telles que strcpy, strlen, strcmp, entre autres, en dépendent. Sinon, s'il n'y avait pas de NUL, un autre moyen de signaler la fin de chaîne doit avoir été utilisé:
Cela permet à la chaîne d'être n'importe quelle longueur avec seulement le surdébit d'un octet; l’alternative consistant à stocker un compte nécessite soit une chaîne limite de longueur de 255 ou un temps système de plus d'un octet.
de wikipedia
C++std::string
suit cette autre convention et ses données sont représentées par une structure appelée _Rep
:
// _Rep: string representation
// Invariants:
// 1. String really contains _M_length + 1 characters: due to 21.3.4
// must be kept null-terminated.
// 2. _M_capacity >= _M_length
// Allocated memory is always (_M_capacity + 1) * sizeof(_CharT).
// 3. _M_refcount has three states:
// -1: leaked, one reference, no ref-copies allowed, non-const.
// 0: one reference, non-const.
// n>0: n + 1 references, operations require a lock, const.
// 4. All fields==0 is an empty string, given the extra storage
// beyond-the-end for a null terminator; thus, the shared
// empty string representation needs no constructor.
struct _Rep_base
{
size_type _M_length;
size_type _M_capacity;
_Atomic_Word _M_refcount;
};
struct _Rep : _Rep_base
{
// Types:
typedef typename _Alloc::template rebind<char>::other _Raw_bytes_alloc;
// (Public) Data members:
// The maximum number of individual char_type elements of an
// individual string is determined by _S_max_size. This is the
// value that will be returned by max_size(). (Whereas npos
// is the maximum number of bytes the allocator can allocate.)
// If one was to divvy up the theoretical largest size string,
// with a terminating character and m _CharT elements, it'd
// look like this:
// npos = sizeof(_Rep) + (m * sizeof(_CharT)) + sizeof(_CharT)
// Solving for m:
// m = ((npos - sizeof(_Rep))/sizeof(CharT)) - 1
// In addition, this implementation quarters this amount.
static const size_type _S_max_size;
static const _CharT _S_terminal;
// The following storage is init'd to 0 by the linker, resulting
// (carefully) in an empty string with one reference.
static size_type _S_empty_rep_storage[];
static _Rep&
_S_empty_rep()
{
// NB: Mild hack to avoid strict-aliasing warnings. Note that
// _S_empty_rep_storage is never modified and the punning should
// be reasonably safe in this case.
void* __p = reinterpret_cast<void*>(&_S_empty_rep_storage);
return *reinterpret_cast<_Rep*>(__p);
}
bool
_M_is_leaked() const
{ return this->_M_refcount < 0; }
bool
_M_is_shared() const
{ return this->_M_refcount > 0; }
void
_M_set_leaked()
{ this->_M_refcount = -1; }
void
_M_set_sharable()
{ this->_M_refcount = 0; }
void
_M_set_length_and_sharable(size_type __n)
{
#ifndef _GLIBCXX_FULLY_DYNAMIC_STRING
if (__builtin_expect(this != &_S_empty_rep(), false))
#endif
{
this->_M_set_sharable(); // One reference.
this->_M_length = __n;
traits_type::assign(this->_M_refdata()[__n], _S_terminal);
// grrr. (per 21.3.4)
// You cannot leave those LWG people alone for a second.
}
}
_CharT*
_M_refdata() throw()
{ return reinterpret_cast<_CharT*>(this + 1); }
_CharT*
_M_grab(const _Alloc& __alloc1, const _Alloc& __alloc2)
{
return (!_M_is_leaked() && __alloc1 == __alloc2)
? _M_refcopy() : _M_clone(__alloc1);
}
// Create & Destroy
static _Rep*
_S_create(size_type, size_type, const _Alloc&);
void
_M_dispose(const _Alloc& __a)
{
#ifndef _GLIBCXX_FULLY_DYNAMIC_STRING
if (__builtin_expect(this != &_S_empty_rep(), false))
#endif
if (__gnu_cxx::__exchange_and_add_dispatch(&this->_M_refcount,
-1) <= 0)
_M_destroy(__a);
} // XXX MT
void
_M_destroy(const _Alloc&) throw();
_CharT*
_M_refcopy() throw()
{
#ifndef _GLIBCXX_FULLY_DYNAMIC_STRING
if (__builtin_expect(this != &_S_empty_rep(), false))
#endif
__gnu_cxx::__atomic_add_dispatch(&this->_M_refcount, 1);
return _M_refdata();
} // XXX MT
_CharT*
_M_clone(const _Alloc&, size_type __res = 0);
};
les données réelles peuvent être obtenues avec:
_Rep* _M_rep() const
{ return &((reinterpret_cast<_Rep*> (_M_data()))[-1]); }
cet extrait de code provient du fichier basic_string.h
qui se trouve sur ma machine dans le usr/include/c++/4.4/bits/basic_string.h
Comme vous pouvez le constater, la différence est significative.
Une chaîne terminée par un zéro signifie que la fin de votre chaîne est définie par l'occurrence d'un caractère nul (tous les bits sont à zéro).
"Autres chaînes", par exemple doivent stocker leur propre longueur.
Une chaîne à terminaison null est un format de chaîne natif en C. Les littéraux de chaîne, par exemple, sont implémentés avec la terminaison NULL. En conséquence, de nombreux codes (la bibliothèque d'exécution C au début) supposent que les chaînes sont terminées par un caractère null.
Une chaîne terminée par un caractère nul (c-string) est un tableau de caractères, le dernier élément du tableau étant une valeur 0x0. Std :: string est essentiellement un vecteur, en ce sens qu'il s'agit d'un conteneur à redimensionnement automatique pour les valeurs. Il n'a pas besoin de terminateur null car il doit garder une trace de la taille pour savoir quand un redimensionnement est nécessaire.
Honnêtement, je préfère les chaînes de caractères aux chaînes standard, elles ont simplement plus d'applications dans les bibliothèques de base, celles avec un code et des affectations minimaux et plus difficiles à utiliser pour cette raison.