c++ - sirve - ¿Qué es una cadena terminada en nulo?
para que sirve string en c++ (7)
¿En qué se diferencia de std::string ?
Hay dos formas principales de representar una cadena:
1) Una secuencia de caracteres con un carácter nulo ASCII (nulo), 0, al final. Puedes saber cuánto tiempo está buscando el terminador. Esto se llama una cadena terminada en nulo, o algunas veces terminada en nulo.
2) Una secuencia de caracteres, más un campo separado (ya sea una longitud entera o un puntero al final de la cadena), para decirle qué tan larga es.
No estoy seguro de la "cadena habitual", pero lo que sucede a menudo es que cuando se habla de un idioma en particular, la palabra "cadena" se usa para significar la representación estándar de ese idioma. Así que en Java, java.lang.String es una cadena de tipo 2, así que eso es lo que significa "cadena". En C, "cadena" probablemente significa una cadena de tipo 1. El estándar es bastante detallado para ser preciso, pero la gente siempre quiere dejar de lado lo que es "obvio".
En C ++, desafortunadamente, ambos tipos son estándar. std :: string es una cadena de tipo 2 [*], pero las funciones de biblioteca estándar heredadas de C operan en cadenas de tipo 1.
[*] En realidad, std :: string a menudo se implementa como una matriz de caracteres, con un campo de longitud independiente y un terminador nulo. Eso es para que la función c_str()
pueda implementarse sin necesidad de copiar o reasignar los datos de cadena. No puedo recordar si es legal implementar std :: string sin almacenar un campo de longitud: la pregunta es qué complejidad garantiza el estándar. Para los contenedores en general, se recomienda que el size()
sea O (1), pero en realidad no se requiere que lo sea. Entonces, incluso si es legal, una implementación de std :: string que solo usa terminadores nul sería sorprendente.
Una "cadena" es realmente una matriz de caracteres; una cadena terminada en nulo es aquella en la que un carácter nulo ''/0''
marca el final de la cadena (no necesariamente el final de la matriz). Todas las cadenas en el código (delimitadas por comillas dobles ""
) son automáticamente terminadas en cero por el compilador.
Entonces, por ejemplo, "hi"
es lo mismo que {''h'', ''i'', ''/0''}
.
Una cadena terminada en nulo (cadena-c) es una matriz de caracteres, y el último elemento de la matriz es un valor 0x0. Std :: string es esencialmente un vector, ya que es un contenedor de cambio de tamaño para valores. No necesita un terminador nulo, ya que debe realizar un seguimiento del tamaño para saber cuándo se necesita un cambio de tamaño.
Honestamente, prefiero c-strings sobre std, solo tienen más aplicaciones en las bibliotecas básicas, las que tienen un mínimo de código y asignaciones, y las más difíciles de usar debido a eso.
Una cadena terminada en nulo es un formato de cadena nativo en C. Los literales de cadena, por ejemplo, se implementan como terminados en nula. Como resultado, una gran cantidad de código (para comenzar con la biblioteca C en tiempo de ejecución) asume que las cadenas terminan en nulo.
Una cadena terminada en nulo significa que el final de su cadena se define a través de la aparición de un carácter nulo (todos los bits son cero).
"Otras cuerdas", por ejemplo, tienen que almacenar su propia longitud.
Una cadena terminada en nulo es una secuencia contigua de caracteres, la última de las cuales tiene el patrón de bits binario todos ceros. No estoy seguro de lo que quieres decir con una "cadena habitual", pero si te refieres a std::string
, entonces no se requiere que std::string
( hasta C ++ 11 ) sea contiguo, y no se requiere que tenga un terminador. Además, los datos de cadena de std::string
siempre son asignados y administrados por el objeto std::string
que lo contiene; para una cadena terminada en nulo, no hay tal contenedor, y normalmente se hace referencia a estas cadenas y se las administra mediante punteros simples.
Todo esto debería estar realmente cubierto en cualquier libro de texto decente de C ++. Recomiendo obtener Accelerated C ++ , uno de los mejores de ellos.
''/0''
es un carácter ASCII con código 0, terminador nulo, carácter nulo, NUL . En lenguaje C sirve como un carácter reservado que se usa para indicar el final de una cadena. Muchas funciones estándar, como strcpy, strlen, strcmp, entre otras, se basan en esto. De lo contrario, si no hubiera un NUL , se debe haber utilizado otra forma de indicar el final de la cadena:
Esto permite que la cadena tenga cualquier longitud con solo la sobrecarga de un byte; la alternativa de almacenar un conteo requiere un límite de longitud de cadena de 255 o una sobrecarga de más de un byte.
de wikipedia
C ++ std::string
sigue esta otra convención y sus datos están representados por una estructura llamada _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);
};
los datos reales se pueden obtener con:
_Rep* _M_rep() const
{ return &((reinterpret_cast<_Rep*> (_M_data()))[-1]); }
este fragmento de código proviene del archivo basic_string.h
que en mi máquina se encuentra en usr/include/c++/4.4/bits/basic_string.h
Entonces, como puedes ver, la diferencia es significativa.