【问题标题】:What is a null-terminated string?什么是空终止字符串?
【发布时间】:2010-01-10 14:04:47
【问题描述】:

它与std::string 有何不同?

【问题讨论】:

  • 什么是“普通”字符串?
  • std::string 反正我已经得到答案了,谢谢。

标签: c++ null-terminated


【解决方案1】:

“字符串”实际上只是 chars 的数组;以空字符结尾的字符串是一个空字符'\0' 标记字符串结尾(不一定是数组结尾)的字符串。代码中的所有字符串(由双引号 "" 分隔)由编译器自动以空值结尾。

例如,"hi"{'h', 'i', '\0'} 相同。

【讨论】:

  • 比接受的答案更好理解。 +1
  • 值得一提的是,编译器会寻找空字符来确定字符串的长度。
  • 我有一个字符串 temp,我将 a、b、c 存储为 temp[0]、temp[1] 和 temp[2]。现在,当我执行“cout
  • @BhavukMathur 打开一个新问题并提供一个测试代码示例,届时有人会帮助您。评论是给 cmets 的。
  • 你找到答案了吗?
【解决方案2】:

以空字符结尾的字符串是一个连续的字符序列,其中最后一个字符的二进制位模式全为零。我不确定您所说的“常用字符串”是什么意思,但如果您的意思是std::string,那么std::string 不需要(until C++11)是连续的,也不需要有终止符。此外,std::string 的字符串数据始终由包含它的std::string 对象分配和管理;对于以 null 结尾的字符串,没有这样的容器,您通常使用裸指针来引用和管理此类字符串。

所有这些都应该包含在任何体面的 C++ 教科书中 - 我建议获取 Accelerated C++,其中最好的之一。

【讨论】:

  • 基本上,零字节决定了C中字符串的长度。
  • 最后一个字符不必有全零的位模式,它只需要有 value 为 0。
  • 我不知道有任何平台存在这种区别。请注意使用 null(或者更准确地说是 NUL)作为字符串终止符与 NULL 指针不同。
  • 我也不是。但是,标准对整数值的位模式几乎没有说明。
  • 你说 std::string 不需要有终止符。这是否意味着 mystring.c_str() 不能保证为空终止?
【解决方案3】:

表示字符串主要有两种方式:

1) 以 ASCII 空 (nul) 字符 0 结尾的字符序列。您可以通过搜索终止符来判断它有多长。这称为空终止字符串,有时也称为空终止。

2) 一个字符序列,加上一个单独的字段(整数长度,或者指向字符串末尾的指针),告诉你它有多长。

我不确定“通常的字符串”,但经常发生的是,在谈论特定语言时,“字符串”一词用于表示该语言的标准表示。所以在 Java 中,java.lang.String 是一个类型 2 的字符串,所以这就是“字符串”的意思。在 C 语言中,“string”可能表示类型 1 字符串。为了准确起见,该标准相当冗长,但人们总是希望省略“显而易见”的内容。

不幸的是,在 C++ 中,这两种类型都是标准的。 std::string 是类型 2 字符串[*],但从 C 继承的标准库函数对类型 1 字符串进行操作。

[*] 实际上,std::string 通常被实现为一个字符数组,带有一个单独的长度字段一个 nul 终止符。这样就可以实现c_str() 函数,而无需复制或重新分配字符串数据。我不记得在不存储长度字段的情况下实现 std::string 是否合法:问题是标准需要什么样的复杂性保证。对于一般容器size() 建议为 O(1),但实际上并不需要。因此,即使它是合法的,只使用 nul 终止符的 std::string 实现也会令人惊讶。

【讨论】:

    【解决方案4】:
    '\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]); }
    

    此代码 sn-p 来自文件 basic_string.h,在我的机器上位于 usr/include/c++/4.4/bits/basic_string.h

    如您所见,差异很大。

    【讨论】:

      【解决方案5】:

      以空字符结尾的字符串意味着字符串的结尾是通过出现空字符来定义的(所有位都为零)。

      “其他字符串”例如必须存储自己的长度。

      【讨论】:

        【解决方案6】:

        以空字符结尾的字符串是 C 中的原生字符串格式。例如,字符串文字被实现为以空字符结尾的字符串。结果,大量代码(首先是 C 运行时库)假定字符串是以空值结尾的。

        【讨论】:

          【解决方案7】:

          以空结尾的字符串(c-string)是一个字符数组,数组的最后一个元素是 0x0 值。 std::string 本质上是一个向量,因为它是值的自动调整大小的容器。它不需要空终止符,因为它必须跟踪大小才能知道何时需要调整大小。

          老实说,我更喜欢 c 字符串而不是标准字符串,它们只是在基本库中有更多应用程序,那些代码和分配最少的应用程序,因此更难使用。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-06-04
            • 2013-05-13
            • 2011-11-05
            相关资源
            最近更新 更多