C ++ 'da dize ve karakter [] türleri arasındaki fark


126

Biraz C biliyorum ve şimdi C ++ 'ya bir göz atıyorum. C dizgileriyle uğraşmak için dizileri char kullanmaya alışkınım, ancak C ++ koduna bakarken hem dize türünü hem de char dizilerini kullanan örnekler olduğunu görüyorum:

#include <iostream>
#include <string>
using namespace std;

int main () {
  string mystr;
  cout << "What's your name? ";
  getline (cin, mystr);
  cout << "Hello " << mystr << ".\n";
  cout << "What is your favorite team? ";
  getline (cin, mystr);
  cout << "I like " << mystr << " too!\n";
  return 0;
}

ve

#include <iostream>
using namespace std;

int main () {
  char name[256], title[256];

  cout << "Enter your name: ";
  cin.getline (name,256);

  cout << "Enter your favourite movie: ";
  cin.getline (title,256);

  cout << name << "'s favourite movie is " << title;

  return 0;
}

(her iki örnek de http : //www.cplusplus.com'dan )

Sanırım bu çokça sorulan ve cevaplanan (açık?) Bir soru, ancak birisi bana C ++ 'da dizelerle başa çıkmak için bu iki yol arasındaki farkın tam olarak ne olduğunu söylese iyi olurdu (performans, API entegrasyonu, her birinin daha iyi, ...).

Teşekkür ederim.


Bu yardımcı olabilir: C ++ char * vs std :: string
Wael Dalloul

Yanıtlar:


187

Bir karakter dizisi tam da budur - bir karakter dizisidir:

  • Yığın üzerinde tahsis edilmişse (örneğinizdeki gibi), her zaman örn. 256 bayt içerdiği metin ne kadar uzun olursa olsun
  • Yığın üzerinde tahsis edilmişse (malloc () veya yeni karakter [] kullanarak) belleği daha sonra serbest bırakmaktan sorumlusunuz ve her zaman bir yığın tahsisinin ek yüküne sahip olacaksınız.
  • Diziye 256 karakterden fazla bir metin kopyalarsanız, bu, programınızın başka bir yerinde çökebilir, çirkin iddia mesajları üretebilir veya açıklanamayan (yanlış) davranışlara neden olabilir.
  • Metnin uzunluğunu belirlemek için, dizinin bir \ 0 karakteri için karakter karakter taranması gerekir.

Dize, bir char dizisi içeren, ancak onu sizin için otomatik olarak yöneten bir sınıftır. Çoğu dize uygulamasının 16 karakterlik yerleşik bir dizisi vardır (bu nedenle kısa dizeler öbeği parçalamaz) ve öbeği daha uzun dizeler için kullanır.

Bir dizenin karakter dizisine şu şekilde erişebilirsiniz:

std::string myString = "Hello World";
const char *myStringChars = myString.c_str();

C ++ dizeleri gömülü \ 0 karakterleri içerebilir, uzunluklarını saymadan bilirler, kısa metinler için yığın ayrılan karakter dizilerinden daha hızlıdır ve sizi arabellek taşmalarından korur. Ayrıca daha okunaklı ve kullanımı daha kolaydır.


Bununla birlikte, C ++ dizeleri DLL sınırları boyunca kullanım için (çok) uygun değildir, çünkü bu, böyle bir DLL işlevinin herhangi bir kullanıcısının, aynı derleyiciyi ve C ++ çalışma zamanı uygulamasını kullandığından emin olmasını gerektirir, çünkü dize sınıfının farklı davranması riskini alır.

Normalde, bir dize sınıfı, yığın belleğini çağıran yığın üzerinde de serbest bırakır, bu nedenle, yalnızca çalışma zamanının paylaşılan (.dll veya .so) bir sürümünü kullanıyorsanız belleği yeniden boşaltabilir.

Kısacası: tüm dahili işlevlerinizde ve yöntemlerinizde C ++ dizeleri kullanın. Bir .dll veya .so yazarsanız, genel (dll / açık) işlevlerinizde C dizeleri kullanın.


4
Ek olarak, dizelerin gerçekten düzgün olabilen bir dizi yardımcı işlevi vardır.
Håkon

1
DLL sınırları hakkında biraz inanmıyorum. Çok özel koşullar altında, potansiyel olarak bozulabilir (bir DLL, çalışma zamanının farklı bir sürümüne diğer DLL'ler tarafından kullanılandan statik olarak bağlanır) ve muhtemelen bu durumlarda daha kötü şeyler olur), ancak genel durumda herkesin varsayılanı kullandığı durumda standart çalışma zamanının paylaşılan sürümü (varsayılan) bu olmayacak.
Martin York

2
Örnek: Bir std :: string ve genel API'sinde bulunan libfoo adlı bir halk kütüphanesinin VC2008SP1 ile derlenmiş ikili dosyalarını dağıtıyorsunuz. Şimdi birisi libfoo.dll dosyanızı indirir ve bir hata ayıklama derlemesi yapar. Std :: string, içinde bazı ek hata ayıklama alanlarına sahip olabilir ve dinamik dizelerin hareket etmesi için göstericinin ofsetine neden olabilir.
Cygon

2
Örnek 2: 2010 yılında, birisi libfoo.dll dosyanızı indirir ve VC2010 tarafından oluşturulmuş uygulamasında kullanır. Onun kodu MSVCP100.dll'yi yükler ve libfoo.dll dosyanız hala MSVCP90.dll'yi yükler -> iki yığın alırsınız -> bellek serbest bırakılamaz, eğer libfoo dize referansını değiştirir ve bir std :: dizesini yenisiyle verirse hata ayıklama kipinde onaylama hataları geri işaretçi.
Cygon

1
Ben sadece "Kısacası: tüm dahili fonksiyonlarınız ve yöntemleriniz için C ++ dizeleri kullanın." Örneklerinizi anlamaya çalışıyorum, beynim patladı.
Stephen

12

Arkaitz, stringbunun yönetilen bir tür olduğu doğru . Bunun sizin için anlamı , dizenin ne kadar uzun olduğu konusunda asla endişelenmenize veya dizenin belleğini serbest bırakmak veya yeniden tahsis etmek konusunda endişelenmenize gerek kalmamasıdır.

Öte yandan, char[]yukarıdaki durumdaki gösterim, karakter tamponunu tam olarak 256 karakterle sınırlamıştır. Bu arabelleğe 256 karakterden fazla yazmaya çalıştıysanız, en iyi ihtimalle programınızın "sahip olduğu" diğer belleğin üzerine yazarsınız. En kötüsü, sahip olmadığınız belleğin üzerine yazmaya çalışacaksınız ve işletim sisteminiz programınızı anında öldürecektir.

Sonuç olarak? Dizeler çok daha programcı dostudur, karakter [] ler bilgisayar için çok daha etkilidir.


4
En kötü ihtimalle, diğer insanlar belleğin üzerine yazacak ve bilgisayarınızda kötü amaçlı kod çalıştıracaktır. Ayrıca bkz . Arabellek taşması .
David Johnstone

6

Dize türü, karakter dizileri için tamamen yönetilen bir sınıftır, oysa char [], sizin için bir karakter dizesini temsil eden bir bayt dizisi olan C'deki halidir.

API ve standart kitaplık açısından her şey karakter dizileri cinsinden uygulanır ve char [] değil, ancak libc'de char [] alan birçok işlev vardır, bu nedenle bunlar için kullanmanız gerekebilir, bunun dışında her zaman std :: string kullanın.

Elbette verimlilik açısından, yönetilmeyen belleğin ham bir tamponu birçok şey için hemen hemen her zaman daha hızlı olacaktır, ancak örneğin dizeleri karşılaştırmayı hesaba katın, std :: string her zaman önce onu kontrol edecek boyuta sahipken, char [] ile karakter karakter karşılaştırmanız gerekir.


5

Ben şahsen, eski kodla uyumluluk dışında, char * veya char [] kullanmak istemenin herhangi bir nedenini görmüyorum. std :: string, bir c-string kullanmaktan daha yavaş değildir, ancak sizin için yeniden ayırmayı halledecektir. Oluşturduğunuzda boyutunu ayarlayabilir ve böylece isterseniz yeniden ayırmayı önleyebilirsiniz. İndeksleme operatörü ([]) sabit zamanlı erişim sağlar (ve kelimenin her anlamıyla bir c-string indeksleyici kullanmakla aynı şeydir). At yöntemini kullanmak size sınır kontrol edilmiş güvenlik de verir, bu da c-dizeleri ile yazmadığınız sürece elde edemeyeceğiniz bir şeydir. Derleyiciniz genellikle yayın modunda dizinleyici kullanımını optimize eder. C dizgileriyle uğraşmak kolaydır; silme ve silme [] gibi şeyler, istisna güvenliği, hatta bir c-string'in nasıl yeniden tahsis edileceği gibi şeyler.

Ve COW dizgilerine sahip olmak ve MT için COW olmayan gibi gelişmiş kavramlarla uğraşmanız gerektiğinde, std :: string'e ihtiyacınız olacak.

Kopyalar konusunda endişeleniyorsanız, referansları ve olabildiğince const referansları kullandığınız sürece, kopyalar nedeniyle ek yükünüz olmayacaktır ve bu c-string ile yaptığınız şeyle aynıdır.


+1 DLL uyumluluğu gibi uygulama sorunlarını düşünmemiş olsanız da, COW aldınız.

char dizimin 12 bayt olduğunu bildiğimden ne haber? Bunun için bir dizge oluşturursam gerçekten verimli olmayabilir, değil mi?
David 天宇 Wong

@David: Son derece hassas bir kodunuz varsa, evet. Std :: string ctor çağrısını, std :: string üyelerinin başlatılmasına ek olarak bir ek yük olarak düşünebilirsiniz. Ancak erken optimizasyonun birçok kod tabanını gereksiz yere C stili yaptığını unutmayın, bu yüzden dikkatli olun.
Abhay

1

Dizelerin yardımcı işlevleri vardır ve karakter dizilerini otomatik olarak yönetir. Dizeleri birleştirebilirsiniz, bir karakter dizisi için onu yeni bir diziye kopyalamanız gerekir, dizeler çalışma zamanında uzunluklarını değiştirebilir. Bir char dizisini yönetmek bir dizeden daha zordur ve bazı işlevler yalnızca bir dizeyi girdi olarak kabul edebilir ve diziyi dizeye dönüştürmenizi gerektirir. Dizeleri kullanmak daha iyidir, dizileri kullanmak zorunda kalmamanız için yapılmışlardır. Diziler nesnel olarak daha iyi olsaydı, dizelerimiz olmazdı.


0

(Char *) 'ı string.begin () olarak düşünün. Temel fark, (char *) bir yineleyici ve std :: string bir kaptır. Eğer temel dizelere bağlı kalırsanız, a (char *) size std :: string :: iterator'ın ne yaptığını verecektir. Bir yineleyiciden ve ayrıca C ile uyumluluktan yararlanmak istediğinizde (char *) kullanabilirsiniz, ancak bu kural değil istisnadır. Her zaman olduğu gibi yineleyici geçersiz kılma konusunda dikkatli olun. İnsanlar (char *) güvenli olmadığını söylediğinde kastettikleri budur. Diğer C ++ yineleyiciler kadar güvenlidir.


0

Farklardan biri Null sonlandırmadır (\ 0).

C ve C ++ 'da, char * veya char [], parametre olarak tek bir karaktere bir gösterici alacak ve 0 bellek değerine (genellikle boş sonlandırıcı denir) ulaşılana kadar bellek boyunca izleyecektir.

C ++ dizeleri gömülü \ 0 karakterleri içerebilir, uzunluklarını saymadan bilirler.

#include<stdio.h>
#include<string.h>
#include<iostream>

using namespace std;

void NullTerminatedString(string str){
   int NUll_term = 3;
   str[NUll_term] = '\0';       // specific character is kept as NULL in string
   cout << str << endl <<endl <<endl;
}

void NullTerminatedChar(char *str){
   int NUll_term = 3;
   str[NUll_term] = 0;     // from specific, all the character are removed 
   cout << str << endl;
}

int main(){
  string str = "Feels Happy";
  printf("string = %s\n", str.c_str());
  printf("strlen = %d\n", strlen(str.c_str()));  
  printf("size = %d\n", str.size());  
  printf("sizeof = %d\n", sizeof(str)); // sizeof std::string class  and compiler dependent
  NullTerminatedString(str);


  char str1[12] = "Feels Happy";
  printf("char[] = %s\n", str1);
  printf("strlen = %d\n", strlen(str1));
  printf("sizeof = %d\n", sizeof(str1));    // sizeof char array
  NullTerminatedChar(str1);
  return 0;
}

Çıktı:

strlen = 11
size = 11
sizeof = 32  
Fee s Happy


strlen = 11
sizeof = 12
Fee

"belirli bir karakterden tüm karakterler çıkarılır" hayır, "kaldırılmazlar", bir karakter işaretçisi yazdırmak yalnızca boş sonlandırıcıya kadar yazdırır. (bir karakterin * sonu bilmesinin tek yolu bu olduğundan) string sınıfı tam boyutu bilir, bu yüzden sadece onu kullanır. Karakterinizin * boyutunu biliyorsanız, tüm karakterleri kendiniz de yazdırabilir / kullanabilirsiniz.
Puddle
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.