web-dev-qa-db-ja.com

ヌル終了文字列とは何ですか?

std :: string とはどう違いますか?

22
lhj7362

ヌル終了文字列は連続した文字列であり、最後の文字列はバイナリビットパターンがすべてゼロです。 「通常の文字列」の意味がわかりませんが、std::stringを意味する場合、std::stringは不要です( C++ 11まで )隣接している必要があり、ターミネータを持っている必要はありません。また、std::stringの文字列データは、それを含むstd::stringオブジェクトによって常に割り当てられ、管理されます。 nullで終わる文字列の場合、そのようなコンテナは存在せず、通常、ベアポインタを使用してそのような文字列を参照および管理します。

これらはすべて、まともなC++テキストブックで実際に取り上げる必要があります。最高の1つである Accelerated C++ を入手することをお勧めします。

41
anon

「文字列」は、実際にはcharsの単なる配列です。 nullで終わる文字列とは、null文字'\0'が文字列の終わり(必ずしも配列の終わりではない)を示すものです。コード内のすべての文字列(二重引用符""で区切られている)は、コンパイラによって自動的にヌルで終了します。

たとえば、"hi"{'h', 'i', '\0'}と同じです。

68
Ricket

文字列を表すには、主に2つの方法があります。

1)ASCII null(nul)文字、0、最後にある文字のシーケンス。ターミネータを検索することで、その長さを知ることができます。これはヌル終端と呼ばれます。文字列、または場合によってはヌル文字で終了します。

2)文字のシーケンスに加えて、長さを示す別のフィールド(整数の長さ、または文字列の末尾へのポインター)。

「通常の文字列」についてはわかりませんが、特定の言語について話すとき、「文字列」という言葉はその言語の標準表現を意味するために使用されることがよくあります。したがって、Javaでは、Java.lang.Stringはタイプ2の文字列であるため、「文字列」とはその意味です。 Cでは、「文字列」はおそらくタイプ1の文字列を意味します。標準は正確であるために非常に冗長ですが、人々は常に「明白な」ものを省きたいと思っています。

C++では、残念ながら両方のタイプが標準です。 std :: stringはタイプ2の文字列[*]ですが、Cから継承された標準ライブラリ関数はタイプ1の文字列を操作します。

[*]実際、std :: stringは多くの場合、文字の配列として実装され、個別の長さフィールドand nulターミネーターを持ちます。これにより、c_str()関数を、文字列データをコピーまたは再割り当てする必要なく実装できます。長さフィールドを保存せずにstd :: stringを実装することが合法かどうかをすぐに思い出すことはできません。標準で要求される複雑さの保証は何ですか。一般的なコンテナの場合、size()はO(1)であることが推奨されますが、実際には必須ではありません。したがって、たとえそれが正当であっても、nul-terminatorsを使用するだけのstd :: stringの実装は驚くでしょう。

16
Steve Jessop
'\0' 

ASCIIコード0の文字、ヌルターミネータ、ヌル文字、[〜#〜] nul [〜#〜][〜#〜] c [〜#〜]言語では、文字列の終わりを示すために使用される予約文字として機能します。 strcpy、strlen、strcmpなどの標準関数はこれに依存しますが、そうでない場合は[〜#〜] nul [〜#〜] 、文字列の終わりを知らせる別の方法を使用する必要があります。

これにより、1バイトのオーバーヘッドのみで、文字列を任意の長さにすることができます。カウントを保存する代わりに、255の文字列長制限または1バイト以上のオーバーヘッドが必要です。

from 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から取得します。

ご覧のとおり、違いは重要です。

6
4pie0

ヌル終了文字列は、文字列の終わりがヌル文字の出現によって定義されることを意味します(すべてのビットはゼロです)。

「その他の文字列」自分の長さを保存する必要があります。

3
Dario

ヌル終了文字列は、Cのネイティブ文字列形式です。たとえば、文字列リテラルは、ヌル終了文字列として実装されます。その結果、多くのコード(最初はCランタイムライブラリ)は、文字列がnullで終了していると想定しています。

1
Seva Alekseyev

Nullで終了する文字列(c-string)はcharの配列であり、配列の最後の要素は0x0値です。 std :: stringは、値の自動サイズ変更コンテナであるという点で、本質的にベクトルです。サイズ変更の必要性を知るためにサイズを追跡する必要があるため、nullターミネータは必要ありません。

正直なところ、私は標準文字列よりもC文字列の方が好きです。基本ライブラリにはより多くのアプリケーションがあり、コードと割り当てが最小限であり、そのため使用が難しくなっています。

0
phyrrus9