• C'deki dize işlevlerine örnekler. C'de karakter dizilerinin girişi ve çıkışı

    Dize bildirimleri

    C dilinde bir dize, son öğesi satır sonu karakteri olan sıfır (null ile sonlandırılmış bir dize, yani NULL ile sonlandırılmış dize) olan tek boyutlu bir karakter dizisidir.

    Duyuru tip değişkeni C'de bir dizge üç şekilde mümkündür, bunlardan ikisi dizgiyi bildirim zamanında başlatır.

    İlk yol:

    Karakter dizisi bildirimleri (sonlandırıcı null için boşluk eklemeyi unutmayın):

    karakterler;

    İkinci yol:

    Bir dizi değişkenine bir başlangıç ​​değeri atayın (bu durumda derleyici, dizenin uzunluğunu kendisi hesaplayabilir):

    Char s = "Dize başlatma örneği";

    Atama işaretinin sağında bir dizi sabiti bulunur. Dizenin sonuna otomatik olarak bir sıfır ('\0') eklenir. Karakter dizisi sabitleri, statik bir depolama sınıfına yerleştirilir.

    Üçüncü yol:

    Bir dizinin kullanılmakta olduğuna dair örtük bir gösterge. Atama işaretinin sol tarafında, sembole bir işaretçi gösterilir:

    Char *s="İkinci başlatma seçeneği";

    s değişkeni, içindeki o yere bir işaretçi olacaktır. rasgele erişim belleği, dize sabitinin bulunduğu yer. Bu gösterim biçimi, bir karaktere işaretçiye genellikle bir dize olarak atıfta bulunulduğu için potansiyel bir hataya sahiptir. Dize için boşluk olmadığından, aşağıdaki giriş yalnızca bir karaktere yönelik bir işaretçidir:

    karakter*ler;

    Standart giriş cihazından (klavye) bir dizi girme

    Dizelerle çalışmak için bir dizi işlev vardır. Standart giriş cihazından (klavye) giriş için, standart giriş / çıkış modülünden kütüphane işlevleri çoğunlukla kullanılır: taramak Ve alır.

    İşlevi kullanarak bir dize girmek için taramak, biçimi kullanır « %S» ve adres işaretinin satır tanımlayıcısından önce kullanılmadığına dikkat edin. « & » , tek boyutlu bir dizi zaten başlangıcına bir işaretçi ile temsil edildiğinden:

    scanf("%s",s);

    İşlev alır() bir atlama karakterine ulaşana kadar karakterleri okur Yeni hat. İşlev, yeni satır karakteri hariç tüm karakterleri kabul eder. Dizenin sonuna sonlandırıcı bir sıfır ('\0') eklenir. İşlev alır() klavyeden okunan bir karakter dizisini bir dize parametresine koyar ve bu dizeye (işlem başarılıysa) veya NULL'a (bir hata durumunda) bir işaretçi döndürür. Aşağıdaki örnekte, işlemin başarıyla tamamlanmasının ardından ekranda birbirinin aynı iki satır görüntülenecektir:

    #katmak int main() ( char s; char *p; p=gets(s); printf(" \n String %s girildi. ",s); if (p) printf(" \n String %s girildi. ", p); 0 döndürür; )

    Geçerken, get işlevinin genellikle klavyeden herhangi bir veriyi sscanf işlevi tarafından daha fazla dönüştürme amacıyla bir dize olarak girmek için kullanıldığını not ediyoruz. istenilen biçim veya giriş verilerinin ön analizi için, örneğin:

    #katmak #katmak #katmak int main() ( char s; int x, err; do ( printf(" \n Bir tamsayı girin -> "); get(s); err=sscanf(s, "%d",&x); if (err) !=1) printf(" \n Giriş hatası. "); ) while (err!=1); printf("\n Tamsayı girildi -> %d", x); 0 döndür; )

    Dizeleri standart çıktıya yazdırma (monitör ekranı)

    Satırların çıktısını almak için standart cihazçıktı (monitör ekranı) iki işlev kullanılabilir printf Ve koyar. printf fonksiyonunda format olarak "%s" geçilir. Bu işlevi kullanmanın rahatlığı, dizeye ek olarak diğer türlerdeki verileri hemen görüntüleyebilmenizdir. özellik özelliği koyar satırın çıkışından sonra bir sonraki satıra geçişin otomatik olarak gerçekleşmesidir.

    Dize İşlevleri

    Dizeleri C dilinde dönüştürmek için dize kitaplığı sağlanır. İşlevlerin her birinin kendi kayıt formatı (prototipi) vardır.

    En çok kullanılan işlevler bu makalede ele alınmıştır. - Okumak

    Dizelerle çalışan programlara (listeleme) bir örnek

    Dizelerle ilgili konuyu "Diziler" bölümüne yerleştirmem tesadüf değil. Bir dize aslında bir karakter dizisi olduğundan. İşte bir örnek:

    char str = "Bu sadece bir dizgedir";

    Daha iyi anlaşılması için aynı satır şu şekilde yazılabilir:

    char str = ("E","t","o"," ","p","r","o","s","t","o","","s", "t", "p", "o", "k", "a");

    Onlar. hepsi aynı dizi, yalnızca karakterlerden oluşuyor. Bu nedenle, tıpkı tamsayı dizilerinde olduğu gibi onunla çalışabilirsiniz.

    şimdi deneyelim c'deki dizelerle çalışmak. Giriş derslerinde karakterlerin tamsayı türleri olduğunu tartışmıştık, örn. her karakterin kendi sayısal değeri vardır. İşte bir örnek ve çözümü:

    1. girilen kelimenin büyük harfe dönüştürülmesi gerekmektedir:
    2. #katmak
      #katmak

      int ana()
      {
      char str = "sergey";

      dizi[i] -= 32;
      }
      for (int i=0; str[i] != "\0";i++)(
      printf("%c", str[i]);
      }
      getch();

      0 dönüşü;
      }

      sayı kodunu almak için printf işlevinde %d belirleyicisini kullanmanız yeterlidir. Evet ve bir tane daha önemli nokta: herhangi birini bitirmek çizgiler belirtilen boş sonlandırıcıdır özel karakter - "\0".

    Bir dize belirtmenin başka bir yolu, onu char* aracılığıyla bildirmektir. İşte bir örnek:

    char*str = "tel";

    Onlar. bellekte bir yerde bulunan bir dizeye işaretçi oluşturulur.

    Zaten bize özgü olan scanf operatörü aracılığıyla dizileri şu şekilde girebilirsiniz:

    charstr; scanf("%s", str);

    Burada iki incelik var:

    1. dizinin adı zaten bildiğimiz gibi adres olduğu için burada adres alma işaretine gerek yoktur.
    2. Giriş dizisinin uzunluğu 15 karakteri geçmemelidir, çünkü sonuncusu boş bir sonlandırıcı olmalıdır. Ayrıca, derleyicinin kendisi bu karakteri son girdiğiniz karakterden sonra dolduracaktır.

    C dili yapılandırılmış bir dil olduğundan, zaten yerleşik işlevler vardır. dizelerle çalışmak ve sembollerle. Dizeleri işlemek için şu dosyayı eklemeniz gerekir: ctype.h. Dosya, durumu, karakter biçimini belirlemek için işlevler içerir. Temel olarak, bir sembol hakkında bilmeniz gereken her şey ctype.h dosya işlevleri kullanılarak yapılabilir.

    Bazen bir diziyi başka bir veri türüne dönüştürmeniz gerekebilir. Dizeleri diğer türlere dönüştürmek için bir stdlib kitaplığı vardır. İşte işlevleri:

    1. int atoi(char*str)
    2. uzun atol (char *str)
    3. çift ​​atof (char*str)

    Bazen, örneğin bir diziden bir yıl veya sayısal bir değer çıkarmanız gerektiğinde, bu işlevler çok yardımcı olur. c (si)'deki dizelerle çalışmaçok önemli bir konudur, bu yüzden bu dersi anlamaya çalışın.

    Teller. Hat girişi/çıkışı. Biçimlendirilmiş G/Ç. Standart C dil fonksiyonlarını kullanarak dizi işleme.Hafıza ile çalışma.

    1.1. Dizeleri bildirme ve başlatma.

    Bir dize, '\0' boş karakteriyle biten bir karakter dizisidir. Bir dizi, normal bir karakter dizisi olarak bildirilir, örneğin,

    karakter s1; // dokuz karakter uzunluğunda dize

    karakter*s2; // diziye işaretçi

    s1 ve s2 işaretçileri arasındaki fark, s1 işaretçisinin adlandırılmış bir sabit, s2 işaretçisinin ise bir değişken olmasıdır.

    Dize sabitleri, tek tırnak içine alınan karakterlerin aksine, çift tırnak içine alınır. Örneğin,

    "Bu bir dizi."

    Uzunluk dizi sabiti standart olarak 509 karakteri aşamaz. Ancak, birçok uygulama daha uzun dizilere izin verir.

    Dizeleri başlatırken, dizinin boyutunu belirtmemek daha iyidir; derleyici bunu dizenin uzunluğunu sayarak ve ona bir ekleyerek yapacaktır. Örneğin,

    char s1 = “Bu bir dizidir.”;

    C programlama dilinde, dizilerle çalışmak için, çok sayıda prototipleri stdlib.h ve string.h başlık dosyalarında açıklanan işlevler. Bu işlevlerle çalışmak aşağıdaki paragraflarda tartışılacaktır.

    1.2. Hat girişi/çıkışı.

    Konsoldan bir dize girmek için işlevi kullanın.

    char* alır(char*str);

    str adresine bir dize yazar ve giriş dizesinin adresini döndürür. İşlev, '\n' karakteri veya EOF (dosya sonu) ile karşılaşırsa girişi durdurur. Yeni satır karakteri kopyalanmaz. Okuma satırının sonuna boş bir bayt yerleştirilir. Başarı durumunda işlev, okuma satırına bir işaretçi ve başarısızlık durumunda NULL döndürür.

    Konsola bir dize çıktısı almak için standart işlevi kullanın

    int koyar (const char *s);

    başarı durumunda negatif olmayan bir sayı ve başarısızlık durumunda EOF döndürür.

    Gets ve puts işlevinin prototipleri stdio.h başlık dosyasında açıklanmıştır.

    #katmak

    printf("Giriş Dizesi: ");

    1.3. Biçimlendirilmiş G/Ç.

    Konsoldan biçimlendirilmiş giriş için işlevi kullanın

    int scanf (const char *format, ...);

    başarı durumunda okunan veri birimlerinin sayısını ve başarısızlık durumunda EOF'yi döndürür. format parametresi, giriş formatı belirtimlerini içeren bir format dizesine işaret etmelidir. Biçim dizesinden sonra gelen bağımsız değişkenlerin sayısı ve türleri, biçim dizesinde belirtilen giriş biçimlerinin sayısı ve türleri ile eşleşmelidir. Bu koşul karşılanmazsa, işlevin sonucu tahmin edilemez.

    Biçim dizesindeki boşluk, "\t" veya "\n" karakterleri, giriş akışında şu karakterleri içeren bir veya daha fazla boş karakteri tanımlar: boşluk, '\t', '\n', '\v', '\f'. scanf işlevi atlar boş karakterler giriş akışında.

    % karakteri dışında, bir biçim dizesindeki değişmez karakterler, giriş akışında tam olarak aynı karakterlerin görünmesini gerektirir. Böyle bir karakter yoksa, scanf girişi durdurur. scanf işlevi değişmez karakterleri atlar.

    Genel olarak, giriş biçimi belirtimi şöyledir:

    %[*] [genişlik] [değiştiriciler] tür

    '*' karakteri, bu belirtim tarafından tanımlanan bir alana girerken bir boşluğu belirtir;

    - 'genişlik', bu belirtim tarafından girilen maksimum karakter sayısını tanımlar;

    Tip aşağıdaki değerleri alabilir:

    c bir karakter dizisidir,

    s – karakter dizisi, diziler boş karakterlerle ayrılır,

    d, 10 s/s'de işaretli bir tamsayıdır,

    i işaretli bir tamsayıdır, sayı sistemi ilk iki basamağı temel alır,

    u, 10 s/s'de işaretsiz bir tamsayıdır,

    o, 8 s/s cinsinden işaretsiz bir tam sayıdır,

    x, X 16 s/s'de işaretsiz bir tamsayıdır,

    e, E, f, g, G - kayan sayı,

    p, bir işaretçiye yönelik bir işaretçidir,

    n, bir tamsayıya işaretçidir,

    […] taranan karakterlerden oluşan bir dizidir, örneğin, .

    İkinci durumda, giriş akışından yalnızca köşeli parantez içindeki karakterler girilecektir. İlk karakter içerideyse köşeli parantez'^' değerine eşitse, yalnızca dizide yer almayan karakterler girilir. Bir dizideki karakter aralığı '-' karakteri ile belirtilir. Karakterler girildiğinde, baştaki boş karakterler ve dizenin sonlandırma boş baytı da girilir.

    Değiştiriciler aşağıdaki değerleri alabilir:

    h kısa bir tamsayıdır,

    l, L - uzun tamsayı veya kayan nokta,

    ve yalnızca tamsayılar veya değişkenler için kullanılır.

    Aşağıdaki örnek, scanf işlevi için kullanım durumlarını göstermektedir. Kayan bir sayı girmeden önce biçim belirticisinin önünde bir boşluk olduğuna dikkat edin.

    #katmak

    printf("Bir tam sayi giriniz: ");

    scanf("%d", &n);

    printf("Çift giriniz: ");

    scanf(" %lf", &d);

    printf("Bir karakter giriniz: ");

    scanf(" %c", &c);

    printf("Bir dizi giriniz: ");

    scanf("%s", &s);

    Bu programda kayan noktalı sayının sıfırlandığını unutmayın. Bu, derleyicinin kayan sayılarla çalışmayı desteklemek için bir kitaplık içermesi için yapılır. Bu yapılmazsa, kayan bir sayı girerken çalışma zamanında bir hata oluşur.

    Konsola biçimlendirilmiş çıktı için işlevi kullanın

    int printf (const char *format, ...);

    başarı durumunda çıktı birimlerinin sayısını ve başarısızlık durumunda EOF'yi döndürür. format parametresi, çıktı formatı belirtimlerini içeren bir format dizesidir. Biçim dizesini izleyen bağımsız değişkenlerin sayısı ve türleri, biçim dizesinde verilen çıktı biçimi belirtimlerinin sayısı ve türleri ile eşleşmelidir. Genel olarak çıktı formatı belirtimi şöyledir:

    %[flags] [genişlik] [.precision] [değiştiriciler] tip

    - "bayraklar" çıktı formatını belirten çeşitli sembollerdir;

    - 'genişlik', bu belirtim tarafından üretilen minimum karakter sayısını tanımlar;

    - '.precision' çıktısı alınacak maksimum karakter sayısını tanımlar;

    - "değiştiriciler" bağımsız değişkenlerin türünü belirtir;

    - 'type' bağımsız değişkenin türünü belirtir.

    İşaretli tamsayıları yazdırmak için aşağıdaki çıktı biçimi kullanılır:

    %[-] [+ | boşluk] [genişlik] [l] d

    - – varsayılan olarak sola hizalama – sağa;

    + - '+' işareti görüntülenir, bunun için negatif sayılar'-' işareti her zaman görüntülenir;

    'boşluk' – karakter konumunda bir boşluk görüntülenir;

    d, int veri türüdür.

    İşaretsiz tamsayıların çıktısını almak için aşağıdaki çıktı formatı kullanılır:

    %[-] [#] [genişlik] [l]

    # - 8 c/c'deki sayılar için ilk 0'ı veya 16 c/c'deki sayılar için ilk 0x veya 0X'i görüntüler,

    l – uzun veri tipi değiştirici;

    u, 10c/c'de bir tamsayıdır,

    o, 8 c/c'de bir tamsayıdır,

    x, X, 16 c/c'de bir tamsayıdır.

    Kayan noktalı sayıları görüntülemek için aşağıdaki çıktı formatı kullanılır:

    %[-] [+ | boşluk] [genişlik] [.kesinlik]

    "kesinlik", f, e ve E biçimleri için ondalık noktadan sonraki basamak sayısını veya g ve G biçimleri için anlamlı basamak sayısını ifade eder. Sayılar yuvarlanır. Varsayılan, altı ondalık basamak kesinliktir;

    f sabit noktalı bir sayıdır,

    e üstel biçimde bir sayıdır, üs "e" harfi ile gösterilir,

    E üstel formda bir sayıdır, üs "E" harfi ile gösterilir,

    g, f veya g biçimlerinin en kısasıdır,

    G, f veya G biçimlerinin en kısasıdır.

    printf ("n = %d\n f = %f\n e = %e\n E = %E\n f = %.2f", -123, 12.34, 12.34, 12.34, 12.34);

    // şunu yazdırır: n = 123 f = 12,340000 e = 1,234000e+001 E = 1,234000E+001 f = 12,34

    1.4. Dize biçimlendirme.

    Dizeleri biçimlendirmek için tasarlanmış olan ve sırasıyla sscanf ve sprintf olarak adlandırılan scanf ve printf işlevlerinin çeşitleri vardır.

    int sscanf (const char *str, const char *format, ...);

    format parametresi tarafından belirtilen format dizgisine göre str parametresi tarafından belirtilen dizgeden veri okur. Başarılı olduğunda okunan veri miktarını, başarısızlık durumunda EOF döndürür. Örneğin,

    #katmak

    char str = "a 10 1.2 String Giriş yok";

    sscanf(str, "%c %d %lf %s", &c, &n, &d,s);

    printf("%c\n", c); // yazdırır: bir

    printf("%d\n",n); // yazdırır: 10

    printf("%f\n", d); // yazdırır: 1.200000

    printf("%s\n",s); // şunu yazdırır: Dize

    int sprintf (char *arabellek, const char *biçim, ...);

    dizgiyi format parametresi tarafından belirtilen formata göre formatlar ve sonucu tampon karakter dizisine yazar. İşlev, sonlandırıcı boş bayt hariç, arabellek karakter dizisine yazılan karakter sayısını döndürür. Örneğin,

    #katmak

    char str = "c = %c, n = %d, d = %f, s = %s";

    char s = "Bu bir dizidir.";

    sprintf(tampon, str, c, n, d, s);

    printf("%s\n", tampon); // şunu yazdırır: c = c, n = 10, d = 1.200000, s = Bu bir dizidir

    1.5. Dizeleri sayısal verilere dönüştürün.

    Dizeleri sayısal verilere dönüştüren işlevlerin prototipleri, programa dahil edilmesi gereken stdlib.h başlık dosyasında verilmiştir.

    Bir diziyi tamsayıya dönüştürmek için işlevi kullanın.

    int atoi (const char *str);

    karakter *str = "-123";

    n = atoi(str); // n = -123

    Bir diziyi uzun bir tamsayıya dönüştürmek için işlevi kullanın.

    uzun int atol (const char *str);

    başarılı olursa, str dizisinin dönüştürüldüğü tamsayıyı ve başarısız olursa 0'ı döndürür.Örneğin,

    karakter *str = "-123";

    n = atol(str); // n = -123

    Bir dizeyi çifte dönüştürmek için işlevi kullanın.

    çift ​​atof (const char *str);

    bu, başarılı olursa, str dizesinin dönüştürüldüğü ve başarısızlık durumunda 0 olan double türünde kayan bir sayı döndürür. Örneğin,

    karakter *str = "-123.321";

    n = atof(str); // n = -123.321

    Aşağıdaki işlevler atoi, atol, atof'a benzer eylemler gerçekleştirir, ancak daha fazla işlevsellik sağlar.

    uzun int strtol (const char *str, char **endptr, int base);

    str dizesini, döndürdüğü uzun bir int'ye dönüştürür. Bu fonksiyonun parametreleri aşağıdaki amaca sahiptir.

    Temel bağımsız değişken 0 ise, dönüşüm str'nin ilk iki karakterine bağlıdır:

    İlk karakter 1'den 9'a kadar bir rakamsa, sayının 10 c/c ile temsil edildiği varsayılır;

    İlk karakter 0 ve ikinci karakter 1'den 7'ye kadar bir sayı ise, sayının 8 c/c ile temsil edildiği varsayılır;

    İlk karakter 0 ve ikinci karakter 'X' veya 'x' ise, sayının 16 c/c ile temsil edildiği varsayılır.

    Temel argüman 2'den 36'ya kadar bir sayıysa, bu değer sayı sisteminin temeli olarak alınır ve bu sistemin ötesine geçen herhangi bir karakter dönüştürmeyi durdurur. 11'den 36'ya kadar olan tabanlara sahip sayı sistemleri, rakamları temsil etmek için 'A' ila 'Z' veya 'a' ila 'z' sembollerini kullanır.

    endptr bağımsız değişkeninin değeri strtol işlevi tarafından belirlenir. Bu değer, str dönüşümünü durduran karaktere bir işaretçi içerir. strtol işlevi, dönüştürülen sayıyı başarı durumunda, başarısızlık durumunda 0 döndürür. Örneğin,

    n = strtol("12a", &p, 0);

    printf(" n = %ld, %stop = %c, n, *p); // n = 12, dur = bir

    n = strtol("012b", &p, 0);

    printf(" n = %ld, %stop = %c, n, *p); // n = 10, dur = b

    n = strtol("0x12z", &p, 0);

    printf(" n = %ld, %stop = %c, n, *p); // n = 18, dur = z

    n = strtol("01117", &p, 0);

    printf(" n = %ld, %stop = %c, n, *p); // n = 7, dur = 7

    unsigned long int strtol (const char *str, char **endptr, int base);

    strtol işlevine benzer şekilde çalışır, ancak bir sayının karakter temsilini bir sayıya dönüştürür imzasız yazın uzun int.

    çift ​​strtod (const char *str, char **endptr);

    bir sayının sembolik gösterimini çift sayıya dönüştürür.

    Bu paragrafta listelenen tüm işlevler, söz konusu sayının biçimine uymayan ilk karakterle karşılaştıklarında çalışmayı durdurur.

    Ek olarak, bir sayının karakter değeri, karşılık gelen veri türü için izin verilen değer aralığını aşarsa, o zaman atof, strtol, strtoul, strtod işlevleri, errno değişkeninin değerini ERANGE olarak ayarlar. errno değişkeni ve ERANGE sabiti, math.h başlık dosyasında tanımlanır. atof ve strtod işlevleri HUGE_VAL döndürür, strtol işlevi LONG_MAX veya LONG_MIN döndürür ve strtoul işlevi ULONG_MAX döndürür.

    Standart olmayan itoa, ltoa, utoa, ecvt, fcvt ve gcvt işlevleri, sayısal verileri karakter dizilerine dönüştürmek için kullanılabilir. Ancak bu amaçlar için standart sprintf işlevini kullanmak daha iyidir.

    1.6. Dizelerle çalışmak için standart işlevler.

    Bu bölümde, prototipleri string.h başlık dosyasında açıklanan dizelerle çalışan işlevler ele alınmaktadır.

    1. Dize karşılaştırması. Stcmp ve strncmp fonksiyonları karakter dizilerini karşılaştırmak için kullanılır.

    int strcmp (const char *str1, const char *str2);

    str1, str2 dizelerini sözlüksel olarak karşılaştırır ve str1 sırasıyla str2'den küçük, eşit veya büyükse -1, 0 veya 1 döndürür.

    int strncmp (const char *str1, const char *str2, size_t n);

    sözlükbilimsel olarak str1 ve str2 dizilerindeki en fazla ilk n karakteri karşılaştırır. İşlev, str1'in ilk n karakteri sırasıyla str2'nin ilk n karakterinden küçük, eşit veya büyükse -1, 0 veya 1 döndürür.

    // dizi karşılaştırma örneği

    #katmak

    #katmak

    char str1 = "aa bb";

    karakter dizi2 = "aa aa";

    karakter str3 = "aa bb cc";

    printf("%d\n", strcmp(str1, str3)); // yazdırır: -1

    printf("%d\n", strcmp(str1, str1)); // yazdırır: -0

    printf("%d\n", strcmp(str1, str2)); // yazdırır: 1

    printf("%d\n", strncmp(str1, str3, 5)); // yazdırır: 0

    2. Satırları kopyalama. Strcpy ve strncpy işlevleri, dizeleri kopyalamak için kullanılır.

    char *strcpy (char *str1, const char *str2);

    str2 dizisini str1 dizisine kopyalar. str2 dizesi, sonlandırıcı boş bayt da dahil olmak üzere bütünüyle kopyalanır. İşlev, str1'e bir işaretçi döndürür. Çizgiler çakışırsa, sonuç tahmin edilemez.

    char *strncpy (char *str1, const char *str2, size_t n);

    str2 dizisinden n karakteri dizi str1'e kopyalar. str2 dizgisi n karakterden az içeriyorsa, str2 dizgisini n karaktere genişletmek için son boş bayt gerektiği kadar kopyalanır. İşlev, str1 dizgesine bir işaretçi döndürür.

    char str2 = "Dizeyi kopyala.";

    strcpy(str1, str2);

    printf(str1); // yazdırır: Dizeyi kopyala.

    4. Bağlantı dizileri. strcat ve strncat işlevleri, dizeleri tek bir dizide birleştirmek için kullanılır.

    char* strcat (char *str1, const char *str2);

    str2 dizesini str1 dizisine ekler, str1 dizisinin sonlandırıcı boş baytı silinir. İşlev, str1 dizgesine bir işaretçi döndürür.

    char* strncat (char *str1, const char *str2, size_t n);

    str1'in sonlandırıcı boş baytını silerek, str2 dizisinden str1 dizisine n karakteri birleştirir. İşlev, str1 dizgesine bir işaretçi döndürür. str2 dizisinin uzunluğu n'den küçükse, yalnızca str2 dizisinde bulunan karakterler eklenir. Dize birleştirme işleminden sonra, str1'e her zaman bir boş bayt eklenir. İşlev, str1 dizgesine bir işaretçi döndürür.

    #katmak

    #katmak

    char str1 = "dize";

    char str2 = "katenasyon";

    karakter str3 = "Evet Hayır";

    strcat(str1, str2);

    printf("%s\n", str1); // yazdırır: Dize katenasyonu

    strncat(str1, str3, 3);

    printf("%s\n", str1); // şunu yazdırır: Dize katenasyonu Evet

    5. Dizide bir karakter arayın. strchr, strrchr, strspn, strcspn ve strpbrk işlevleri bir dizide karakter aramak için kullanılır.

    char* strchr (const char *str, int c);

    str dizesinde c parametresi tarafından belirtilen karakterin ilk geçtiği yeri arar. Başarı durumunda, işlev bulunan ilk karaktere bir işaretçi ve başarısızlık durumunda NULL döndürür.

    char* strrchr (const char *str, int c);

    str dizesinde c parametresi tarafından belirtilen karakterin son geçtiği yeri arar. Başarı durumunda işlev, bulunan son karaktere bir işaretçi ve başarısızlık durumunda NULL döndürür.

    #katmak

    #katmak

    char str = "char arama";

    printf("%s\n", strchr(str, "r")); // yazdırır: r ara

    printf("%s\n", strrchr(str, "r")); // yazdırır: rch

    size_t strspn (const char *str1, const char *str2);

    str1'de str2'de olmayan ilk karakterin indeksini döndürür.

    size_t strcspn (const char *str1, const char *str2);

    str1'deki str2'deki ilk karakterin indeksini döndürür.

    karakter dizisi = "123 abc";

    printf("n = %d\n", strspn(str, "321"); // şunu yazdırır: n = 3

    printf ("n = %d\n", strcspn (str, "cba"); // yazdırır: n = 4

    char* strpbrk (const char *str1, const char *str2);

    str1'de str2'deki karakterlerden birine eşit olan ilk karakteri bulur. Başarı durumunda, işlev o karaktere bir işaretçi ve başarısızlık durumunda NULL döndürür.

    karakter dizisi = "123 abc";

    printf("%s\n", strpbrk(str, "bca")); // şunu yazdırır: abc

    6. Dizelerin karşılaştırılması. strstr işlevi, dizeleri karşılaştırmak için kullanılır.

    char* strstr (const char *str1, const char *str2);

    str1 dizgisinde str2 dizgisinin (sonunda boş bayt olmadan) ilk geçtiği yeri bulur. Başarı durumunda, işlev bulunan alt dizeye bir işaretçi ve başarısızlık durumunda NULL döndürür. str1 işaretçisi sıfır uzunluklu bir dizeyi gösteriyorsa işlev str1 işaretçisini döndürür.

    char str = "123 abc 456;

    printf("%s\n", strstr(str, "abc"); // yazdir: abc 456

    7. Bir diziyi belirteçlere ayrıştırma. strtok işlevi, bir dizeyi belirteçlere ayrıştırmak için kullanılır.

    char* strtok (char *str1, const char *str2);

    str1 dizisindeki bir sonraki simgeye (sözcük) bir işaretçi döndürür; burada belirteç ayırıcılar, str2 dizisindeki karakterlerdir. Belirteçler bittiyse, işlev NULL değerini döndürür. strtok işlevine yapılan ilk çağrıda, str1 parametresi belirteçlere ayrıştırılan bir dizeyi işaret etmelidir ve sonraki çağrılarda bu parametre NULL olarak ayarlanmalıdır. Belirteci bulduktan sonra, strtok işlevi sınırlayıcı yerine bu belirteçten sonra boş bir bayt yazar.

    #katmak

    #katmak

    char str = "12 34 ab cd";

    p = strtok(str, " ");

    printf("%s\n",p); // bir sütundaki değerleri yazdırır: 12 34 ab cd

    p = strtok(BOŞ, " ");

    8. Dizinin uzunluğunu belirleme. strlen işlevi, bir dizenin uzunluğunu belirlemek için kullanılır.

    size_t strlen(const char *str);

    son boş baytı yok sayarak dizenin uzunluğunu döndürür. Örneğin,

    karakter dizisi = "123";

    printf("len = %d\n",strlen(str)); // yazdırır: len = 3

    1.7. Bellekle çalışmak için işlevler.

    Başlık dosyası string.h ayrıca, dizelerle çalışmak için karşılık gelen işlevlere benzer olan bellek bloklarıyla çalışmak için işlevleri de açıklar.

    void* memchr(const void *str, int c, size_t n);

    str'nin n baytında c tarafından belirtilen karakterin ilk geçtiği yeri arar.

    int memcmp(sabit geçersiz *str1, sabit geçersiz *str2, size_t n);

    str1 ve str2'nin ilk n baytını karşılaştırır.

    geçersiz* memcpy(const geçersiz *str1, sabit geçersiz *str2, size_t n);

    ilk n baytı str1 dizisinden str2 dizisine kopyalar.

    void* memmove(const void *str1, const void *str2, size_t n);

    ilk n baytı str1'den str2'ye kopyalar ve örtüşen dizelerin doğru şekilde işlenmesini sağlar.

    void* memset(const void *str, int c, size_t n);

    bir karakteri kopyalar parametre ile ayarla c, str'nin ilk n baytına kadar.

    Merhaba!

    Kısa bir süre önce, bir bilgisayar bilimleri kolejinin öğretmenlerinden birinin karıştığı oldukça ilginç bir olay yaşadım.

    Linux altında programlama hakkındaki konuşma, yavaş yavaş bu kişinin sistem programlamanın karmaşıklığının aslında büyük ölçüde abartıldığını iddia etmeye başladığı gerçeğine dönüştü. C dilinin bir eşleştirme kadar basit olması, aslında, Linux çekirdeği(sözlerinden).

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

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

    Char *str = (char *)malloc(sizeof(char) * strlen(buffer));
    tampon, klavyeden gelen verilerle doldurulmuş bir yığın değişkenidir.

    “Burada bir yanlışlık mı var?” diye soranlar mutlaka çıkacaktır diye düşünüyorum.
    Yapabileceğine inan.

    Ve tam olarak ne - kediyi okuyun.

    Biraz teori - bir tür LikBez.

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

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

    karakter dizi[n] = ( 0 );
    n, karakter dizisinin boyutudur, dizenin uzunluğu ile aynıdır.

    Atama ( 0 ) - dizgiyi "boş kılma" (isteğe bağlı, onsuz ilan edebilirsiniz). Sonuç, memset(str, 0, sizeof(str)) ve bzero(str, sizeof(str)) işlevleriyle aynıdır. Çöpün başlatılmamış değişkenlere atılmasını önlemek için kullanılır.

    Yığındaki bir dizeyi hemen de başlatabilirsiniz:

    Char buf = "varsayılan arabellek metni\n";
    Ek olarak, bir dize bir işaretçi olarak bildirilebilir ve yığında onun için bellek ayırabilir:

    Karakter *str = malloc(boyut);
    boyut - dize için ayırdığımız bayt sayısı. Bu tür satırlara dinamik denir (çünkü doğru beden dinamik olarak hesaplanır + ayrılan bellek boyutu, realloc() işlevi kullanılarak herhangi bir zamanda artırılabilir).

    Bir yığın değişkeni olması durumunda, dizinin boyutunu belirlemek için n gösterimini kullandım, yığındaki bir değişken olması durumunda, gösterim boyutunu kullandım. Ve bu, yığındaki bir bildirim ile yığındaki bellek tahsisli bir bildirim arasındaki farkın gerçek özünü mükemmel bir şekilde yakalar, çünkü n genellikle öğelerin 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 ( , iki - küçük nasıl yapılır) - çok faydalı program, programcının dizelerle çalışırken en sık ortaya çıkan bellek sızıntılarını ve bağlam hatalarını izlemesine yardımcı olur.

    Bahsettiğim programa benzer bir şey uygulayan ve onu valgrind aracılığıyla çalıştıran küçük bir listeye bakalım:

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

    $ gcc main.c $ ./a.out -> Merhaba Habr!
    Şimdiye kadar, sıra dışı 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== Telif hakkı (C) 2002-2015 ve GNU GPL"d, Julian Seward ve diğerleri tarafından. == 3892== Valgrind-3.12.0 ve LibVEX kullanılarak, telif hakkı bilgisi için -h ile yeniden çalıştırın ==3892== Komut: ./a.out ==3892== ==3892== 2 boyutunda geçersiz yazma ==3892= = 0x4005B4'te: main ( /home/indever/prg/C/public/a.out içinde) ==3892== Adres 0x520004c, 13 boyutunda bir bloğun içinde 12 bayttır alloc"d ==3892== 0x4C2DB9D'de: malloc (vg_replace_malloc.c:299) ==3892== 0x400597 tarafından: main ( /home/indever/prg/C/public/a.out içinde) ==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/'da) lib64/libc-2.24.so) ==3892== 0x4005CF tarafından: main ( /home/indever/prg/C/public/a.out içinde) ==3892== Adres 0x520004d, 13 boyutlu bir bloktan sonra 0 bayttır alloc"d ==3892== 0x4C2DB9D'de: malloc (vg_replace_malloc.c:299) ==3892== 0x400597 ile: ana ( /home/ içinde endever/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 ayırma, 2 serbest bırakma, 1.037 bayt ayırma ==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 yeniden çalıştırın: -v ==3892== HATA ÖZETİ: 3 hata 2 bağlamdan (bastırılmış: 0'dan 0)
    ==3892== Tüm yığın blokları serbest bırakıldı - sızıntı mümkün değil- sızıntı yok ve memnun. Ancak gözlerinizi biraz aşağı indirmeye değer (yine de belirtmek isterim ki bu sadece bir özet, ana bilgiler biraz farklı bir 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!?

    Evet, çok basit. Tüm "hile", strlen işlevinin satır sonu karakterini - "\0" dikkate almamasıdır. Giriş dizisinde (#define HELLO_STRING "Merhaba, Habr!\n\0") açıkça belirtilse bile dikkate alınmaz.

    Program yürütme sonucunun hemen üzerinde, satırlar -> Merhaba Habr! değerli valgrindimizin neyi ve nerede beğenmediğine dair detaylı bir rapor var. Bu satırlara kendi başınıza bakmanızı ve kendi sonuçlarınızı çıkarmanızı öneririm.

    Aslında, doğru versiyonu program şöyle görünecek:

    #katmak #katmak #katmak #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'den geçiyoruz:

    $ 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 ayırma, 2 serbest bırakma, 1.038 bayt ayırma ==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 yeniden çalıştırın: -v ==3435== HATA ÖZETİ: 0 hata 0 bağlamdan (bastırılmış: 0'dan 0)
    Harika. Hata yok, +1 bayt ayrılmış bellek sorunu çözmeye yardımcı oldu.

    İlginç bir şekilde, çoğu durumda hem birinci hem de ikinci program aynı şekilde çalışacaktır, ancak son karaktere uymayan satır için ayrılan bellek sıfıra ayarlanmamışsa, bu tür görüntülerken printf () işlevi bir satır, bu satırdan sonraki tüm çöpleri de görüntüler - satır sonu karakteri printf () yoluna girene kadar her şey yazdırılır.

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

    1. Ancak, örneğin s(n)printf(..) kullanılarak oluşturulan bir dizi için bellek ayırmamız gerekirse ne olur? Argümanları desteklemiyoruz.
    2. Dış görünüş. Bir değişkenin bildirimini içeren satır çok kötü görünüyor. Bazı arkadaşlar malloc'a (char *) eklemeyi de başarıyor, sanki artıların altına yazıyorlar. Düzenli olarak dizileri 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 bulalım.

    snprintf()

    int snprintf(char *str, size_t boyutu, const char *biçimi, ...);- işlev - bir dizgiyi biçimlendiren ve onu ilk bağımsız değişken olarak iletilen işaretçiye yazan bir sprintf uzantısı. sprintf() işlevinden farklıdır, çünkü str'ye boyut olarak belirtilenden daha fazla bayt yazılmaz.

    Fonksiyonun bir tane var ilginç özellik- her durumda, üretilen dizgenin boyutunu döndürür (satır sonu karakteri hariç). Dize boşsa, 0 döndürülür.

    Tanımladığım strlen ile ilgili sorunlardan biri sprintf() ve snprintf() işlevleriyle ilgilidir. Diyelim ki str dizgesine bir şeyler yazmamız gerekiyor. Son dize, diğer değişkenlerin değerlerini içerir. Girişimiz şöyle görünmelidir:

    Char * str = /* belleği buraya ayır */; sprintf(str, "Merhaba, %s\n", "Habr!");
    Şu soru ortaya çıkıyor: str dizisi için ne kadar bellek ayrılması gerektiği nasıl belirlenir?

    Char * str = malloc(sizeof(char) * (strlen(str, "Merhaba, %s\n", "Habr!") + 1)); - çalışmayacak. strlen() işlev prototipi şöyle görünür:

    #katmak size_t strlen(const char *s);
    const char *s, s'ye iletilen dizenin değişken biçimli bir dize olabileceği anlamına gelmez.

    İşte bize yardımcı olacak kullanışlı özellik yukarıda bahsettiğim snprintf() fonksiyonu. Aşağıdaki programın koduna bakalım:

    #katmak #katmak #katmak void main() ( /* snprintf() satır sonu karakterine uymadığından boyutunu sonuca ekleyin */ size_t gerekli_mem = snprintf(NULL, 0, "Merhaba, %s!\n", "Habr") + sizeof("\0"); char *str = malloc(needed_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 ayırma, 2 serbest bırakma, 1.041 bayt ayırma ==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 yeniden çalıştırın: -v ==4132== HATA ÖZETİ: 0 hata 0 bağlamdan (bastırılmış: 0'dan 0) $
    Harika. Argümanlar için desteğimiz var. snprintf() işlevine ikinci argüman olarak sıfırı ilettiğimiz için, boş gösterici ile yazmak asla bir Seagfault'a yol açmaz. Ancak buna rağmen, işlev yine de dize için gereken 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() durumunda olduğundan 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, "Merhaba, %s!\n) \0 ”, “Habr”);), ancak bu her zaman mümkün değildir (dizi işleme mekanizmasına bağlı olarak fazladan bir bayt ayırabiliriz).

    Bir şey yapılmalı. Bir süre düşündüm ve artık eskilerin bilgeliğine başvurma zamanının geldiğine karar verdim. İlk argüman olarak bir boş gösterici ve ikincisi olarak bir boş gösterici ile snprintf()'i çağıracak bir makro işlevi tanımlayalım. Ve sıranın sonunu da unutmayalım!

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

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

    #katmak #katmak #katmak #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 ayırma, 2 serbest bırakma, 1.041 bayt ayırma ==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 yeniden çalıştırın: -v ==6432== HATA ÖZETİ: 0 hata 0 bağlamdan (bastırılmış: 0'dan 0)
    Evet herhangi bir hata yok. Her şey doğru. Ve valgrind mutlu ve programcı sonunda uyuyabilir.

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

    asprintf işlevinden bahsediyoruz:

    #define _GNU_SOURCE /* Bkz. feature_test_macros(7) */ #include int asprintf(char **strp, const char *fmt, ...);
    İlk bağımsız değişkeni olarak bir dizeye (**strp) bir işaretçi alır ve başvurusu kaldırılan işaretçide bellek ayırır.

    asprintf() kullanılarak yazılan programımız şöyle görünecektir:

    #katmak #katmak #katmak 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 ayırma, 3 serbest bırakma, 1.138 bayt ayırma ==6674= ===6674== Tüm yığın blokları serbest bırakıldı -- sızıntı mümkün değil ==6674== ==6674== Algılanan ve bastırılan hataların sayısı için, şununla yeniden çalıştırın: -v ==6674== HATA ÖZETİ: 0 hata 0 bağlamdan (bastırılmış: 0'dan 0)
    Her şey yolunda, ancak gördüğünüz gibi, daha fazla bellek ayrıldı ve şimdi iki değil, üç ayırma var Zayıf gömülü sistemlerde bu işlev istenmez.
    Ayrıca konsolda man asprintf yazarsak şunu görürüz:

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

    Bu, bu özelliğin yalnızca GNU kaynaklarında mevcut olduğunu açıkça ortaya koymaktadır.

    Çözüm

    Sonuç olarak, C'de dizelerle çalışmanın bir takım nüansları olan çok karmaşık bir konu olduğunu söylemek istiyorum. Örneğin, dinamik bellek tahsisli "güvenli" kod yazmak için malloc() yerine calloc() işlevinin kullanılması tavsiye edilir - calloc ayrılan belleği sıfırlarla doldurur. Ya da hafıza 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 hatta bazen dize ile çalışırken sorunlara neden olabilir.

    Benim isteğim üzerine dizeler için bellek ayırma sorununu çözen tanıdığım C programcılarının yarısından fazlası (çoğu yeni başlayan), bunu sonunda bağlam hatalarına yol açacak şekilde yaptılar. Bir durumda - bir hafıza sızıntısına bile (pekala, bir kişi ücretsiz yapmayı unuttu (str), kimin başına gelmez). Aslında, az önce okuduğunuz bu yaratımı yaratmam için bana ilham veren şey buydu.

    Umarım bu makale birileri için faydalı olacaktır. Neden tüm bunları çitle çevirdim - hiçbir dil basit değildir. Her yerin incelikleri vardır. Ve dilin ne kadar inceliklerini bilirseniz, kodunuz o kadar iyi olur.

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