2010-01-10 5 views

ответ

36

Строка с завершающим нулевым символом является непрерывной последовательностью символов, последний из которых имеет бинарный битовый шаблон всех нулей. Я не уверен, что вы подразумеваете под «обычной строкой», но если вы имеете в виду std::string, то std::string не требуется (until C++11) быть смежным и не требует наличия терминатора. Кроме того, строковые данные std::string всегда выделяются и управляются объектом std::string, который содержит его; для строки с нулевым завершением нет такого контейнера, и вы обычно ссылаетесь на такие строки и управляете такими строками с помощью простых указателей.

Все это должно действительно быть покрыто любым приличным учебником на C++ - я рекомендую получить Accelerated C++, один из лучших из них.

+4

В принципе, обнуленный байт определяет длину символьной строки в C. – Costique

+0

Это просто, thx! – lhj7362

+0

Последний символ не должен иметь битовую схему всех нулей, он просто должен иметь * значение * 0. – avakar

2

Строка с нулевым символом означает, что конец вашей строки определяется путем вхождения нулевого символа (все биты равны нулю).

"Другие строки", например. должны хранить свою длину.

46

«Строка» - это просто массив из char s; строка с нулевым завершением - это строка, где нулевой символ '\0' обозначает конец строки (необязательно конец массива). Все строки кода (разделенные двойными кавычками "") автоматически завершаются нулевым компилятором.

Так, например, "hi" - это то же самое, что и {'h', 'i', '\0'}.

+4

Путь лучше понять, чем принятый ответ. +1 – Mike

+1

Возможно, стоит упомянуть, что компиляторы ищут нулевой символ для определения длины строки. –

+0

У меня есть строка temp, и я сохранил a, b, c как temp [0], temp [1] и temp [2]. Теперь, когда я делаю «cout << temp», он не дает - «abc». Что мне делать? Я узнал, что '\ 0' также не работает в качестве ограничителя строк. –

1

Строка с нулевым символом является родным строковым форматом в C. Строковые литералы, например, реализованы как завершающие нуль. В результате, весь код (C-библиотека времени выполнения для начала) предполагает, что строки имеют нулевое завершение.

14

Есть два основных способа представления строки:

1) Последовательность символов с ASCII нуль (NUL) характер, 0, в конце концов. Вы можете сказать, как долго это происходит, ища терминатор. Это называется строкой, завершающей нуль, или иногда nul-terminated.

2) Последовательность символов, а также отдельное поле (целая длина или указатель на конец строки), чтобы рассказать вам, как долго это происходит.

Я не уверен в «обычной строке», но часто случается, что при разговоре о конкретном языке слово «строка» используется для обозначения стандартного представления для этого языка. Таким образом, в Java, java.lang.String является строкой типа 2, так что это означает «строка». В C «строка», вероятно, означает строку типа 1. Стандарт достаточно подробный, чтобы быть точным, но люди всегда хотят отказаться от того, что «очевидно».

В C++, к сожалению, оба типа являются стандартными. std :: string - это строка типа 2 [*], но стандартные функции библиотеки, унаследованные от C, работают с строками типа 1.

[*] На самом деле, std :: string часто реализуется как массив символов с отдельным полем длины и nul terminator. Это значит, что функция c_str() может быть реализована без необходимости копирования или перераспределения строковых данных. Я не могу вспомнить, вне зависимости от того, законно ли реализовать std :: string, не сохраняя поле длины: вопрос в том, какие гарантии сложности требуются стандартом. Для контейнеров вообще size() рекомендуется быть O (1), но на самом деле не требуется.Поэтому, даже если это законно, реализация std :: string, которая просто использует nul-terminators, была бы удивительной.

6
'\0' 

является ASCII символ с кодом 0, нулевой символ, нулевой символ, NUL. В языке C он служит зарезервированным символом, обозначающим конец строки. Многие стандартные функции, такие как strcpy, strlen, strcmp среди других, полагаются на это. В противном случае, если не было NUL, другой способ, чтобы сигнализировать конец строки должен быть использован:

Это позволяет строке быть любой длиной с только над головой одного байта ; альтернатива хранения счетчика требует либо строки предел длины 255, либо накладных расходов более одного байта.

от wikipedia

C++std::string следует этой другой конвенции и ее данные представлены структурой под названием _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); 
     }; 

фактических данных могут быть получены с:

_Rep* _M_rep() const 
     { return &((reinterpret_cast<_Rep*> (_M_data()))[-1]); } 

этот фрагмент кода приходит из файла basic_string.h, который на моей машине находится в usr/include/c++/4.4/bits/basic_string.h

Так как вы можете видеть, разница существенна.

0

Строка с нулевым завершением (c-string) - это массив символов char, а последний элемент массива - значение 0x0. Std :: string - это, по сути, вектор, поскольку он является контейнером для автоматического изменения размеров. Он не нуждается в нулевом терминаторе, поскольку он должен отслеживать размер, чтобы знать, когда требуется изменить размер.

Честно говоря, я предпочитаю c-strings над std, у них просто больше приложений в базовых библиотеках, те с минимальным кодом и распределениями, и из-за этого сложнее использовать.