char * so với std :: string trong c ++ [đóng]


81

Khi nào tôi nên sử dụng std::stringvà khi nào tôi nên sử dụng char*để quản lý mảng chars trong C ++?

Có vẻ như bạn nên sử dụng char*nếu hiệu suất (tốc độ) là quan trọng và bạn sẵn sàng chấp nhận một số công việc kinh doanh rủi ro vì quản lý bộ nhớ.

Có các kịch bản khác để xem xét không?

Câu trả lời:


56

Bạn có thể chuyển chuỗi std :: bằng tham chiếu nếu chúng lớn để tránh sao chép hoặc một con trỏ đến cá thể, vì vậy tôi không thấy bất kỳ lợi thế thực sự nào khi sử dụng con trỏ char.

Tôi sử dụng std :: string / wstring cho nhiều hơn hoặc ít hơn mọi thứ là văn bản thực tế. char *Tuy nhiên, rất hữu ích cho các loại dữ liệu khác và bạn có thể chắc chắn rằng nó được phân bổ theo đúng cách. Nếu không thì std :: vector là cách để đi.

Có lẽ có những ngoại lệ cho tất cả những điều này.


8
có sự khác biệt về hiệu suất trước cả hai?
vtd-xml-author

3
@ vtd-xml-author: Một số có thể. Straight char *hầu như không có chi phí. std::stringTôi không biết chính xác chi phí , nó có thể phụ thuộc vào việc triển khai. Tôi hầu như không mong đợi chi phí lớn hơn nhiều so với một con trỏ char trần. Vì tôi không sở hữu một bản sao của tiêu chuẩn, tôi không thể thực sự nêu chi tiết bất kỳ đảm bảo nào được thực hiện bởi tiêu chuẩn. Mọi sự khác biệt về hiệu suất có thể sẽ khác nhau tùy thuộc vào các hoạt động được thực hiện. std::string::sizecó thể lưu trữ kích thước bên cạnh dữ liệu ký tự và do đó nhanh hơn strlen.
Skurmedel

2
Tại sao không sử dụng std :: string cho dữ liệu không phải văn bản? Chúng không bị vô hiệu hóa, vì vậy bạn có thể lưu trữ bất cứ thứ gì bạn muốn trong đó.
Casey Rodarmor

1
@rodarmor Bạn có thể lưu trữ bất kỳ thứ gì bạn muốn, mặc dù điều đó khá rủi ro vì chuỗi được thiết kế cho các chuỗi ký tự kết thúc bằng null. Bạn phải cẩn thận chỉ sử dụng các phép toán an toàn nhị phân, ví dụ: append(const string&)append(const char*, size_t)thay vì operator+=().
boycy

6
Bạn có chắc không? Tôi biết rằng nhiều phép toán sẽ giả định rằng một char * là một chuỗi bị kết thúc bằng null, nhưng tôi không thể nghĩ ra bất kỳ điều gì giả định rằng một chuỗi std :: không chứa null.
Casey Rodarmor

57

Quan điểm của tôi là:

  • Không bao giờ sử dụng ký tự * nếu bạn không gọi mã "C".
  • Luôn sử dụng std :: string: Nó dễ dàng hơn, thân thiện hơn, nó được tối ưu hóa, nó chuẩn, nó sẽ giúp bạn không gặp lỗi, nó đã được kiểm tra và chứng minh là hoạt động.

13

Sử dụng chuỗi thô

Vâng, đôi khi bạn thực sự có thể làm điều này. Khi sử dụng const char *, các mảng char được cấp phát trên ngăn xếp và các ký tự chuỗi, bạn có thể thực hiện theo cách không có cấp phát bộ nhớ nào cả.

Viết mã như vậy thường đòi hỏi nhiều suy nghĩ và cẩn thận hơn so với sử dụng chuỗi hoặc vector, nhưng với một kỹ thuật thích hợp, nó có thể được thực hiện. Với các kỹ thuật thích hợp, mã có thể an toàn, nhưng bạn luôn cần đảm bảo rằng khi sao chép vào char [], bạn có một số đảm bảo về độ dài của chuỗi được sao chép hoặc bạn kiểm tra và xử lý các chuỗi quá khổ một cách khéo léo. Không làm như vậy là điều khiến họ hàm strcpy có tiếng là không an toàn.

Cách các mẫu có thể giúp viết bộ đệm ký tự an toàn

Về độ an toàn của bộ đệm char [], các mẫu có thể giúp ích, vì chúng có thể tạo ra một gói để xử lý kích thước bộ đệm cho bạn. Các mẫu như thế này được Microsoft triển khai, ví dụ như để cung cấp các thay thế an toàn cho strcpy. Ví dụ ở đây được trích xuất từ ​​mã của riêng tôi, mã thực có nhiều phương thức hơn, nhưng điều này đủ để truyền đạt ý tưởng cơ bản:

template <int Size>
class BString
{
  char _data[Size];

  public:
  BString()
  {
    _data[0]=0;
    // note: last character will always stay zero
    // if not, overflow occurred
    // all constructors should contain last element initialization
    // so that it can be verified during destruction
    _data[Size-1]=0;
  }
  const BString &operator = (const char *src)
  {
    strncpy(_data,src,Size-1);
    return *this;
  }

  operator const char *() const {return _data;}
};

//! overloads that make conversion of C code easier 
template <int Size>
inline const BString<Size> & strcpy(BString<Size> &dst, const char *src)
{
  return dst = src;
}

1
+1 cho "Khi sử dụng const char *, các mảng char được phân bổ trên ngăn xếp và chuỗi ký tự, bạn có thể thực hiện theo cách không có cấp phát bộ nhớ nào cả." Mọi người quên rằng "phân bổ" ngăn xếp nhanh hơn nhiều so với đống.
NoSenseEtAl

char*chuỗi không phải lúc nào cũng ở trên ngăn xếp. char *str = (char*)malloc(1024); str[1024] = 0;
Cole Johnson

@ColeJohnson Tôi không khẳng định điều đó, tôi chỉ muốn nói rằng nếu bạn muốn chuỗi của mình được phân bổ ngăn xếp, bạn cần sử dụng const char * kết hợp với chuỗi ký tự, không phải std :: string.
Suma

9

Một trường hợp mà bạn PHẢI sử dụng char*và không phải std::stringlà khi bạn cần hằng số chuỗi tĩnh. Lý do là bạn không có bất kỳ quyền kiểm soát nào đối với các mô-đun thứ tự khởi tạo các biến tĩnh của chúng và một đối tượng toàn cục khác từ một mô-đun khác có thể tham chiếu đến chuỗi của bạn trước khi nó được khởi tạo. http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Static_and_Global_Variables

std::string ưu điểm:

  • quản lý bộ nhớ cho bạn (chuỗi có thể phát triển và việc triển khai sẽ cấp phát bộ đệm lớn hơn cho bạn)
  • Giao diện lập trình cấp cao hơn, hoạt động tốt với phần còn lại của STL.

std::stringnhược điểm: - hai trường hợp chuỗi STL riêng biệt không thể chia sẻ cùng một bộ đệm cơ bản. Vì vậy, nếu bạn vượt qua giá trị, bạn luôn nhận được một bản sao mới. - có một số hình phạt về hiệu suất, nhưng tôi muốn nói trừ khi yêu cầu của bạn là đặc biệt, nó không đáng kể.


Trên thực tế, các triển khai STL thường triển khai ngữ nghĩa copy-on-write cho std :: string, vì vậy việc chuyển chúng theo giá trị không tốn nhiều chi phí. Tuy nhiên, tốt hơn là không nên dựa vào điều đó và nói chung tốt hơn là chuyển một tham chiếu đến const.

1
Một số triển khai std :: string đã từ bỏ việc triển khai COW. Hơn nữa, nó không phải là tầm thường vì nó dường như cung cấp một lớp an toàn luồng (POSIX) tương thích với tiêu chuẩn. Xem groups.google.fr/group/ifi.test.boa/browse_frm/thread/… hoặc groups.google.fr/group/comp.programming.threads/browse_frm/…
Luc Hermitte

8

Bạn nên cân nhắc để sử dụng char*trong các trường hợp sau:

  • Mảng này sẽ được chuyển vào tham số.
  • Bạn biết trước kích thước tối đa của mảng của bạn (bạn biết điều đó HOẶC bạn áp đặt nó).
  • Bạn sẽ không thực hiện bất kỳ chuyển đổi nào trên mảng này.

Trên thực tế, trong C ++, char*thường được sử dụng cho các từ nhỏ cố định, như các tùy chọn, tên tệp, v.v.


3
Mảng không được chuyển, một con trỏ đến mảng. Đó là những gì một con trỏ - một con trỏ đến một đối tượng.
Cole Johnson

5

Khi nào sử dụng c ++ std :: string:

  • string, nhìn chung, an toàn hơn char *, Thông thường khi bạn làm việc với char *, bạn phải kiểm tra mọi thứ để đảm bảo mọi thứ đều đúng, trong lớp string, tất cả điều này được thực hiện cho bạn.
  • Thông thường khi sử dụng char *, bạn sẽ phải giải phóng bộ nhớ mà bạn đã cấp phát, bạn không cần phải làm điều đó với chuỗi vì nó sẽ giải phóng bộ đệm bên trong của nó khi bị hủy.
  • Chuỗi hoạt động tốt với chuỗi ký tự c ++, IO được định dạng rất dễ dàng.

Khi nào sử dụng ký tự *

  • Sử dụng char * cho phép bạn kiểm soát nhiều hơn những gì đang xảy ra ở hậu trường, có nghĩa là bạn có thể điều chỉnh hiệu suất nếu cần.

3

Sử dụng (const) char * làm tham số nếu bạn đang viết thư viện. Việc triển khai chuỗi std :: khác nhau giữa các trình biên dịch khác nhau.


Nếu bạn đang viết một thư viện bằng C ++, bố cục của std :: string không phải là điều duy nhất bạn phải lo lắng. Có rất nhiều khả năng không tương thích giữa hai triển khai. Chỉ sử dụng các thư viện trong C ++ nếu có sẵn trong nguồn hoặc được biên dịch cho trình biên dịch chính xác mà bạn đang sử dụng. Thư viện C thường dễ di động hơn, nhưng trong trường hợp đó, bạn không có std :: string.
David Thornley

Đúng rằng std :: string không phải là vấn đề duy nhất, nhưng hơi quá khi kết luận "Chỉ sử dụng thư viện trong C ++ nếu có sẵn trong nguồn hoặc được biên dịch cho trình biên dịch chính xác mà bạn đang sử dụng." Có hệ thống thiết bị làm việc tốt với các trình biên dịch khác nhau (COM, ví dụ) và nó có thể để lộ một giao diện C đến một thư viện mà là trong nội bộ bằng văn bản với C ++ (Win32 API, ví dụ)
Nemanja Trifunović

2

Nếu bạn muốn sử dụng thư viện C, bạn sẽ phải xử lý chuỗi C. Điều tương tự cũng áp dụng nếu bạn muốn hiển thị API của mình cho C.


2

Bạn có thể mong đợi hầu hết các hoạt động trên chuỗi std :: (chẳng hạn như ví dụ find) được tối ưu hóa hết mức có thể, vì vậy chúng có khả năng thực hiện ít nhất cũng như một bản sao C thuần túy.

Cũng cần lưu ý rằng các trình lặp std :: string thường ánh xạ tới các con trỏ vào mảng char bên dưới. Vì vậy, bất kỳ thuật toán nào bạn đưa ra trên đầu các trình vòng lặp về cơ bản giống với cùng một thuật toán trên char * về mặt hiệu suất.

Những điều cần chú ý là ví dụ operator[]- hầu hết các triển khai STL không thực hiện kiểm tra giới hạn và nên chuyển điều này sang cùng một hoạt động trên mảng ký tự bên dưới. AFAIK STLPort có thể tùy chọn thực hiện kiểm tra giới hạn, lúc này toán tử này sẽ chậm hơn một chút.

Vì vậy, những gì sử dụng std :: string đạt được bạn? Nó giúp bạn thoát khỏi việc quản lý bộ nhớ thủ công; Việc thay đổi kích thước mảng trở nên dễ dàng hơn và bạn thường ít phải suy nghĩ về việc giải phóng bộ nhớ.

Nếu bạn lo lắng về hiệu suất khi thay đổi kích thước chuỗi, có một reservechức năng mà bạn có thể thấy hữu ích.


1

nếu bạn đang sử dụng mảng ký tự như văn bản, v.v. hãy sử dụng std :: string linh hoạt hơn và dễ sử dụng hơn. Nếu bạn sử dụng nó cho việc khác như lưu trữ dữ liệu? sử dụng mảng (thích vectơ hơn)


1

Ngay cả khi hiệu suất là quan trọng, bạn nên sử dụng tốt hơn vector<char>- nó cho phép cấp phát bộ nhớ trước (phương thức dự trữ ()) và sẽ giúp bạn tránh rò rỉ bộ nhớ. Sử dụng vector :: operator [] dẫn đến một chi phí, nhưng bạn luôn có thể trích xuất địa chỉ của bộ đệm và lập chỉ mục nó chính xác như thể nó là một char *.


Nhưng sẽ rất tuyệt nếu sử dụng một số loại chức năng chuỗi điển hình và chỉ có tùy chọn để chỉ định chính sách cho bộ nhớ. Đối với điều đó, hãy xem liên kết trong câu trả lời của tôi.
Ẩn danh

Nó không phải là sự thật. Nếu bạn cho rằng vectơ sẽ được cấp phát trong không gian bộ nhớ liền kề, thì việc phân bổ lại (để tăng kích thước vectơ) sẽ không hiệu quả chút nào, vì nó ngụ ý bản sao của đoạn trước đó.
Jérôme

Tôi đã hiểu nhầm câu trả lời của bạn, vì bạn sử dụng vector thay vì char *, không phải thay vì chuỗi ... Trong trường hợp này, tôi đồng ý.
Jérôme

Không nên có chi phí sử dụng toán tử []. Xem ví dụ: stackoverflow.com/questions/381621/…
Luc Hermitte,

-1

AFAIK nội bộ hầu hết std :: string triển khai bản sao khi ghi, ngữ nghĩa được tính tham chiếu để tránh chi phí, ngay cả khi các chuỗi không được chuyển bằng tham chiếu.


5
Điều này không còn đúng nữa, vì sao chép khi ghi gây ra các vấn đề nghiêm trọng về khả năng mở rộng trong môi trường đa luồng.
Suma

Điều đó đúng với việc thực hiện STL của GCC.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.