2010-01-10 47 views

Trả lời

36

Một chuỗi null-chấm dứt là một chuỗi liền kề của các nhân vật, cuối cùng một trong số đó có các bit nhị phân mô hình tất cả các số không. Tôi không chắc chắn ý của bạn là "chuỗi thông thường", nhưng nếu bạn muốn nói là std::string thì không yêu cầu std::string (until C++11) để tiếp giáp và không bắt buộc phải có người kết thúc. Ngoài ra, dữ liệu chuỗi của std::string luôn được phân bổ và quản lý bởi đối tượng std::string có chứa nó; đối với một chuỗi đã kết thúc bằng null, không có vùng chứa nào như vậy, và bạn thường tham chiếu và quản lý các chuỗi như vậy bằng cách sử dụng các con trỏ trần.

Tất cả điều này thực sự nên được đề cập trong bất kỳ cuốn sách văn bản C++ nào - tôi khuyên bạn nên giữ lại Accelerated C++, một trong những điều tốt nhất của chúng.

+4

Về cơ bản, byte zeroed xác định độ dài của một chuỗi ký tự trong C. – Costique

+0

Thật dễ dàng, thx! – lhj7362

+0

Ký tự cuối cùng không cần phải có mẫu bit của tất cả các số không, nó chỉ đơn thuần phải có giá trị * * bằng 0. – avakar

2

Một chuỗi null-chấm dứt có nghĩa là kết thúc chuỗi của bạn được xác định thông qua sự xuất hiện của một null-char (tất cả các bit là zero).

"Chuỗi khác", ví dụ: phải lưu trữ chiều dài của riêng mình.

46

A "chuỗi" thực sự chỉ là một mảng của char s; một chuỗi null chấm dứt là một trong đó một ký tự null '\0' đánh dấu sự kết thúc của chuỗi (không nhất thiết là kết thúc của mảng). Tất cả các chuỗi trong mã (được phân cách bằng dấu ngoặc kép "") sẽ được trình biên dịch tự động vô hiệu hóa.

Ví dụ: "hi" giống với {'h', 'i', '\0'}.

+4

Cách tốt hơn để hiểu hơn câu trả lời được chấp nhận. +1 – Mike

+1

Có thể đáng nói đến là các trình biên dịch tìm kiếm ký tự null để xác định độ dài của chuỗi. –

+0

Tôi có một chuỗi tạm thời, và tôi đã lưu trữ a, b, c là temp [0], temp [1] và temp [2]. Bây giờ, khi tôi làm "cout << temp", nó không cho - "abc". Tôi nên làm gì? Tôi đã biết rằng '\ 0' cũng không hoạt động như trình kết thúc chuỗi ở đây. –

1

Một chuỗi null-chấm dứt là một định dạng chuỗi có nguồn gốc trong literals C. String, ví dụ, được thực hiện như null-chấm dứt. Kết quả là, toàn bộ rất nhiều mã (thư viện thời gian chạy C bắt đầu bằng) giả định rằng các chuỗi được kết thúc bằng null.

14

Có hai cách chính để đại diện cho một chuỗi:

1) Một chuỗi các ký tự có giá trị rỗng ASCII (nul) nhân vật, 0, ở cuối. Bạn có thể biết nó dài bao nhiêu bằng cách tìm kiếm terminator. Điều này được gọi là một chuỗi null chấm dứt, hoặc đôi khi nul-chấm dứt.

2) Một chuỗi ký tự, cộng với một lĩnh vực riêng biệt (hoặc chiều dài số nguyên, hoặc một con trỏ đến cuối chuỗi), để cho bạn biết nó là bao lâu.

Tôi không chắc chắn về "chuỗi thông thường", nhưng điều thường xảy ra là khi nói về một ngôn ngữ cụ thể, từ "chuỗi" được sử dụng để có nghĩa là biểu diễn chuẩn cho ngôn ngữ đó. Vì vậy, trong Java, java.lang.String là một chuỗi kiểu 2, do đó, đó là những gì "chuỗi" có nghĩa là. Trong C, "chuỗi" có thể có nghĩa là một chuỗi loại 1. Tiêu chuẩn khá rõ ràng để chính xác, nhưng mọi người luôn muốn bỏ qua những gì "hiển nhiên".

Trong C++, thật không may, cả hai loại đều là tiêu chuẩn. std :: string là một chuỗi kiểu 2 [*], nhưng các hàm thư viện chuẩn được kế thừa từ C hoạt động trên các chuỗi kiểu 1.

[*] Thực tế, std :: chuỗi thường được triển khai dưới dạng một mảng các ký tự, với trường độ dài riêng biệt một trình kết thúc nul. Đó là để các chức năng c_str() có thể được thực hiện mà không cần phải sao chép hoặc phân bổ lại dữ liệu chuỗi. Tôi không thể nhớ off-hand cho dù đó là hợp pháp để thực hiện std :: string mà không lưu trữ một lĩnh vực chiều dài: câu hỏi là những gì đảm bảo phức tạp được yêu cầu theo tiêu chuẩn. Đối với các thùng chứa nói chung, size() được đề xuất là O (1), nhưng thực sự không bắt buộc phải là.Vì vậy, ngay cả khi nó là hợp pháp, một thực hiện của std :: string mà chỉ sử dụng nul-terminators sẽ là đáng ngạc nhiên.

6
'\0' 

là một ký tự ASCII với mã 0, terminator null, kí tự null, NUL. Trong ngôn ngữ C, nó hoạt động như một ký tự dành riêng được sử dụng để biểu thị phần cuối của chuỗi. Nhiều chức năng tiêu chuẩn như strcpy, strlen, strcmp trong số những người khác dựa vào điều này. Ngược lại, nếu không có NUL, một cách khác để báo hiệu kết thúc chuỗi phải đã được sử dụng:

Điều này cho phép các chuỗi là bất kỳ chiều dài chỉ với chi phí của một byte; thay thế lưu trữ số yêu cầu hoặc là một giới hạn độ dài chuỗi dài 255 hoặc phí trên một byte.

từ wikipedia

C++std::string sau hội nghị khác này và dữ liệu của nó được thể hiện bằng một cấu trúc gọi là _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); 
     }; 

dữ liệu thực tế có thể đạt được với:

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

đoạn mã này xuất phát từ tập tin basic_string.h đó trên máy tính của tôi nằm ở usr/include/c++/4.4/bits/basic_string.h

Như bạn có thể thấy, sự khác biệt có ý nghĩa.

0

Một null chấm dứt chuỗi (c-string) là một mảng của char, và yếu tố cuối cùng của mảng là một giá trị 0x0. Chuỗi std :: về cơ bản là một vectơ, trong đó nó là một vùng chứa tự động thay đổi kích thước cho các giá trị. Nó không cần một terminator null vì nó phải theo dõi kích thước để biết khi nào cần thay đổi kích thước.

Thành thật mà nói, tôi thích c-strings hơn std, họ chỉ có nhiều ứng dụng trong thư viện cơ bản, những ứng dụng có mã và phân bổ tối thiểu và khó sử dụng hơn vì điều đó.

Các vấn đề liên quan