Karakter dizileri dize olarak nasıl kullanılmalıdır?


10

C'deki dizelerin sadece karakter dizileri olduğunu anlıyorum. Bu yüzden aşağıdaki kodu denedim, ancak çöp çıktısı veya program çökmeleri gibi garip sonuçlar veriyor:

#include <stdio.h>

int main (void)
{
  char str [5] = "hello";
  puts(str);
}

Bu neden çalışmıyor?

Temiz bir şekilde derler gcc -std=c17 -pedantic-errors -Wall -Wextra.


Not: Bu yazı, bir dize bildirilirken bir NUL sonlandırıcı için yer ayrılmamasından kaynaklanan sorunlar için standart bir SSS olarak kullanılmak üzere tasarlanmıştır.

Yanıtlar:


12

AC dize, boş bir sonlandırıcıyla biten bir karakter dizisidir .

Tüm karakterlerin sembol tablosu değeri vardır. Boş sonlandırıcı sembol değeridir 0(sıfır). Bir dizenin sonunu işaretlemek için kullanılır. Dizenin boyutu hiçbir yerde depolanmadığından bu gereklidir.

Bu nedenle, bir dize için her oda ayırdığınızda, boş sonlandırıcı karakteri için yeterli alan eklemeniz gerekir. Örneğiniz bunu yapmaz, yalnızca 5 karakteri için yer ayırır "hello". Doğru kod şöyle olmalıdır:

char str[6] = "hello";

Veya eşdeğer olarak, 5 karakter artı 1 boş sonlandırıcı için kendi kendini belgeleyen kod yazabilirsiniz:

char str[5+1] = "hello";

Çalışma zamanında bir dizeye dinamik olarak bellek ayırırken, boş sonlandırıcı için de yer ayırmanız gerekir:

char input[n] = ... ;
...
char* str = malloc(strlen(input) + 1);

Bir dizenin sonuna boş bir sonlandırıcı eklemezseniz, bir dizeyi bekleyen kütüphane işlevleri düzgün çalışmaz ve çöp çıktısı veya program çökmeleri gibi "tanımlanmamış davranış" hataları alırsınız.

C boş sonlandırıcı karakteri yazmak için en yaygın yolu, bu gibi bakarak, bir sözde "sekizlik kaçış dizisi" kullanmaktır: '\0'. Bu, yazmaya% 100 eşdeğerdir 0, ancak \sıfırın açıkça boş bir sonlandırıcı olması gerektiğini belirtmek için kendi kendini belgeleyen kod görevi görür. Gibi kodif(str[i] == '\0') belirli karakter boş sonlandırıcı olup olmadığını kontrol edecektir.

Null sonlandırıcı teriminin null işaretçiler veya NULLmakro ile ilgisi olmadığını lütfen unutmayın ! Bu kafa karıştırıcı olabilir - çok benzer isimler ama çok farklı anlamlar. Bu nedenle null sonlandırıcı bazen NULbir L ile birlikte olarak adlandırılır , karıştırılmamalıdır NULLveya null işaretçiler olarak adlandırılır. Daha fazla ayrıntı için bu SO sorunun yanıtlarına bakın .

"hello"Kodunuzdaki bir denir dize . Bu salt okunur bir dize olarak kabul edilmelidir. ""Derleyici değişmezi otomatik dizesinin sonunda boş terminatör ekler o sözdizimi anlamına gelir. Bu nedenle, çıktı sizeof("hello")alırsanız 5 değil 6 elde edersiniz, çünkü boş bir sonlandırıcı içeren dizinin boyutunu elde edersiniz.


Gcc ile temiz bir şekilde derler

Gerçekten de bir uyarı bile yok. Bunun nedeni, C dizisindeki, karakter dizilerinin, dizide yer olduğu kadar çok karakter içeren bir dize değişmez değeri ile başlatılmasına izin veren ve ardından boş sonlandırıcıyı sessizce atayan ince bir ayrıntı / kusurdan kaynaklanmaktadır (C17 6.7.9 / 15). Dil, tarihsel nedenlerle bilerek böyle davranıyor, ayrıntılar için dize başlatma için Tutarsız gcc tanı bölümüne bakın. Ayrıca burada C ++ 'nın farklı olduğunu ve bu hile / kusurun kullanılmasına izin vermediğini unutmayın.


1
Davadan bahsetmelisin char str[] = "hello";.
Jabberwocky

@Jabberwocky Bu bir topluluk wiki'si, düzenlemek ve katkıda bulunmaktan çekinmeyin.
Lundin

1
... ve belki de char *str = "hello";... str[0] = foo;problemi.
Jabberwocky

Belki de sizeofbir işlev parametresindeki kullanımının kullanımını, özellikle bir dizi olarak tanımlandığında, genişletebilirsiniz .
Weather Vane

@WeatherVane Burada başka bir SSS tarafından ele alınmalıdır: stackoverflow.com/questions/492384/…
Lundin

4

C Standardından (7.1.1 Terimlerin tanımları)

1 Dize, ilk null karakterle ve bu null karakterle sona eren bitişik karakter dizisidir. Çok baytlı dize terimi bazen dizede bulunan çok baytlı karakterlere verilen özel işlemeyi vurgulamak veya geniş bir dizeyle karışıklığı önlemek için kullanılır. Bir dizeye işaretçi, başlangıç ​​(en düşük adresli) karakterine bir işarettir. Bir dizenin uzunluğu, boş karakterden önceki bayt sayısıdır ve bir dizenin değeri, sırayla içerilen karakterlerin değerlerinin sırasıdır.

Bu beyanda

char str [5] = "hello";

dizgi değişmezi "hello",

{ 'h', 'e', 'l', 'l', 'o', '\0' }

yani sonlandırıcı sıfır da dahil olmak üzere 6 karakteri vardır. Öğeleri karakter dizisini başlatmak için kullanılırstr yalnızca 5 karakter için yer ayıran .

C Standardı (C ++ Standardının tersi), bir dize hazır bilgisinin sonlandırma sıfırının bir başlatıcı olarak kullanılmadığı durumlarda bir karakter dizisinin bu şekilde başlatılmasına izin verir.

Ancak sonuç olarak karakter dizisi str bir dize içermez.

Dizinin yazabileceğiniz bir dize içermesini istiyorsanız

char str [6] = "hello";

ya da sadece

char str [] = "hello";

Son durumda karakter dizisinin boyutu, dizge değişmezinin 6'ya eşit olan başlatıcı sayısından belirlenir.


0

Tüm Can dizeleri bir düşünülebilir karakterlerin dizisi ( Evet ), tüm edebilirsiniz karakter diziler düşünülebilir dizeleri ( Yok ).

Neden olmasın? ve neden önemli?

Bir dizenin uzunluğunun dizenin bir parçası olarak hiçbir yerde saklanmadığını ve bir dizenin tanımlandığı standarda yapılan referansları açıklayan diğer cevaplara ek olarak, diğer "C kütüphanesi fonksiyonları dizeleri nasıl işler?"

Bir karakter dizisi aynı karakterleri tutabilirken, son karakteri nul sonlandırıcı karakter izlemediği sürece, bu sadece bir karakter dizisidir . Bu nul sonlandırıcı karakter, karakter dizisinin bir dize olarak ele alınmasına (işlenmesine) izin veren karakterdir.

C'de bir dizeyi bağımsız değişken olarak bekleyen tüm işlevler, karakter dizisinin boş bırakılmasını bekler . Neden?

Tüm dize işlevlerinin çalışma şekliyle ilgilidir. Uzunluk bir dizinin parçası olarak yer almadığından, dize işlevleri, nul karakter (örn. '\0'- ondalık sayıya eşdeğer 0) bulunana kadar dizide ileriye doğru tarayın . Bkz. ASCII Tablosu ve Açıklaması . Kullandığınız olursa olsun strcpy, strchr, strcspn, vb .. Bütün dize fonksiyonları itimat nul-sonlandırma İpin ucunun nerede tanımlamak için karakter olmanın günümüze.

İki benzer fonksiyonun karşılaştırılması string.h, nul sonlandırıcı karakterin önemini vurgulayacaktır . Örnek verelim:

    char *strcpy(char *dest, const char *src);

strcpyBasitçe kopya fonksiyonu gelen bayt srciçin destkadar nul-sonlandırma karakterin olduğu anlatma strcpykarakterleri kopyalamaya durdurmak için. Şimdi benzer işlevi alın memcpy:

    void *memcpy(void *dest, const void *src, size_t n);

İşlev benzer bir işlem gerçekleştirir, ancak srcparametrenin bir dize olduğunu düşünmez veya gerektirmez . Çünkü memcpysadece ileriye doğru tarayamazsrc için bayt kopyalama destbir kadar nul-sonlandırma karakteri ulaşıldığında, bir üçüncü parametre olarak kopyalamak için bayt açık sayıda gerektirir. Bu üçüncü parametre memcpy, aynı boyut bilgisini sağlar strcpy; boş bir sonlandırma karakteri bulunana kadar ileriye doğru tarayarak türetilebilir .

(bu da neyin yanlış gittiğini vurgular strcpy , işleve nul sonlandırılmış bir dize sağlamadığınız takdirde (veya bir dize bekleyen herhangi bir işlevi) - nerede duracağına dair hiçbir fikri yoktur ve bellek bölümünüzün geri kalanında mutlu bir şekilde yarışacaktır çağırma Tanımsız Davranışı bir kadar nul-karakteri ) ya da bir segmentasyon Arıza oluştuğunda - sadece bellekte bir yere bulunabilir olur

Yani neden bir bekliyor fonksiyonları nul-sonlandırılmış dize bir geçirilmelidir nul-sonlandırılmış dize ve öneminden söz .


0

Sezgisel ...

Bir diziyi bir değişken (şeyleri tutar) ve bir dizeyi bir değer (bir değişkene yerleştirilebilir) olarak düşünün.

Kesinlikle aynı şey değiller.Sizin durumunuzda, değişken dizeyi tutamayacak kadar küçük olduğundan dize kesilir. (C'deki "alıntılanan dizeler", sonunda örtük bir boş karakter içerir.)

Ancak bir dizeyi çok daha büyük bir dizide saklamak mümkündür olan .

Normal atama ve karşılaştırma işleçlerinin ( = == <vb.) Beklediğiniz gibi çalışmadığını unutmayın. Ancak strxyzne yaptığınızı bildiğinizde, işlev ailesi oldukça yakındır. Bkz C SSS üzerinde dizeleri ve diziler .

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.