Yürütülebilir koda C dizesi. Dizelerle çalışmak. Dize sınıfı. Sınıf kurucuları. Ata (), ekle (), ekle (), değiştir (), sil (), bul (), rfind (), karşılaştır (), c_str () işlevleri. Örnekleri

  • 29.07.2019

Habra, merhaba!

Çok uzun zaman önce, bir bilgisayar bilimleri kolejinin öğretmenlerinden birinin dahil olduğu oldukça ilginç bir olay yaşadım.

Linux programlaması hakkındaki konuşma, yavaş yavaş, bu adamın sistem programlamanın karmaşıklığının aslında fazlasıyla abartılı olduğunu tartışmaya başladığı noktaya geldi. C dilinin bir eşleşme kadar basit olduğu, aslında Linux çekirdeği gibi (kendi sözleriyle).

Yanımda bir centilmen C geliştirme araçları seti (gcc, vim, make, valgrind, gdb) olan bir Linux dizüstü bilgisayarım vardı. O zaman kendimize hangi hedefi koyduğumuzu artık hatırlamıyorum, ancak birkaç dakika sonra rakibim kendini bu dizüstü bilgisayarın arkasında, sorunu çözmeye tamamen hazır buldu.

Ve kelimenin tam anlamıyla ilk satırlarda, bir satır için bellek ayırırken ciddi bir hata yaptı.

Char * str = (char *) malloc (sizeof (char) * strlen (tampon));
tampon, klavyeden verilerin yazıldığı bir yığın değişkenidir.

Bence mutlaka "burada bir terslik olabilir mi?" diye soranlar olacaktır.
İnan bana, olabilir.

Ve tam olarak ne - kesimde okuyun.

Küçük bir teori - bir tür FaceBez.

Biliyorsanız, sonraki başlığa gidin.

C'deki bir dize, dostane bir şekilde her zaman satır sonu karakteri olan "\ 0" ile bitmesi gereken bir karakter dizisidir. Yığındaki (statik) dizeler şu şekilde bildirilir:

Karakter str [n] = (0);
n, karakter dizisinin boyutudur, dizenin uzunluğuyla aynıdır.

Atama (0) - dizeyi "sıfırlama" (isteğe bağlı, onsuz bildirebilirsiniz). Sonuç, memset (str, 0, sizeof (str)) ve bzero (str, sizeof (str)) işlevleriyle aynıdır. Başlatılmamış değişkenlerde çöpü önlemek için kullanılır.

Ayrıca yığında, satırı hemen başlatabilirsiniz:

Char buf = "varsayılan arabellek metni \ n";
Ek olarak, bir dize işaretçi olarak bildirilebilir ve öbek üzerindeki bellek bunun için ayrılabilir:

Karakter * str = malloc (boyut);
size - dize için ayırdığımız bayt sayısı. Bu tür dizelere dinamik denir (gerekli boyutun dinamik olarak hesaplanmasından dolayı + tahsis edilen bellek boyutu, realloc () işlevi kullanılarak herhangi bir zamanda artırılabilir).

Yığın değişken olması durumunda, dizinin boyutunu belirlemek için n gösterimini kullandım, yığın üzerinde bir değişken olması durumunda gösterim boyutunu kullandım. Ve bu, yığındaki bir bildirim ile öbek üzerinde bellek tahsisli bir bildirim arasındaki farkın gerçek özünü mükemmel bir şekilde yansıtır, çünkü n genellikle eleman sayısından bahsederken kullanılır. Ve boyut tamamen farklı bir hikaye ...

Valgrind bize yardım edecek

Bir önceki yazımda da bahsetmiştim. Valgrind (ikisi biraz nasıl yapılır), programcının bellek sızıntılarını ve bağlam hatalarını izlemesine yardımcı olan çok kullanışlı bir programdır - sadece dizelerle çalışırken en sık ortaya çıkan türden şeyler.

Bahsettiğim programa benzer bir şey uygulayan küçük bir listeye bakalım ve onu valgrind üzerinden çalıştıralım:

#Dahil etmek #Dahil etmek #Dahil etmek #define HELLO_STRING "Merhaba, Habr! \ n" void main () (char * str = malloc (sizeof (char) * strlen (HELLO_STRING)); strcpy (str, HELLO_STRING); printf ("-> \ t% s" , str); ücretsiz (str);)
Ve aslında, programın sonucu:

$ gcc main.c $ ./a.out -> Merhaba Habr!
Şimdiye kadar olağandışı bir şey yok. Şimdi bu programı valgrind ile çalıştıralım!

$ valgrind --tool = memcheck ./a.out == 3892 == Memcheck, bir bellek hatası dedektörü == 3892 == Copyright (C) 2002-2015 ve GNU GPL "d, Julian Seward ve diğerleri tarafından == 3892 == Valgrind-3.12.0 ve LibVEX kullanarak; telif hakkı bilgisi için -h ile tekrar çalıştırın == 3892 == Komut: ./a.out == 3892 == == 3892 == 2 boyutunda geçersiz yazma == 3892 = = 0x4005B4'te: main (in /home/indever/prg/C/public/a.out) == 3892 == 0x520004c adresi, 13 büyüklüğünde bir blok içinde 12 bayttır "d == 3892 == 0x4C2DB9D'de: malloc (vg_replace_malloc.c: 299) == 3892 == 0x400597 ile: main (in /home/indever/prg/C/public/a.out) == 3892 == == 3892 == 1 boyutunda geçersiz okuma == 3892 == 0x4C30BC4'te: strlen (vg_replace_strmem.c: 454) == 3892 == 0x4E89AD0'da: vfprintf ( /usr/lib64/libc-2.24.so'da) == 3892 == 0x4E90718'de: printf ( / usr /'de) lib64 / libc-2.24.so) == 3892 == 0x4005CF ile: main (in /home/indever/prg/C/public/a.out) == 3892 == 0x520004d adresi, 13 boyutlu bir bloktan sonra 0 bayttır alloc "d == 3892 == 0x4C2DB9D'de: malloc (vg_replace_malloc.c: 299) == 3892 == 0x400597 ile: main (in / home / indever / prg / C / public / a.out) == 3892 == -> Merhaba Habr! == 3892 == == 3892 == Yığın ÖZETİ: == 3892 == çıkışta kullanımda: 0 blokta 0 bayt == 3892 == toplam yığın kullanımı: 2 tahsis, 2 serbest, 1.037 bayt tahsis == 3892 = === 3892 == Tüm yığın blokları serbest bırakıldı - sızıntı mümkün değil == 3892 == == 3892 == Algılanan ve bastırılan hataların sayısı için, şununla tekrar çalıştırın: -v == 3892 == HATA ÖZETİ: 2 bağlam (bastırılmış: 0'dan 0'a)
== 3892 == Tüm yığın blokları serbest bırakıldı - sızıntı mümkün değil- sızıntı yok ve bu iyi bir haber. Ancak gözlerinizi biraz daha aşağı indirmenizde fayda var (bunun sadece bir özet olduğunu belirtmek isterim, temel bilgiler biraz başka yerde):

== 3892 == HATA ÖZETİ: 2 bağlamdan 3 hata (bastırılmış: 0'dan 0)
3 hata. 2 bağlamda. Bu kadar basit bir programda. Nasıl!?

Çok basit. Tüm "hile", strlen işlevinin satır sonu karakteri "\ 0"ı hesaba katmamasıdır. Giriş satırında açıkça belirtseniz bile (#define HELLO_STRING "Merhaba, Habr! \ N \ 0"), yok sayılır.

Program yürütme sonucunun biraz üzerinde, satırlar -> Merhaba Habr! değerli valgrind'imizin neyi ve nerede sevmediğine dair ayrıntılı bir rapor var. Bu satırlara kendi başınıza bakmayı ve sonuçlar çıkarmayı öneriyorum.

Aslında, programın doğru sürümü şöyle görünecektir:

#Dahil etmek #Dahil etmek #Dahil etmek #define HELLO_STRING "Merhaba, Habr! \ n" void main () (char * str = malloc (sizeof (char) * (strlen (HELLO_STRING) + 1)); strcpy (str, HELLO_STRING); printf ("-> \ t% s ", str); serbest (str);)
Valgrind üzerinden geçelim:

$ valgrind --tool = memcheck ./a.out -> Merhaba, Habr! == 3435 == == 3435 == Yığın ÖZETİ: == 3435 == çıkışta kullanımda: 0 blokta 0 bayt == 3435 == toplam yığın kullanımı: 2 tahsis, 2 serbest, 1.038 bayt tahsis == 3435 = === 3435 == Tüm yığın blokları serbest bırakıldı - sızıntı mümkün değil == 3435 == == 3435 == Algılanan ve bastırılan hataların sayısı için, şununla tekrar çalıştırın: -v == 3435 == HATA ÖZETİ: 0 hatadan itibaren 0 bağlam (bastırılmış: 0'dan 0'a)
İyi. Hata yok, +1 bayt ayrılmış bellek sorunu çözmeye yardımcı oldu.

İlginçtir ki, çoğu durumda, hem birinci hem de ikinci programlar aynı şekilde çalışacaktır, ancak son karakterin sığmadığı satır için ayrılan bellek boş değilse, böyle bir çıktı verirken printf () işlevi satır, ayrıca bu satırdan sonraki tüm çöpleri çıkaracaktır - printf () yolunda satır sonu karakteri görünene kadar her şey görüntülenecektir.

Ancak bilirsiniz, (strlen (str) + 1) böyle bir çözümdür. 2 sorunla karşı karşıyayız:

  1. Peki ya örneğin s (n) printf (..) kullanılarak oluşturulan bir dize için bellek ayırmamız gerekirse? Tartışmaları desteklemiyoruz.
  2. Dış görünüş. Değişken bildirim satırı çok kötü görünüyor. Bazı adamlar, artıların altına yazıyormuş gibi (char *) malloc'a vidalamayı da başarır. Düzenli olarak dizeleri işlemeniz gereken bir programda, daha zarif bir çözüm bulmak mantıklıdır.
Hem bizi hem de valgrind'i tatmin edecek bir çözüm üretelim.

snprintf()

int snprintf (char * str, size_t boyut, const char * biçimi, ...);- işlev - bir dizgeyi biçimlendiren ve onu ilk argüman olarak iletilen işaretçiye yazan bir sprintf uzantısı. Sprintf'den () farklıdır, çünkü str, boyuttan daha fazla bayt yazmayacaktır.

İşlevin ilginç bir özelliği vardır - her durumda oluşturulan dizenin boyutunu döndürür (satır sonu karakteri hariç). Dize boşsa, 0 döndürülür.

strlen kullanımıyla ilgili anlattığım sorunlardan biri sprintf() ve snprintf() işlevleriyle ilgilidir. Diyelim ki str'ye bir şeyler yazmamız gerekiyor. Son dize, diğer değişkenlerin değerlerini içerir. Girişimiz şöyle bir şey olmalı:

Char * str = / * hafızayı buraya ayırın * /; sprintf (str, "Merhaba,% s \ n", "Habr!");
Soru ortaya çıkıyor: str dizesi için ne kadar bellek ayrılması gerektiğini nasıl belirleyebilirim?

Char * str = malloc (sizeof (char) * (strlen (str, "Merhaba,% s \ n", "Habr!") + 1)); - çalışmayacak. strlen() fonksiyon prototipi şuna benzer:

#Dahil etmek size_t strlen (const char * s);
const char * s, s'ye iletilen dizenin değişken sayıda argüman içeren bir biçim dizesi olabileceği anlamına gelmez.

İşte burada yukarıda bahsettiğim snprintf() fonksiyonunun kullanışlı özelliği bize yardımcı olacaktır. Aşağıdaki programın koduna bir göz atalım:

#Dahil etmek #Dahil etmek #Dahil etmek void main() (/ * snprintf () satır sonu karakterini dikkate almadığından, boyutunu sonuca ekleyin * / size_t gerekli_mem = snprintf (NULL, 0, "Hello,% s! \ n" , "Habr") + sizeof ("\ 0"); char * str = malloc (gerekli_mem); snprintf (str, gerekli_mem, "Merhaba,% s! \ n", "Habr"); printf ("-> \ t% s", str); serbest (str);)
Programı valgrind'de çalıştırın:

$ valgrind --tool = memcheck ./a.out -> Merhaba, Habr! == 4132 == == 4132 == Yığın ÖZETİ: == 4132 == çıkışta kullanımda: 0 blokta 0 bayt == 4132 == toplam yığın kullanımı: 2 tahsis, 2 serbest, 1.041 bayt tahsis == 4132 = === 4132 == Tüm yığın blokları serbest bırakıldı - sızıntı mümkün değil == 4132 == == 4132 == Algılanan ve bastırılan hataların sayısı için, şununla tekrar çalıştırın: -v == 4132 == HATA ÖZETİ: 0 hata 0 bağlam (bastırılmış: 0'dan 0'a) $
İyi. Argümanlar için desteğimiz var. snprintf() işlevine ikinci argüman olarak sıfır iletmemiz nedeniyle, boş gösterici ile yazmak asla Seagfault'a yol açmaz. Ancak buna rağmen, işlev yine de dize için gerekli boyutu döndürür.

Ancak öte yandan, ek bir değişken eklemek zorunda kaldık ve yapı

Size_t gerekli_mem = snprintf (NULL, 0, "Merhaba,% s! \ N", "Habr") + sizeof ("\ 0");
strlen'den () bile daha kötü görünüyor.

Genel olarak, + sizeof ("\ 0"), biçim dizesinin sonunda açıkça "\ 0" belirtilerek kaldırılabilir (size_t gerekli_mem = snprintf (NULL, 0, "Hello,% s! \ N) \0 "," Habr ");), ancak bu her zaman mümkün değildir (hat işleme mekanizmasına bağlı olarak fazladan bir bayt tahsis edebiliriz).

Bir şeyler yapılması gerekiyor. Biraz düşündüm ve eskilerin bilgeliğine hitap etme zamanının geldiğine karar verdim. İlk argüman olarak null pointer ve ikinci olarak null ile snprintf()'i çağıracak bir makro fonksiyonu tanımlayalım. Ve yolun sonunu da unutmayalım!

#define strsize (args ...) snprintf (NULL, 0, args) + sizeof ("\ 0")
Evet, belki birileri için haber olabilir, ancak C'deki makrolar değişken sayıda argümanı destekler ve üç nokta önişlemciye birkaç gerçek argümanın makro fonksiyonunun belirtilen argümanına karşılık geldiğini söyler (bizim durumumuzda, bu argümanlardır).

Çözümümüzü pratikte kontrol edelim:

#Dahil etmek #Dahil etmek #Dahil etmek #define strsize (args ...) snprintf (NULL, 0, args) + sizeof ("\ 0") void main () (char * str = malloc (strsize ("Merhaba,% s \ n", "Habr! ")); sprintf (str," Merhaba,% s \ n "," Habr! "); printf (" -> \ t% s ", str); ücretsiz (str);)
Valgrund ile çalıştırın:

$ valgrind --tool = memcheck ./a.out -> Merhaba, Habr! == 6432 == == 6432 == Yığın ÖZETİ: == 6432 == çıkışta kullanımda: 0 blokta 0 bayt == 6432 == toplam yığın kullanımı: 2 tahsis, 2 serbest, 1.041 bayt tahsis == 6432 = === 6432 == Tüm yığın blokları serbest bırakıldı - sızıntı mümkün değil == 6432 == == 6432 == Algılanan ve bastırılan hataların sayısı için, şununla tekrar çalıştırın: -v == 6432 == HATA ÖZETİ: 0 hatadan itibaren 0 bağlam (bastırılmış: 0'dan 0'a)
Evet, hata yok. Her şey doğru. Ve valgrind mutludur ve programcı sonunda uyuyabilir.

Ama son olarak bir şey daha söyleyeceğim. Herhangi bir dize için bellek ayırmamız gerekirse (argümanlarla bile), zaten var tamamen çalışan anahtar teslimi çözüm.

asprintf işleviyle ilgili:

#define _GNU_SOURCE / * feature_test_macros'a bakın (7) * / #include int asprintf (char ** strp, const karakter * fmt, ...);
İlk argüman olarak, bir dizgeye (** strp) bir işaretçi alır ve başvuruyu kaldırılan işaretçiye bellek ayırır.

asprintf() programımız şöyle görünür:

#Dahil etmek #Dahil etmek #Dahil etmek void main () (char * str; asprintf (& str, "Merhaba,% s! \ n", "Habr"); printf ("-> \ t% s", str); ücretsiz (str);)
Ve aslında, valgrind'de:

$ valgrind --tool = memcheck ./a.out -> Merhaba, Habr! == 6674 == == 6674 == Yığın ÖZETİ: == 6674 == çıkışta kullanımda: 0 blokta 0 bayt == 6674 == toplam yığın kullanımı: 3 tahsis, 3 serbest, ayrılan 1138 bayt == 6674 = === 6674 == Tüm yığın blokları serbest bırakıldı - sızıntı mümkün değil == 6674 == == 6674 == Tespit edilen ve bastırılan hataların sayısı için, şununla tekrar çalıştırın: -v == 6674 == HATA ÖZETİ: dan 0 hata 0 bağlam (bastırılmış: 0'dan 0'a)
Her şey yolunda, ancak gördüğünüz gibi, daha fazla bellek tahsis edildi ve şimdi iki değil, üç tahsis "s var. Zayıf gömülü sistemlerde bu işlev istenmez.
Ayrıca konsola man asprintf yazarsak şunu görürüz:

UYUM Bu işlevler GNU uzantılarıdır, C veya POSIX'te değil. * BSD altında da mevcutturlar. FreeBSD uygulaması, hata durumunda strp'yi NULL olarak ayarlar.

Buradan bu özelliğin yalnızca GNU kaynağında mevcut olduğu açıktır.

Çözüm

Sonuç olarak, C'de stringlerle çalışmanın bir takım nüansları olan çok karmaşık bir konu olduğunu söylemek istiyorum. Örneğin, dinamik bellek ayırma için "güvenli" kod yazmak için, yine de malloc () yerine calloc () işlevinin kullanılması önerilir - calloc, ayrılan belleği sıfırlarla tıkar. Peki ya da bellek ayırdıktan sonra memset() işlevini kullanın. Aksi takdirde, başlangıçta ayrılan bellek alanında bulunan çöp, hata ayıklama sırasında ve bazen bir dize ile çalışırken sorulara neden olabilir.

Benim isteğim üzerine, dizeler için bellek ayırma sorununu çözen C programcı arkadaşlarımın yarısından fazlası (çoğu yeni başlayanlar), bunu sonuçta bağlam hatalarına yol açacak şekilde yaptı. Bir durumda - bir bellek sızıntısına bile (peki, bir kişi serbest bırakmayı unuttu (str), kim olmaz). Aslında bu, az önce okuduğunuz bu yaratımı yaratmamı istedi.

Umarım birileri bu makaleyi faydalı bulur. Bütün bunları neden pazarlık ettim - hiçbir dil basit değildir. Her yerin kendine has incelikleri vardır. Ve bildiğiniz dilin incelikleri ne kadar fazlaysa, kodunuz o kadar iyi olur.

Bu makaleyi okuduktan sonra kodunuzun biraz daha iyi olacağına inanıyorum :)
İyi şanslar, Habr!

Programcı diyor ki:

Merhaba! makalenizi okudum. Aynı zamanda çok üzgün ve komiktim. Özellikle şu cümleniz can yakıyor: "Karakter türünde bir değişken genellikle dizi olarak kullanıldığı için olası değerlerin sayısı belirlenir." 😆 😆 😆
sana gülmüyorum. Bir web sitesi oluşturmak gerçekten bir başarıdır. Ben sadece size tavsiyede bulunmak ve birkaç hataya dikkat çekmek istiyorum.

1. char türünde bir değişkenin değeri şu şekilde atanır:

Buraya:

Karakter a = * "A";

Dizinin işaretçisi adressizdir ve sonuç olarak dizinin ilk öğesinin değeri döndürülür, yani. 'A'

2. Sıfırlama şu şekilde yapılır:

Karakter a = NULL;
karakter b = ();

// Ve programın gövdesindeki satır bu şekilde temizlenir

"" - bu karaktere boş sonlandırıcı denir. Satırın sonuna yerleştirilir. Siz kendiniz bilmeden makalenizdeki s1 dizisini bu sembolle doldurdunuz. Ancak bu sembolü sadece dizinin sıfır elemanına atamak mümkündü.

3. Terminolojiyi kullanmaktan çekinmeyin.
= işareti bir atama işlemidir.
* işareti adresleme işlemidir.
Makalenin şu kısmını kastediyorum: "Her şey çok basit çıktı, = işaretinden önce * koymak zorunda kaldınız ve eleman numarasını bildirmek zorunda kaldınız (sıfır birinciye karşılık gelir)"

Beni yanlış anlama, makale olduğu gibi var olamaz. Tembel olmayın, yeniden yazın.
Büyük bir sorumluluğunuz var! Ciddiyim. Sitenizin sayfaları Yandex arama sonuçlarının ilk sayfasında yer aldı. Birçok kişi sizden sonra hataları tekrarlamaya başladı bile.

İyi şanlar! Bunu kullanabilirsin!

:
Bunu uzun zamandır biliyorum, bir şeyi düzeltmek için her zaman 200 makaleyi tekrar okumak zor. Ve bazı kaba tipler öyle bir şekilde yazar ki, neyin daha iyi düzeltileceğini bilmek bile, onu düzeltme arzusu değildir.

Diğer hataları da düzeltmekten mutlu olacağım. açılırlarsa yanlışlıkları düzeltin. yardımın için minnettarım. Teşekkürler Bunu uzun zamandır biliyorum, bir şeyi düzeltmek için her zaman 200 makaleyi tekrar okumak zor. Ve bazı kaba tipler öyle bir şekilde yazar ki, neyin daha iyi düzeltileceğini bilmek bile, onu düzeltme arzusu değildir.
Karakterinizle b = (); Bu kesinlikle sıfır değil. en azından b'yi kontrol ederdi.
boş karakter "" hakkında konuşursak; Çizgiyi onlarla doldurduğumda çok iyi biliyordum ve amaç gerçek temizliği göstermekti ve gözle görülemezdi, çünkü çizgi bazen araya giren çöpleri içeriyordu. Kendiniz, "boş sonlandırma karakteri" veya sadece "boş karakter", bir sonlandırıcı değil) terimlerine daha dikkat edersiniz))) Ve sonlandırıcı karakter kulağa çok hoş geliyor.

Makaleyi modernize ediyorum ama başkasının tarzına geçmeyeceğim. Yeni başlayanlar için bu şekilde daha net olduğunu ve onun istediği gibi olmadığını düşünürsem, o şekilde bırakacağım. Sen de beni yanlış anlama. "İşaret" kelimesi, yeni başlayanların anlaması ve hatırlaması için, her bir işaretin tanımından ve adından çok daha kolaydır. Bunda kesinlikle bir hata yok, işaret - işaret. Bir şeye daha az vurgu, diğerine daha fazla vurgu yapar.

Diğer hataları da düzeltmekten mutlu olacağım. açılırlarsa yanlışlıkları düzeltin. Yardımını takdir ediyorum. Teşekkürler.

Tekrar merhaba!
Netleştirmek istiyorum. "Sıfır terminatör" (terminatör) terimi üniversitedeki hocam tarafından kullanılıyordu. Görünüşe göre bu eski okul!
Boş satırlara gelince.
karakter b = (); Bu gerçekten sıfırlanıyor. Tüm dizi sıfırlarla doldurulur. İnanmıyorsanız, kontrol edin!
Bir dizgiyi doğal, günlük anlamıyla ele alırsak, o zaman "boş", içinde tek bir karakter olmayan dize olacaktır. Bu nedenle, vakaların %99,9'unda başına bir boş karakter eklemek yeterlidir. Genellikle, bir dizenin işlenmesi ilk boş karaktere kadar gider ve onu hangi karakterlerin takip ettiği artık önemli değildir. Dizeyi sıfırlamak istediğini anlıyorum. Sadece zamana göre test edilmiş bir klasik versiyon sunmaya karar verdim.

:
"Genellikle dizenin işlenmesi ilk boş karaktere kadar gider ve hangi karakterlerin onu takip ettiği artık önemli değildir" - evet, dize sıfırlanır
"Dizedeki (hakkında yazdığım) tüm hücrelerin gerçek sıfırlamasını" düşünürsek - hayır, sıfırlanmaz ve hatta ilk karakter bile sıfır değildir. Bu seçeneği işaretledim. MinGW (CodeBlock) - tüm dizi "a" karakterini verir
Bunun bir tartışma nedeni olduğunu düşünmüyorum.

Bir programda dizeler aşağıdaki gibi tanımlanabilir:

  • dizi sabitleri olarak;
  • karakter dizileri olarak;
  • bir karakter tipine bir işaretçi aracılığıyla;
  • dizi dizileri olarak.

Ek olarak, dizeyi depolamak için bir bellek tahsisi olmalıdır.

Çift tırnak "" içine alınmış herhangi bir karakter dizisi, dizi sabiti.

Doğru çıktı için, herhangi bir dize, tamsayı değeri 0 olan bir boş karakter "\ 0" ile bitmelidir. Bir dize sabiti bildirilirken, buna otomatik olarak bir boş karakter eklenir. Böylece, bir dizge sabiti olan bir dizi karakter, sıfır bayt da dahil olmak üzere bilgisayarın RAM'inde yer alacaktır.

Diziyi depolamak için sıralı bellek hücreleri tahsis edilir. Bu nedenle, dize bir karakter dizisidir. Dizedeki her karakterin kodunu saklamak için 1 bayt tahsis edilir.

Hizmet karakterlerinden bazılarını bir dizge sabitine yerleştirmek için sembolik kombinasyonlar kullanılır. Bu nedenle, bir dizeye çift tırnak karakteri eklemek istiyorsanız, önüne ters eğik çizgi karakteri gelmelidir: '\' .

Dize sabitleri statik bellekte tahsis edilir. Çift tırnak içindeki bir dizi karakterin başlangıç ​​adresi, bir dize adresi olarak kabul edilir. Dize sabitleri genellikle printf() gibi işlevlerde kullanıcıyla iletişim kurmak için kullanılır.

Belirlenmesinde karakter dizisi derleyiciye gerekli bellek boyutunu söylemek gerekir.

Cazibe;

Dizi başlatma, bir dize sabiti olarak bildirilirken belirtilirse, derleyici karakter dizisinin boyutunu bağımsız olarak da belirleyebilir:

karakter m2 =;
karakter m3 = ( "T", "ve", "x", "ve", "e", "", "d", "o", "l", "ve", "n", "s", "", "p", "o", "l", "n", "s", "", "s", "v", "e", "g", "e", "y", "", "m", "g", "l", "o", "y", "\ 0"};

Bu durumda, m2 ve m3 isimleri dizilerin ilk elemanlarına işaret eder:

  • m2, & m2'ye eşittir
  • m2 'G'ye eşittir
  • m2 'o'ya eşittir
  • m3, & m3'e eşittir
  • m3 'x'e eşittir

Bir karakter dizisi bildirirken ve onu bir dize sabitiyle başlatırken, dizinin boyutunu açıkça belirtebilirsiniz, ancak dizinin belirtilen boyutu, başlatma dizesi sabitinin boyutundan daha büyük olmalıdır:

karakter m2 = "Dağ zirveleri gecenin karanlığında uyur.";

Bir dize belirtmek için kullanabilirsiniz karakter türüne işaretçi.

karakter * m4;

Bu durumda, bir dizinin m4 değişkenine bildirilmesine dizinin adresi atanabilir:

m4 = m3;
* m4, m3 = "T" ile eşdeğerdir
* (m4 + 1), m3 = "ve" ile eşdeğerdir

Burada m3 bir işaretçi sabitidir. m3'ü değiştiremezsiniz, çünkü bu, m4'ün aksine dizinin bellekteki konumunu (adresini) değiştirmek anlamına gelir.

Bir işaretçi için artırma işlemini kullanabilirsiniz (bir sonraki karaktere geçin):

Karakter dizileri

Bazen programlarda bir açıklamaya ihtiyaç vardır karakter dizisi dizisi... Bu durumda, birden çok farklı satıra erişmek için satır dizinini kullanabilirsiniz.

karakter * şair = ( "Şair öldü!", "- namus kölesi-",
"Arkadaş", "söylentilerle iftira attı ..."};

Bu durumda şair, karakter dizilerine yönelik dört işaretçi dizisidir. Her karakter dizisi bir karakter dizisidir, dolayısıyla dizilere yönelik dört işaretçi vardır. Şair işaretçisi ilk satıra atıfta bulunur:
* şair eşittir "NS",
* şair [l] eşittir "-" .

Başlatma, diziler için tanımlanan kurallara göre gerçekleştirilir.
Alıntılanan metinler, dizideki her dizeyi başlatmaya eşdeğerdir. Virgül bitişik ayırır
sıra.
Ayrıca, aşağıdaki gibi bir açıklama kullanarak karakter dizilerinin boyutunu açıkça ayarlayabilirsiniz:

karakter şairi;

Aradaki fark, bu şeklin tüm çizgilerin aynı uzunlukta olduğu bir "dikdörtgen" diziyi tanımlamasıdır.

Ücretsiz dizi

Açıklama

char * şair;


her dizenin uzunluğunun bu dizeyi başlatan işaretçi tarafından belirlendiği serbest bir dizi tanımlar. Boş bir dizi belleği boşa harcamaz.

dize işlemleri

C'nin dize işlemlerinin çoğu işaretçiler üzerinde çalışır. RAM'e bir karakter dizisi yerleştirmek için şunları yapmalısınız:

  • bir dizi için bir RAM bloğu tahsis edin;
  • dizeyi başlat.

Bir dizgeyi depolamak için bellek ayırmak için dinamik bellek ayırma işlevleri kullanılabilir. Bu durumda, gerekli satır boyutunu dikkate almak gerekir:

karakter ismi;
isim = (char *) malloc (10);
scanf ("% 9s", isim);

Bir dize girmek için scanf () işlevi kullanıldı ve giriş dizesi 9 karakteri aşamaz. Son karakter "\ 0" içerecektir.

Dize giriş işlevleri

scanf () işlevi, bir dize girmek için kullanılabilir. Bununla birlikte, scanf () işlevi, bir dize yerine bir sözcük almak içindir. Giriş için "% s" biçimini kullanırsanız, dize, boşluk, sekme veya satır beslemesi olabilen bir sonraki boş karakterden önce (ancak dahil değil) girilir.

Boşluklar dahil bir dize girmek için işlevi kullanın

char * alır (char *);


veya eşdeğeri

karakter * get_s (char *);

Giriş dizesine bir işaretçi, işleve argüman olarak iletilir. İşlev, kullanıcı tıklayana kadar kullanıcıdan bir diziye koyduğu bir dize girmesini ister. Girmek.

Dize çıkış işlevleri

Çizgileri görüntülemek için daha önce tartışılan işlevi kullanabilirsiniz.

printf ("% s", str); // str bir dizgeye işaretçidir

veya kısaltılmış biçimde

printf(str);

İşlev, dizeleri çıktılamak için de kullanılabilir

int koyar (char * s);

bu, s dizesini yazdırır ve imleci yeni bir satıra taşır (printf ()'den farklı olarak). puts () işlevi, alıntılanan dize sabitlerinin çıktısını almak için de kullanılabilir.

Karakter giriş işlevi

İşlev, karakterleri girmek için kullanılabilir.

char getchar();


klavyeden girilen karakterin değerini döndürür. Belirtilen işlev, programı çalıştırdıktan sonra bir tuşa basılana kadar konsol penceresini geciktirmek için daha önce tartışılan örneklerde kullanıldı.

Karakter çıkış fonksiyonu

Karakterleri görüntülemek için işlev kullanılabilir

char putchar (char);


bu, görüntülenecek karakterin değerini döndürür ve ekrana argüman olarak iletilen karakteri yazdırır.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

#Dahil etmek
#Dahil etmek
#Dahil etmek
int ana () (
karakter s, sym;
int sayısı, i;
sistem ("chcp 1251");
sistem ("cls");
yazdır ( "Dizeyi girin:");
get_s(ler);
yazdır ( "Karakteri girin:");
sym = getchar();
sayı = 0;
for (i = 0; s [i]! = "\ 0"; ben ++)
{
if (s [i] == sym)
saymak ++;
}
printf ("Çevrimiçi \ n");
koyar (lar); // Hat çıkışı
printf ("karakter");
putchar (sym); // Sembolü göster
yazdır ( "% d kez oluşur", saymak);
getchar(); getchar();
0 döndür;
}

Yürütme sonucu

Standart kitaplık string.h'nin temel işlevleri

Standart string.h kitaplığının ana işlevleri tabloda gösterilmiştir.

İşlev Açıklama

karakter * strcat (char * s1, karakter * s2)

s2'yi s1'e birleştirir, s1'i döndürür

karakter * strncat (char * s1, karakter * s2, int n)

s2'den s1'e en fazla n karakter ekler, dizeyi "\ 0" ile bitirir, s1 değerini döndürür

karakter * strcpy (char * s1, karakter * s2)

s2 dizesini "\ 0" dahil olmak üzere s1 dizesine kopyalar, s1 değerini döndürür
);
strncpy (m3, m1, 6); // satırın sonuna "\ 0" eklemez
koyar ( "Sonuç strncpy (m3, m1, 6)");
koyar (m3);
strcpy (m3, m1);
koyar ( "strcpy sonucu (m3, m1)");
koyar (m3);
koyar ( "strcmp (m3, m1) sonucu");
printf ("% d", strcmp (m3, m1));
strncat (m3, m2, 5);
koyar ( "Sonuç strncat (m3, m2, 5)");
koyar (m3);
strcat (m3, m2);
koyar ( "Sonuç strcat (m3, m2)");
koyar (m3);
koyar ( "m1 dizesindeki karakter sayısı strlen'e (m1) eşittir:");
printf ("% d \ n", strlen (m1));
_strnset (m3, "f", 7);
koyar ( "Sonuç strnset (m3," f ", 7)");
koyar (m3);
_strset (m3, "k");
koyar ( "Sonuç strnset (m3," k ")");
koyar (m3);
getchar();
0 döndür;
}

Yürütme sonucu

C ve C++ işlev kitaplığı, zengin bir dizi ve karakter işleme işlevi içerir. Dize işlevleri, boş sonlandırılmış karakter dizilerinde çalışır. C dilinde, dize işlevlerini kullanmak için program modülünün başına bir başlık dosyası eklemelisiniz. , ve sembolik - başlık dosyası için ... C++, dize ve karakter işlevleriyle çalışmak için üstbilgileri kullanır. ve sırasıyla. Bu bölüm, basitlik için C-başlık adlarını kullanır.

C ve C++ dillerinde dizilerle işlem yaparken, sınırlarının ihlalinin otomatik kontrolü sağlanmadığından, dizilerin taşması için tüm sorumluluk programcının omuzlarına düşer. Bu incelikleri ihmal etmek programın çökmesine neden olabilir.

C ve C++'da yazdırılabilir karakterler terminalde görüntülenen karakterlerdir. ASCII ortamlarında, bir boşluk (0x20) ve bir tilde (OxFE) arasında bulunurlar. Kontrol karakterleri sıfır ile Ox1F arasında değerlere sahiptir; bunlar ayrıca DEL sembolünü (Ox7F) içerir.

Tarihsel olarak, sembolik işlevlere ilişkin argümanlar, yalnızca en az anlamlı baytın kullanıldığı tamsayı değerleridir. Sembolik işlevler, argümanlarını otomatik olarak işaretsiz karaktere dönüştürür. Elbette, bu işlevleri sembolik argümanlarla çağırmakta özgürsünüz, çünkü işlev çağrıldığında semboller otomatik olarak tamsayılar derecesine yükseltilir.

başlıkta Sizeof operatörünün sonucu olan ve bir tür işaretsiz tamsayı olan size_t türü tanımlanmıştır.

C99, orijinal olarak C89'da tanımlanan çeşitli işlevlerin bazı parametrelerine kısıtlama niteleyicisini ekledi. Bu tür her bir işlev, C89 ortamında (C++ ortamında olduğu gibi) kullanılan prototipi ile gözden geçirilecek ve kısıtlama özniteliğine sahip parametreler bu işlevin açıklamasında belirtilecektir.

Özellik listesi

ait olup olmadığını kontrol edin

isalnum - Bir karakterin alfasayısal olup olmadığını kontrol edin
isalpha - Bir karakterin harflere ait olup olmadığını kontrol edin
isblank - Boş bir karakter olup olmadığını kontrol edin
iscntrl - Bir sembolün kontrole ait olup olmadığını kontrol edin
isdigit - Bir karakterin dijital olup olmadığını kontrol edin
isgraph - Bir karakterin yazdırılabilir olup olmadığını ancak boşluk olmadığını kontrol edin
islower - Bir karakterin küçük harf olup olmadığını kontrol edin
isprint - Bir karakterin yazdırılabilir olup olmadığını kontrol edin
ispunct - Bir karakterin noktalama işaretlerine ait olup olmadığını kontrol edin
isspace - Bir karakterin boşluk olup olmadığını kontrol edin
isupper - Bir karakterin büyük harf olup olmadığını kontrol edin
isxdigit - Bir karakterin onaltılık olup olmadığını kontrol edin

Karakter dizileriyle çalışma

memchr - Bir karakterin ilk oluşumunu bulmak için dizide dolaş
memcmp - Belirtilen sayıda karakteri iki dizide karşılaştırır
memcpy - Karakterleri bir diziden diğerine kopyalar
memmove - Çakışan dizileri hesaba katarak karakterleri bir diziden diğerine kopyalar
memset - Bir dizideki belirli sayıda karakteri verilen bir karakterle doldurur

dize manipülasyonu

strcat - Verilen dizenin bir kopyasını ekler
strchr - Verilen parametrenin en az anlamlı baytının ilk oluşumuna bir işaretçi döndürür
strcmp - İki diziyi sözlükbilimsel olarak karşılaştırır
strcoll - Bir diziyi setlocale'e göre diğeriyle karşılaştırır
strcpy - Bir dizenin içeriğini diğerine kopyalar
strcspn - Belirtilen karakterlerin eksik olduğu bir dize döndürür
strerror - Sistem hata mesajını içeren dizeye bir işaretçi döndürür
strlen - Boş sonlandırılmış bir dizenin uzunluğunu döndürür

C++ dizileri

Bir dize, bir karakter dizisidir (dizi). Bir ifadede tek bir karakter varsa, bu karakterin içine alınmalıdır. tek tırnak... İfadelerde kullanıldığında, dize içine alınır. ikili alıntı. Satırın sonu boş bir karakterdir \0 ... C++'da, karakter dizileri semboller kullanılarak tanımlanabilir (bir dizi karakter), satır sonlandırıcıyı saklamak için bir yer sağlamalıdır.

Örneğin, 25 karakterlik bir dize açıklaması şöyle görünmelidir:

Ayrıca bir dizi diziyi de tanımlayabilirsiniz:

Her biri 25 baytlık 3 satırlık bir dizi tanımlanmıştır.

İşaretçilerle çalışmak için kullanabilirsiniz ( karakter *). İlk karakterin adresi, işaretçinin başlangıç ​​değeri olacaktır.

Dizeleri bildirme ve görüntüleme örneğine bakalım.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

#include "stdafx.h"
#Dahil etmek
ad alanı std kullanarak;
int ana ()
{
setlocale (LC_ALL, "Rus");
// 3 satırı tanımla, s3 bir işaretçidir
karakter s2 [20], * s3, s4 [30];
cout<< «s2=» ; cin >> s2; // s2 dizgisini gir
cout<< «s2=» << s2<< endl;
// s4'ün depolandığı dizgenin adresini s3'e yaz. Şimdi değişkenlerde
// (işaretçiler) s3 ve s4 aynı adresin değerini saklar
s3 = s4;
cout<< «s3=» ; cin >> s3; // s3 dizgisini gir
// s3 ve s4 satırlarını göster, ancak s3 = s4 atamasının bir sonucu olarak;
// şimdi s3 ve s4 aynı
cout<< «s3=» << s3<< endl;
cout<< «s4=» << s4<< endl;
sistem ("duraklat");
0 döndür;
}

Programın sonucu:

Ancak, kullanıcı boşlukla ayrılmış kelimeleri tek bir değişkene girerse, programın farklı çalışacağına dikkat edilmelidir:

Mesele şu ki, fonksiyon Cin karşılaşılan boşluktan önceki satırlara girer. Daha çok yönlü bir işlev hat almak.

cin.getline (char * s, int n);

Klavye giriş dizeleri için tasarlandı s boşluklarla, satır şundan fazlasını içermemelidir: n karakterler. Bu nedenle, boşluk içeren satırların doğru girişi için, programımızda değiştirilmesi gerekir. cin >> süzerinde cin.getline (s, 80).

dize işlemleri

Bir dize, bazıları aşağıda listelenen dizi işleme algoritmaları veya özel dize işleme işlevleri kullanılarak bir karakter dizisi olarak ele alınabilir. Bu çizgilerle çalışmak için kütüphaneyi bağlamanız gerekir. cstring.

Bir sayıyı bir dizgeye dönüştürmek için işlevi kullanabilirsiniz. sprintf kütüphaneden stdio.h.

Dizelerle çalışmak için bazı işlevler:

Fonksiyon prototipi İşlev açıklaması
size_t strlen (const char * s) bayt cinsinden s dizesinin uzunluğunu hesaplar.
char * strcat (char * hedef, const char * scr) src dizesini hedef dizesinin sonuna ekler, elde edilen terim sonuç olarak döndürülür
char * strcpy (char * hedef, const char * scr) scr dizesini hedef ile gösterilen bellek konumuna kopyalar
char strncat (char * hedef, const karakter * hedef, size_t maxlen) src'nin maxlen karakter dizisini hedefin sonuna ekler
char * strncpy (char * hedef, const karakter * scr, size_t maxlen) src'nin maxlen karakterlerini hedef ile gösterilen bellek konumuna kopyalar
int ctrcmp (const char * s1, const char * s2) iki dizeyi sözlükbilimsel sırayla karşılaştırır, büyük ve küçük harfler arasındaki farkı dikkate alır, işlev dizeler aynıysa 0, s1 s2'den önce alfabetik sıradaysa - 1, değilse 1 döndürür.
int strncmp (const char * s1, const char * s2, size_t maxlen) iki dizenin maxlen karakterlerini sözlük sırasına göre karşılaştırır, işlev dizeler eşleşirse 0, s1 s2'den önce alfabetik sıradaysa - 1, aksi takdirde 1 döndürür.
çift ​​atof (const char * s) dizeyi gerçek bir sayıya dönüştürür, başarısız dönüştürme durumunda 0 sayısı döndürülür
uzun atol (const char * s) dizeyi uzun bir tam sayıya dönüştürür, dönüştürme başarısız olursa 0 döndürülür
karakter * strchr (sabit karakter * s, int c); bir karakterin ilk oluşumuna bir işaretçi döndürür C işaret ettiği dizeye s... eğer sembol C bulunamadı, NULL döndürür
karakter * strupr (char * s) s ile gösterilen dizenin karakterlerini büyük harfe dönüştürür ve sonra onu döndürür

Dize veri türü

Bir dizi karakterde olduğu gibi dizelerle çalışmaya ek olarak, C++'da özel bir veri türü vardır. sicim... Bu türdeki değişkenleri girmek için şunu kullanabilirsiniz: Cin veya özel işlev hat almak.

getline (cin, s);

Buraya s- girdi türü değişkeninin adı sicim.

Bu tip bir değişkeni tanımlarken, hemen bu değişkene değer atayabilirsiniz.

string var(lar);

Buraya var- değişken ismi, s- dize sabiti. Bu operatörün bir sonucu olarak bir değişken oluşturulur. var tip sicim, ve dize sabitinin değeri buna yazılır s... Örneğin,

string v ("Merhaba");

Bir dize oluşturulur v, içine değerin yazıldığı Merhaba.

Erişim bençizgi öğesi s tip sicim standart bir şekilde gerçekleştirilir s [i]... gibi satırların üstünde sicim aşağıdaki işlemler tanımlanmıştır:

  • atamalar, örneğin s1 = s2;
  • dize bitiştirme (s1 + = s2 veya s1 = s1 + s2) - s2 dizesini s1 dizesine ekler, sonuç s1 dizesinde depolanır dize bitiştirme örneği:
  • sözlük sırasına göre dizi karşılaştırmaları: s1 = s2, s1! = s2, s1 s2, s1<=s2, s1>= s2 - sonuç bir boole değeri olacaktır;

Gibi dizeleri işlerken sicim aşağıdaki işlevler kullanılabilir:

  • s.substr (konum, uzunluk)- bir dizgeden bir alt dizgi döndürür s numaradan başlayarak konum uzun uzunluk karakterler;
  • s.boş ()- dize ise true döndürür s boş, aksi takdirde yanlış;
  • s.insert (konum, s1)- bir satır ekler s1Çizgide s pozisyondan başlayarak konum;
  • s.remove (konum, uzunluk)- dizeden kaldırır s alt dize uzunluk uzun konum karakterler;
  • s.bul (s1, konum)- dizenin ilk oluşumunun numarasını döndürür s1Çizgide s, arama numara ile başlar konum, parametre konum olmayabilir, bu durumda arama satırın başından başlar;
  • s.findfirst (s1, konum)- bir dizeden herhangi bir karakterin ilk geçtiği yerin sayısını döndürür s1Çizgide s, arama numara ile başlar konum hangi eksik olabilir.

dizeler için Rusça

Sanırım Rus harflerini görüntülerken konsolda "sol" karakterlerin göründüğünü fark etmişsinizdir. Bu yanlış anlaşılmayı önlemek için bir üçüncü taraf işlevi kullanmanız gerekir. CharToOemA... Kütüphaneyi bağladık windows.h, işlevimizin dizeleri farklı bir kodlamaya dönüştürebilmesi için gereklidir. Ayrıca, ek bir karakter dizisine ihtiyacımız var. Programın kaynak kodu şöyle görünecektir:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

#include "stdafx.h"
#Dahil etmek
#Dahil etmek
ad alanı std kullanarak;
int ana ()
(setlocale (LC_ALL, "Rus");
karakter [255] = ( "Dönüştürülmem gerekiyor"} ;
char * ön = yeni karakter [255];
CharToOemA (s, ön); // dönüştürmek
cout<< s;
ön silme;
sistem ("duraklat >> geçersiz");
0 döndür;
}

Az önce açıklanan yöntem yeterince uygun değil. Ancak "Rus" sorununa daha basit bir çözüm var. Görüldüğü gibi program setlocale() fonksiyonunu kullanmaktadır, bunun yerine ana fonksiyona aşağıdaki yapıyı yazmak daha uygundur.