2010-01-10 16 views

Respuesta

36

Una cadena terminada en nulo es una secuencia contigua de caracteres, la última de las cuales tiene todos los ceros del patrón de bits binarios. No estoy seguro de lo que quiere decir con una "cadena habitual", pero si quiere decir std::string, entonces no es necesario un std::string (until C++11) para que sea contiguo y no se requiere un terminador. Además, los datos de cadena std::string siempre se asignan y administran mediante el objeto std::string que lo contiene; para una cadena terminada en nulo, no hay tal contenedor, y normalmente se refiere y administra tales cadenas usando punteros simples.

Todo esto debería estar cubierto en cualquier libro de texto C++ decente. Recomiendo apoderarse de Accelerated C++, uno de los mejores de ellos.

+4

Básicamente, el byte a cero determina la longitud de una cadena de caracteres en C. – Costique

+0

¡Es fácil, gracias! – lhj7362

+0

El último carácter no necesita tener el patrón de bits de todos los ceros, simplemente tiene que tener el * valor * de 0. – avakar

2

Una cadena con terminación nula significa que el final de la cadena se define mediante la aparición de un carácter nulo (todos los bits son cero).

"Otras cadenas", p. tiene que almacenar su propia duración.

46

Una "cadena" es realmente solo una matriz de char s; una cadena terminada en nulo es aquella donde un carácter nulo '\0' marca el final de la cadena (no necesariamente el final de la matriz). El compilador anula automáticamente todas las cadenas de código (delimitadas por comillas dobles "").

Entonces, por ejemplo, "hi" es lo mismo que {'h', 'i', '\0'}.

+4

Forma mejor de entender que la respuesta aceptada. +1 – Mike

+1

Puede valer la pena mencionar que los compiladores buscan el carácter nulo para determinar la longitud de la cadena. –

+0

Tengo una temperatura de cadena, y guardé a, b, c como temp [0], temp [1] y temp [2]. Ahora, cuando hago "cout << temp", no da - "abc". ¿Que debería hacer? Tengo que saber que '\ 0' tampoco funciona como terminador de cadena aquí. –

1

Una cadena con terminación nula es un formato de cadena nativo en C. Los literales de cadena, por ejemplo, se implementan como terminados en nulo. Como resultado, una gran cantidad de código (biblioteca de tiempo de ejecución de C, para empezar) asume que las cadenas tienen terminación nula.

14

Hay dos formas principales para representar una cadena:

1) Una secuencia de caracteres ASCII con un nulo (nulo) carácter, 0, al final. Puedes saber cuánto tiempo está buscando el terminador. Esto se llama una cadena terminada en nulo, o a 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 cuánto tiempo es.

No estoy seguro acerca de "cadena habitual", pero lo que sucede a menudo es que cuando se habla de un idioma en particular, la palabra "cadena" se usa para referirse a la representación estándar para ese idioma. Entonces 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 tipo 1. El estándar es bastante detallado para ser preciso, pero la gente siempre quiere dejar fuera 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 separado y un terminador nulo. Eso es para que la función c_str() se pueda implementar sin tener que copiar o volver a asignar los datos de cadena. No recuerdo de improviso si es legal implementar std :: string sin almacenar un campo de longitud: la pregunta es qué garantías de complejidad son requeridas por el estándar. Para contenedores en general size() se recomienda que 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 use terminadores nulos sería sorprendente.

6
'\0' 

es un carácter ASCII con el código 0, terminador nulo, carácter nulo, NUL. En C idioma que sirve como un carácter reservado utilizado para indicar el final de una cadena. Muchas funciones estándar tales como strcpy, strlen, strcmp entre otros se basan en esto. De lo contrario, si no había NUL, otra forma de señal de final de cadena debe haber sido utilizada:

Esto permite que la cadena a ser de cualquier longitud, con sólo la sobrecarga de uno byte; la alternativa de almacenar un conteo requiere una cadena límite de longitud 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á representado 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 de archivo basic_string.h, que en mi máquina se encuentra en usr/include/c++/4.4/bits/basic_string.h

Así como se puede ver, la diferencia es significativa.

0

Una cadena terminada nula (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 automático para valores. No necesita un terminador nulo, ya que debe hacer un seguimiento del tamaño para saber cuándo se necesita un cambio de tamaño.

Honestamente, prefiero las cadenas c sobre las estándar, simplemente tienen más aplicaciones en las bibliotecas básicas, las que tienen un código y asignaciones mínimos, y las más difíciles de usar debido a eso.

Cuestiones relacionadas