• c'de bir değişken nedir? C dilinde veri türleri ve işlemleri. ifade

    Veri tipleri. C'nin ait olduğu prosedürel dillerdeki bir program, miktarlar üzerindeki işlemlerin bir açıklamasıdır. çeşitli tipler. Bir tür, bir değerin alabileceği değerler kümesini ve katılabileceği işlemler kümesini tanımlar.

    C dilinde türler, değerlerin adlarıyla (tanımlayıcılarıyla), yani değişkenlerle ilişkilendirilir. C'deki bir değişken, bir bellek konumuyla ilişkilendirilir. Değişkenin türü, hücrenin boyutunu, içeriğinin kodlanma şeklini ve bu değişkenin değeri üzerinden izin verilen dönüşümleri belirtir. Tüm değişkenler kullanılmadan önce bildirilmelidir. Her değişken yalnızca bir kez bildirilmelidir.

    Açıklama, bir tür belirtici ve ardından bir değişkenler listesinden oluşur. Listedeki değişkenler virgülle ayrılır. Açıklamanın sonuna noktalı virgül konulur.

    Açıklama örnekleri:

    karakter a, b; /* a ve b değişkenlerinin tipi var

    karakter */intx; /* Değişken x - int yazın

    */ karakter sembolü; /" char türündeki değişkenler sym açıklanmıştır;

    */ int sayı.sayısı; /* int türünün sayısı ve sayısı */

    Değişkenlere bildirimleri içerisinde başlangıç ​​değerleri atanabilir. Bir değişken adının ardından eşittir işareti ve bir sabit geliyorsa, bu sabit bir başlatıcı işlevi görür.

    Örnekler: karakter arkası = "\0";

    C dilindeki temel türleri düşünün.

    int - tamsayı ("tamsayı"). Bu türdeki değerler, bazı sınırlı aralıktaki (genellikle - 32768 ila 32767) tam sayılardır. Aralık, tip için hücre boyutuna göre belirlenir ve makineye özeldir. Ayrıca int tipi ile kullanılabilecek yardımcı kelimeler de vardır: short int ("short integer" - "short integer"), unsigned int ("unsigned integer" - "unsigned integer"), long int ("long integer") " ), sayıların temsil aralığını azaltan veya tersine genişleten.

    karakter- karakter ("karakter"). Bu tür için geçerli değer bir karakterdir (metinle karıştırılmamalıdır!). Sembol kesme işaretleri ile yazılır.

    Örnekler:"x" 2 "?"

    Bir karakter, bilgisayar belleğinde bir bayt yer kaplar. Aslında, bir karakter değil, bir sayı saklanır - bir karakter kodu (0'dan 255'e kadar). Özel kodlama tablolarında, tüm geçerli karakterler ve bunlara karşılık gelen kodlar belirtilir.

    C dilinde, parantez içinde - (int) tamsayı türü belirteci kullanılırken, char türünün sayısal bir tür olarak kullanılmasına, yani bir karakter koduyla işlem yapılmasına izin verilir.

    kayan nokta - gerçek (kayan nokta). Bu türdeki değerler sayılardır, ancak char ve int'den farklı olarak tamsayı olmaları gerekmez.

    12,87 -316,12 -3,345e5 12,345e-15

    çift ​​- çift duyarlıklı gerçek sayılar. Bu tip, float tipine benzer, ancak çok daha geniş bir değer aralığına sahiptir (örneğin, Borland-C programlama sistemi için 3.4E-38 ila 1.7E-308 yerine 1.7E+308'den 3.4E-38'e kadar) 3.4E+38, kayan tip için). Bununla birlikte, sayıların gösterim aralığı ve doğruluğundaki bir artış, program yürütme hızında bir azalmaya ve israfa yol açar. rasgele erişim belleği bilgisayar.


    Bu listede bir dize türünün olmadığına dikkat edin. Dizeleri tanımlamak için kullanılabilecek özel bir C türü yoktur. Bunun yerine, dizeler bir char öğeleri dizisi olarak temsil edilir. Bu, dizideki karakterlerin bitişik bellek hücrelerinde yer alacağı anlamına gelir.

    Dizinin son elemanının \0 karakteri olduğuna dikkat edilmelidir. Bu "boş karakterdir" ve C'de bir dizgenin sonunu işaretlemek için kullanılır. Boş karakter 0 rakamı değildir; yazdırılmaz ve ASCII kod tablosunda 0 sayısına sahiptir.Boş karakterin varlığı, dizideki hücre sayısının olması gerektiği anlamına gelir. hafızada saklanacak karakter sayısından en az bir fazla.

    Dize kullanımına bir örnek verelim.

    program 84

    #katmak ana()

    scanf("%s",dize) ;

    printf("%s",dize);

    Bu örnek, 30'u bir char öğesi tutabilen 31 bellek konumundan oluşan bir diziyi açıklamaktadır. scanf("%s",string); işlevi çağrıldığında girilir. Bir karakter dizisi belirtilirken "&" eksik.

    İşaretçiler. İşaretçi - değişken için ayrılan bellek hücresinin adresinin bazı sembolik gösterimi.

    Örneğin, &name, name değişkenine bir işaretçidir;

    Burada & adres alma işlemidir. Gerçek adres bir sayıdır ve &ad adresinin sembolik temsili bir işaretçi sabitidir.

    C dili ayrıca işaretçi değişkenlerine sahiptir. Tıpkı değer gibi tip değişkeni char bir karakterdir ve int türündeki bir değişkenin değeri bir tamsayıdır, işaretçi türündeki bir değişkenin değeri bir değerin adresidir.

    İşaretçiye ptr adını verirsek, aşağıdaki ifadeyi yazabiliriz:

    ptr = /* ismin adresini ptr'ye atar */

    Bu durumda prt'nin bir "işaretçi" adı olduğunu söylüyoruz. İki notasyon ptr ve &name arasındaki fark, prt'nin bir değişken, &name'nin ise bir sabit olmasıdır. Gerekirse, ptr değişkeninin başka bir nesneye işaret etmesini sağlayabilirsiniz:

    puan= /* ptr isme değil bah'a işaret eder */

    Şimdi prt değişkeninin değeri bah değişkeninin adresidir. ptr değişkeninin bah değişkenine bir referans içerdiğini bildiğimizi varsayalım. Daha sonra bu değişkenin değerine erişmek için “dolaylı adresleme” işlemini * kullanabilirsiniz:

    val = *ptr; /* ptr'nin işaret ettiği değeri belirle */ Birlikte alınan son iki ifade aşağıdakine eşdeğerdir:

    Yani işaret ne zaman & ardından değişkenin adı gelir, işlemin sonucu belirtilen değişkenin adresidir; &hemşire hemşire değişkeninin adresini verir; * işaretinin ardından bir değişken işaretçisi geldiğinde, işlemin sonucu, belirtilen adresteki bellek konumuna yerleştirilen değerdir.

    Örnek: hemşire = 22;

    sayı= /* hemşireye işaretçi */

    Sonuç, 22 değerinin val değişkenine atanmasıdır.

    Bazı değişkenlerin işaretçi olduğunu söylemek yeterli değildir. Ek olarak, bu işaretçinin ne tür bir değişkene atıfta bulunduğunu söylemelisiniz. Bunun nedeni, farklı türdeki değişkenlerin farklı numara bellek hücreleri, işaretçiyle ilgili bazı işlemler ise ayrılan bellek miktarını bilmenizi gerektirir.

    örnekler doğru açıklama işaretçiler: int *pi; karakter*bilgisayar;

    Tip belirtimi, işaretçi tarafından atıfta bulunulan değişkenin türünü belirtir ve * simgesi, değişkenin kendisini bir işaretçi olarak tanımlar. Tip açıklama int *pi; pi'nin bir işaretçi olduğunu ve *pi'nin bir int değeri olduğunu söyler.

    C dili, veri türü adlarını tanımlama yeteneği sağlar. typedef tanımı kullanılarak herhangi bir veri tipine bir isim verilebilir ve bu isim daha sonra nesneleri tanımlarken kullanılabilir.

    Biçim: typedef<старый тип> <новый тип> Örnek: typedef uzun BÜYÜK; /* long ile eşdeğer olan büyük tip tanımlandı */

    typedef tanımı herhangi bir yeni tür getirmez, yalnızca zaten mevcut olana yeni bir ad ekler. mevcut tip. Bu şekilde bildirilen değişkenler, açıkça bildirilen değişkenlerle tamamen aynı özelliklere sahiptir. Tür yeniden adlandırma, programların anlaşılırlığını artıran ve program taşınabilirliğini iyileştiren anlamlı veya kısaltılmış tür adlarını tanıtmak için kullanılır (aynı veri türünün adları farklı bilgisayarlarda farklı olabilir).

    Operasyonlar. C dili, çok çeşitli işlemlerle (40'tan fazla) ayırt edilir. Burada sadece ana olanları ele alıyoruz, Tablo. 3.3.

    Aritmetik işlemler. Onlar içerir

    Toplama(+),

    Çıkarma (ikili) (-),

    Çarpma işlemi (*),

    Bölüm (/),

    Tamsayı bölümünden kalan (%),

    Çıkarma (birli) (-) .

    C dilinde kural kabul edilir: Bölen ve bölen int türündeyse, bölme işlemi tamamen yapılır, yani sonucun kesirli kısmı atılır.

    Her zaman olduğu gibi ifadelerde çarpma, bölme ve kalan işlemleri toplama ve çıkarmadan önce yapılır. Parantezler eylemlerin sırasını değiştirmek için kullanılır.

    program 85

    #katmak

    5 = -3 + 4 * 5 - 6; printf("%d\n",s);

    s = -3 + %45 - 6; printf("%d\n",s);

    s = -3 * %4 - 6/5; printf("%d\n",s);

    s= (7 + 6)%5/2; printf("%d\n",s);

    Program yürütme sonucu: 11 1 0 1

    Tablo 3.3 Kıdem ve operasyonların icra sırası

    Dil Temelleri

    Programın kodu ve programın manipüle ettiği veriler, bilgisayarın belleğine bir bit dizisi olarak yazılır. Biraz en küçük elementtir bilgisayar hafızası, 0 veya 1'i saklayabilir. Açık fiziksel seviye karşılık gelir elektrik voltajı, var olup olmadığı bilinen. Bilgisayarın belleğinin içeriğine baktığımızda şöyle bir şey göreceğiz:
    ...

    Böyle bir diziyi anlamlandırmak çok zordur, ancak bazen bu tür yapılandırılmamış verileri manipüle etmemiz gerekir (buna ihtiyaç genellikle donanım aygıt sürücülerini programlarken ortaya çıkar). C++, bit verileriyle çalışmak için bir dizi işlem sağlar. (Bu konudan 4. Bölümde bahsedeceğiz.)
    Kural olarak, bir bit dizisine, bitleri gruplar halinde gruplandıran bir yapı empoze edilir. bayt Ve kelimeler. Bir bayt 8 bit içerir ve bir kelime 4 bayt veya 32 bit içerir. Ancak, bir kelimenin tanımı farklı işletim sistemlerinde farklı olabilir. 64 bit sistemlere geçiş şimdi başlıyor ve 16 bit sözcüklere sahip sistemler son zamanlarda yaygınlaştı. Sistemlerin büyük çoğunluğunda byte boyutu aynı olsa da biz yine de bu değerlere makineye özel olarak değineceğiz.

    Şimdi örneğin 1040 adresindeki bir bayttan veya 1024 adresindeki bir kelimeden bahsedebilir ve 1032 adresindeki baytın 1040 adresindeki bayta eşit olmadığını söyleyebiliriz.
    Ancak herhangi bir baytın, herhangi bir makine kelimesinin neyi temsil ettiğini bilmiyoruz. Belirli 8 bitin anlamı nasıl anlaşılır? Bu baytın (veya kelimenin veya diğer bit kümesinin) anlamını açık bir şekilde yorumlamak için, bu baytın temsil ettiği veri türünü bilmemiz gerekir.
    C++ bir dizi yerleşik veri türü sağlar: karakter, tamsayı, gerçek ve bir dizi bileşik ve genişletilmiş tür: dizeler, diziler, karmaşık sayılar. Ayrıca, bu verilerle yapılan işlemler için temel set işlemler: karşılaştırma, aritmetik ve diğer işlemler. Geçişler, döngüler, koşullu ifadeler de vardır. C++ dilinin bu öğeleri, herhangi bir karmaşıklıkta bir sistem oluşturabileceğiniz yapı taşları kümesini oluşturur. C++'da uzmanlaşmanın ilk adımı, bu kitabın II. Kısmında ayrılmış olan bu temel unsurları öğrenmektir.
    Bölüm 3, yerleşik ve genişletilmiş türlere ve yeni türler oluşturabileceğiniz mekanizmalara genel bir bakış sağlar. Temel olarak, elbette, Bölüm 2.3'te tanıtılan sınıf mekanizmasıdır. 4. Bölüm, ifadeler, yerleşik işleçler ve bunların önceliği ve tür dönüştürmeleri ile ilgilidir. 5. Bölüm dil yönergelerinden bahsediyor. Son olarak, Bölüm 6'da C++ Standart Kitaplığı ve kap türleri, vektör ve ilişkisel dizi tanıtılmaktadır.

    3. C++ veri türleri

    Bu bölüm bir genel bakış sağlar gömülü, veya temel, C++ dilinin veri türleri. Tanımla başlar değişmezler 3.14159 veya pi gibi ve ardından kavram değişken, veya nesne veri türlerinden biri olmalıdır. Bölümün geri kalanı şuna ayrılmıştır: Detaylı Açıklama her yerleşik tür. Ayrıca, C++ standart kitaplığı tarafından sağlanan dizeler ve diziler için türetilmiş veri türlerini de listeler. Bu türler temel olmamakla birlikte, gerçek C++ programları yazmak için çok önemlidir ve okuyucuyu mümkün olduğunca erken tanıtmak istiyoruz. Bu tür veri türlerini arayacağız eklenti C++ temel türleri.

    3.1. değişmezler

    C++, tamsayıları ve gerçek sayıları, sembolleri temsil etmek için bir dizi yerleşik veri türüne ve ayrıca depolamak için kullanılan bir "karakter dizisi" veri türüne sahiptir. karakter dizileri. Karakter türü, tek tek karakterleri ve küçük tamsayıları depolamak için kullanılır. Bir makine baytını kaplar. kısa tipler, int ve long tamsayıları temsil etmek içindir. Bu türler yalnızca sayıların alabileceği değer aralığında farklılık gösterir ve listelenen türlerin belirli boyutları uygulamaya bağlıdır. Genellikle kısa bir makine kelimesinin yarısıdır, int bir kelimedir, uzun bir veya iki kelimedir. 32 bit sistemlerde int ve long genellikle aynı boyuttadır.

    Float, double ve long double türleri, kayan noktalı sayılar içindir ve temsil kesinliği (anlamlı basamak sayısı) ve aralık bakımından farklılık gösterir. Tipik olarak, kayan nokta (tek kesinlik) bir makine sözcüğü, çift (çift kesinlik) iki kelime ve uzun çift (genişletilmiş kesinlik) üç kelime alır.
    char, short, int ve long birlikte oluşur tam sayı türleri, bu da olabilir ikonik(imzalı) ve imzasız(imzasız). İşaretli türlerde, en soldaki bit işareti depolamak için kullanılır (0 artıdır, 1 eksidir) ve kalan bitler değeri içerir. İşaretsiz türlerde, değer için tüm bitler kullanılır. 8 bitlik imzalı bir karakter -128 ile 127 arasındaki değerleri, unsigned bir karakter ise 0 ile 255 arasındaki değerleri temsil edebilir.

    Bir programda 1 gibi bir sayı ile karşılaşıldığında bu sayı çağrılır. değişmez, veya gerçek sabit. Bir sabit, çünkü değerini değiştiremeyiz ve bir değişmez, çünkü değeri program metninde görünür. Değişmez değer, adreslenemeyen bir değerdir: Her ne kadar aslında makinenin belleğinde saklanmış olsa da, adresini bilmenin hiçbir yolu yoktur. Her sabit değerin belirli bir türü vardır. Böylece, 0 int türünde, 3.14159 ise double türündedir.

    Tamsayı tipi sabit değerler ondalık, sekizli ve onaltılık olarak yazılabilir. 20 sayısı ondalık, sekizlik ve onaltılık değişmez değer olarak şöyle görünür:

    20 // ondalık
    024 // sekizli
    0x14 // onaltılık

    Değişmez değer 0 ile başlıyorsa sekizlik, 0x veya 0X ile başlıyorsa onaltılık olarak kabul edilir. Olağan gösterim ondalık sayı olarak ele alınır.
    Varsayılan olarak, tüm tamsayı sabit değerleri imzalanmış int'dir. Sayının sonuna L harfini ekleyerek bir tamsayı hazır bilgisini long türünde açık bir şekilde tanımlayabilirsiniz (hem büyük L hem de küçük l kullanılır, ancak okunabilirlik açısından küçük harf kullanılmamalıdır: ile karıştırılması kolaydır

    1). Sondaki U (veya u), sabit değeri unsigned int olarak ve iki harf UL veya LU unsigned long olarak tanımlar. Örneğin:

    128u 1024UL 1L 8Lu

    Gerçek sayıları temsil eden hazır değerler, ondalık noktayla veya bilimsel (üstel) gösterimle yazılabilir. Varsayılan olarak çift tiptedirler. Kayan nokta tipini açıkça belirtmek için, F veya f son ekini ve uzun çift için - L veya l kullanmalısınız, ancak yalnızca ondalık noktalı gösterim durumunda. Örneğin:

    3,14159F 0/1f 12,345L 0,0 3el 1,0E-3E 2,1,0L

    true ve false kelimeleri bool tipi hazır değerlerdir.
    Temsil edilebilir hazır bilgi karakter sabitleri, tek tırnaklı karakterler olarak yazılır. Örneğin:

    "a" "2" "," " " (boşluk)

    Özel karakterler (sekme, satır başı) kaçış dizileri olarak yazılır. Aşağıdaki diziler tanımlanmıştır (ters eğik çizgi karakteriyle başlarlar):

    Yeni satır \n yatay sekme \t geri alma \b dikey sekme \v satır başı \r sayfa besleme \f çan \a ters eğik çizgi \\ soru \? tek alıntı \" çift alıntı \"

    kaçış dizisi Genel görünüm\ooo biçimindedir, burada ooo bir ila üç sekizli basamaktır. Bu sayı karakter kodudur. ASCII kodunu kullanarak aşağıdaki hazır değerleri yazabiliriz:

    \7 (arama) \14 (yeni satır) \0 (boş) \062("2")

    Bir karakter değişmezinin önüne L eklenebilir (örneğin, L "a"), bu, normal karakter türüyle temsil edilemiyorlarsa ulusal alfabelerin karakterlerini depolamak için kullanılan iki baytlık bir karakter türü olan wchar_t özel türü anlamına gelir. Çince veya Japonca harfler gibi.
    Bir dize sabit değeri, çift tırnak içine alınmış bir karakter dizisidir. Böyle bir hazır bilgi birkaç satıra yayılabilir, bu durumda satırın sonuna ters eğik çizgi konur. Özel karakterler, kendi kaçış dizileriyle temsil edilebilir. İşte dize hazır değerlerinin örnekleri:

    "" (boş dize) "a" "\nCC\options\tfile.\n" "bir çok satırlı \ dize sabit değeri, \ devamını ters eğik çizgi ile bildirir"

    Aslında bir dize sabit değeri bir dizidir karakter sabitleri, burada, C ve C++ dillerinin kurallarına göre, son öğe her zaman 0 (\0) kodlu özel bir karakterdir.
    "A" değişmez değeri, tek bir A karakterini belirtir ve "A" dizesi, iki öğeden oluşan bir diziyi belirtir: "A" ve \0 (boş bir karakter).
    Bir wchar_t tipi olduğu için, bu tip değişmez değerler vardır ve tek tek karakterlerde olduğu gibi L ön eki ile gösterilir:

    L "geniş bir dize sabit değeri"

    wchar_t türünde bir dizge sabit değeri, aynı türde boş sonlandırılmış bir karakter dizisidir.
    Bir program testinde bir satırda iki veya daha fazla dize sabit değeri (char veya wchar_t türünde) görünürse, derleyici bunları tek bir dizede birleştirir. Örneğin, aşağıdaki metin

    "iki" "bazı"

    sekiz karakterlik bir dizi üretecek - ikili ve sonlandırıcı boş karakter. Dize birleştirmenin sonucu farklı tip tanımsız Eğer yazarsanız:

    // bu iyi bir fikir değil "iki" L "biraz"

    o zaman bazı bilgisayarlarda sonuç anlamlı bir dizi olacaktır ve diğerinde tamamen farklı bir şey olabilir. Belirli bir derleyicinin uygulama özelliklerini kullanan programlar veya işletim sistemi, taşınabilir değildir. Bu tür yapıların kullanımını kesinlikle önermiyoruz.

    Alıştırma 3.1

    Aşağıdaki değişmezlerin tanımlarındaki farkı açıklayın:

    (a) "a", L"a", "a", L"a" (b) 10, 10u, 10L, 10uL, 012, 0*C (c) 3.14, 3.14f, 3.14L

    Alıştırma 3.2

    Aşağıdaki örneklerdeki hatalar nelerdir?

    (a) "F\144rgus?\014 ile kim gider" (b) 3.14e1L (c) "iki" L"biraz" (d) 1024f (e) 3.14UL (f) "çok satırlı yorum"

    3.2. Değişkenler

    2'nin 10'uncu kuvvetini toplama problemini çözdüğümüzü hayal edin. Şunu yazıyoruz:

    #katmak
    int ana() (
    // ilk çözüm
    cout<< "2 raised to the power of 10: ";
    cout<< 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2;
    cout<< endl;
    0 dönüşü;
    }

    Sorun çözüldü, ancak değişmez 2'nin gerçekten 10 kez tekrarlanıp tekrarlanmadığını tekrar tekrar kontrol etmemiz gerekti.Bu uzun ikili diziyi yazarken bir hata yapmadık ve program doğru sonucu verdi - 1024.
    Ama şimdi bizden 2'yi 17'nin kuvvetine ve ardından 23'e yükseltmemiz istendi. Program metnini her seferinde değiştirmek son derece elverişsizdir! Ve daha da kötüsü, fazladan iki yazarak veya atlayarak hata yapmak çok kolaydır ... Peki ya 0'dan 15'e kadar ikinin bir kuvvetler tablosunu yazdırmanız gerekirse? Ortak bir forma sahip iki satırı 16 kez tekrarlayın:

    Cout<< "2 в степени X\t"; cout << 2 * ... * 2;

    burada X sırayla 1 artırılır ve nokta yerine gerekli sayıda hazır bilgi ikame edilir?

    Evet, işi hallettik. Müşterinin sonuçtan memnun kaldığı için ayrıntılara girmesi pek olası değildir. Gerçek hayatta, bu yaklaşım genellikle işe yarar ve haklı çıkar: sorun açık ara en zarif şekilde değil, istenen zaman diliminde çözülür. Daha güzel ve yetkin bir seçenek aramak pratik olmayan bir zaman kaybı olabilir.

    Bu durumda "kaba kuvvet yöntemi" doğru cevabı verir, ancak sorunu bu şekilde çözmek ne kadar tatsız ve sıkıcıdır! Hangi adımları atacağımızı tam olarak biliyoruz, ancak adımların kendisi basit ve monoton.

    Kural olarak, aynı görev için daha karmaşık mekanizmaların dahil edilmesi, hazırlık aşamasının süresini önemli ölçüde artırır. Ek olarak, ne kadar karmaşık mekanizmalar kullanılırsa, hata olasılığı o kadar artar. Ancak kaçınılmaz hatalara ve kötü hamlelere rağmen, "yüksek teknoloji" kullanımı, bu teknolojilerin yeteneklerimizi büyük ölçüde genişletmesi bir yana, geliştirme hızında fayda sağlayabilir. Ve - ilginç olan ne! Karar sürecinin kendisi çekici hale gelebilir.
    Örneğimize dönelim ve uygulamasını "teknolojik olarak iyileştirmeye" çalışalım. Numaramızı yükseltmek istediğimiz kuvvetin değerini depolamak için adlandırılmış bir nesne kullanabiliriz. Ayrıca, yinelenen değişmez değerler dizisi yerine, döngü operatörünü kullanabiliriz. İşte nasıl görüneceği:

    #katmak
    int ana()
    {
    // int türündeki nesneler
    int değeri = 2;
    int güç = 10;
    cout<< value << " в степени "
    << pow << ": \t";
    int res = 1;
    // döngü operatörü:
    // res hesaplamasını tekrarla
    // cnt pow'dan büyük olana kadar
    için (int cnt=1; cnt<= pow; ++cnt)
    res = res * değer;
    cout<< res << endl;
    }

    value, pow, res ve cnt, değerleri saklamanıza, değiştirmenize ve almanıza izin veren değişkenlerdir. for döngüsü deyimi, sonuç hesaplama dizesini pow kez tekrarlar.
    Şüphesiz çok daha esnek bir program oluşturduk. Ancak, bu hala bir işlev değildir. Bir sayının derecesini hesaplamak için herhangi bir programda kullanılabilecek gerçek bir işlev elde etmek için, hesaplamaların genel bölümünü seçmeniz ve parametre olarak belirli değerleri ayarlamanız gerekir.

    int pow(int val, int exp) ( for (int res = 1; exp > 0; --exp) res = res * val; res dönüşü; )

    Artık istenen sayının herhangi bir derecesini elde etmek zor olmayacak. İşte son görevimiz şu şekilde uygulanıyor - 0'dan 15'e kadar ikinin bir kuvvet tablosunu yazdırmak:

    #katmak harici int pow(int,int); int main() ( int val = 2; int exp = 15;
    cout<< "Степени 2\n";
    için (int cnt=0; cnt<= exp; ++cnt)
    cout<< cnt << ": "
    << pow(val, cnt) << endl;
    0 dönüşü;
    }

    Tabii ki pow() fonksiyonumuz hala yeterince genel ve yeterince güvenilir değil. Gerçek sayılar üzerinde çalışamaz, sayıları yanlış bir şekilde negatif bir kuvvete yükseltir - her zaman 1 döndürür. Büyük bir sayıyı büyük bir kuvvete yükseltmenin sonucu bir int değişkenine sığmayabilir ve sonra rastgele yanlış bir değer döndürülür. Yaygın olarak kullanılması amaçlanan işlevleri yazmanın ne kadar zor olabileceğini görüyor musunuz? Belirli bir sorunu çözmeyi amaçlayan belirli bir algoritmayı uygulamaktan çok daha zordur.

    3.2.1. değişken nedir

    Değişken, veya bir obje- bu, programdan erişebildiğimiz adlandırılmış bir hafıza alanıdır; oraya değerler koyabilir ve sonra onları alabilirsiniz. Her C++ değişkeni, o bellek alanının boyutunu ve konumunu, saklayabileceği değer aralığını ve o değişkene uygulanabilen işlemler kümesini karakterize eden belirli bir türe sahiptir. Farklı türde beş nesneyi tanımlamaya bir örnek:

    int öğrenci_sayı; çift ​​maaş; bool on_loan; sokak_adresi dizileri; karakter sınırlayıcı;

    Bir değişken, tıpkı bir hazır bilgi gibi, belirli bir türe sahiptir ve değerini belleğin bazı alanlarında saklar. Adreslenebilirlik- kelimenin tam anlamıyla eksik olan şey bu. Bir değişkenle ilişkili iki değer vardır:

    • bu bellek alanında depolanan ve hem bir değişkende hem de değişmez değerde bulunan değerin kendisi veya r-değeri (okuma değerinden - okuma değeri);
    • değişkenle ilişkili bellek alanının adresinin değeri veya l-değeri (konum değerinden - konum değerinden) - r-değerinin depolandığı yer; sadece nesneye aittir.

    ifadede

    Ch = ch - "0";

    ch değişkeni, atama sembolünün hem solunda hem de sağındadır. Sağda okunan değer bulunur (ch ve karakter değişmezi "0"): değişkenle ilişkilendirilen veri ilgili bellek alanından okunur. Solda konum değeri bulunur: çıkarmanın sonucu, ch değişkeni ile ilişkili bellek alanına yerleştirilir. Genel olarak, bir atama operatörünün sol işleneni bir l değeri olmalıdır. Aşağıdaki ifadeleri yazamayız:

    // derleme hataları: soldaki değerler l-değerleri değil // hata: sabit değer bir l-değeri değil 0 = 1; // hata: aritmetik ifade l-değeri maaş + maaş değil * 0.10 = yeni_salary;

    Değişken tanımı operatörü, bunun için bellek ayırır. Bir nesnenin kendisiyle ilişkili yalnızca bir bellek alanı olduğundan, böyle bir işleç bir programda yalnızca bir kez olabilir. Bir kaynak dosyada tanımlanan bir değişkenin başka bir kaynak dosyada kullanılması gerekiyorsa sorunlar ortaya çıkar. Örneğin:

    // file module0.C // bir fileName nesnesi tanımlar string fileName; // ... dosyaAdı'nı bir değere ayarla
    // dosya modülü1.C
    // fileName nesnesini kullanır
    // ne yazık ki derlenmiyor:
    // dosyaAdı modül1.C'de tanımlı değil
    ifstream girdi_dosyası(dosyaAdı);

    C++, bir nesneye ilk erişilmeden önce bilinmesini gerektirir. Bunun nedeni, nesnenin türüne göre doğru kullanılmasını sağlama ihtiyacıdır. Örneğimizde, modül1.C içinde dosyaAdı tanımlı olmadığı için bir derleme hatasına neden olacaktır. Bu hatayı önlemek için derleyiciye önceden tanımlanmış fileName değişkenini söylemeliyiz. Bu, değişken bildirim bildirimi kullanılarak yapılır:

    // file module1.C // fileName nesnesini kullanır // fileName bildirilir, yani program alır
    // ikincil tanımı olmadan bu nesne hakkında bilgi
    harici dize dosyaAdı; ifstream girdi_dosyası(dosyaAdı)

    Bir değişken bildirimi, derleyiciye verilen ada ve verilen türe sahip bir nesnenin programın herhangi bir yerinde tanımlandığını söyler. Bildirildiğinde bir değişken için bellek ayrılmaz. (extern anahtar sözcüğü Bölüm 8.2'de ele alınmıştır.)
    Bir program, aynı değişkenin herhangi bir sayıda bildirimini içerebilir, ancak yalnızca bir kez tanımlanabilir. Bu tür bildirimler, bunları gerektiren modüller dahil olmak üzere başlık dosyalarına uygun şekilde yerleştirilir. Böylece nesneler hakkındaki bilgileri tek bir yerde saklayabilir ve gerekirse değiştirme kolaylığı sağlayabiliriz. (Bölüm 8.2'de başlık dosyaları hakkında daha fazla konuşacağız.)

    3.2.2. Değişken ismi

    değişken adı veya tanımlayıcı, latin harfleri, rakamlar ve alt çizgi karakterinden oluşabilir. İsimlerdeki büyük ve küçük harfler farklıdır. C++ dili bir tanımlayıcının uzunluğunu sınırlamaz, ancak gosh_this_is_an_impossably_name_to_type gibi çok uzun adlar kullanmak sakıncalıdır.
    Bazı kelimeler C++'da anahtar kelimelerdir ve tanımlayıcı olarak kullanılamazlar; Tablo 3.1 bunların tam bir listesini sunar.

    Tablo 3.1. Anahtar kelimeler C++

    asm Oto bool kırmak dava
    yakalamak karakter sınıf sabit const_cast
    devam etmek varsayılan silmek Yapmak çift
    dinamik_cast başka Sıralama açık ihracat
    harici YANLIŞ batmadan yüzmek için arkadaş
    git eğer Çizgide int uzun
    değişken ad alanı yeni Şebeke özel
    korumalı halk kayıt olmak yeniden yorumlamak geri dönmek
    kısa imzalı boyutu statik static_cast
    yapı anahtar şablon Bu fırlatmak
    typedef doğru denemek typeid yazı adı
    birlik geçersiz birlik kullanarak sanal geçersiz

    Programınızın metnini daha anlaşılır hale getirmek için, nesneleri adlandırmak için genel olarak kabul edilen kuralları izlemenizi öneririz:

    • değişken adı genellikle küçük harflerle yazılır, örneğin index (karşılaştırma için: Index türün adıdır ve INDEX #define önişlemci yönergesi kullanılarak tanımlanan bir sabittir);
    • tanımlayıcı, programdaki nesnenin amacını açıklayan bir anlam taşımalıdır, örneğin: doğum_tarihi veya maaş;

    böyle bir ad doğum_tarihi gibi birkaç sözcükten oluşuyorsa, sözcükleri alt çizgi karakteriyle (doğum_tarihi) ayırmak veya sonraki her sözcüğü büyük harfle yazmak (birthDate) gelenekseldir. Nesne Yönelimli Yaklaşıma alışkın olan programcıların sözcüklerin büyük harfle yazılmasını tercih ettikleri, oysa_who_have_writing_C yazanların alt çizgi karakterini çok kullandıkları gözlemlendi. İki yöntemden hangisinin daha iyi olduğu bir zevk meselesidir.

    3.2.3. nesne tanımı

    En basit durumda, nesne tanımlama operatörü şunlardan oluşur: tip belirleyici Ve Nesne adı ve noktalı virgülle biter. Örneğin:

    çift ​​maaş; çift ​​ücret; int ay; gün; yıl; işaretsiz uzun mesafe;

    Aynı türde birden çok nesneyi tek bir ifadede tanımlayabilirsiniz. Bu durumda, adları virgülle ayrılmış olarak listelenir:

    Çift maaş, ücret; int ay, gün, yıl; işaretsiz uzun mesafe;

    Basitçe bir değişkeni tanımlamak, başlangıç ​​değerini belirlemez. Bir nesne global olarak tanımlanırsa, C++ belirtimi onun null olarak başlatılacağını garanti eder. Değişken yerel ise veya dinamik olarak tahsis edilmişse (new operatörü kullanılarak), başlangıç ​​değeri tanımlanmamıştır, yani bazı rasgele değerler içerebilir.
    Bunun gibi değişkenleri kullanmak çok yaygın bir hatadır ve fark edilmesi de zordur. En azından nesnenin kendi kendini başlatıp başlatamayacağının bilinmediği durumlarda, bir nesnenin başlangıç ​​değerinin açıkça belirtilmesi önerilir. Sınıf mekanizması, varsayılan değerleri atamak için kullanılan bir varsayılan kurucu kavramını tanıtır. (Bölüm 2.3'te bundan zaten bahsetmiştik. Varsayılan oluşturucular hakkında konuşmaya biraz sonra, standart kitaplıktan dize ve karmaşık sınıfları tartışacağımız Bölüm 3.11 ve 3.15'te devam edeceğiz.)

    int main() ( // başlatılmamış yerel nesne int ival;
    // string türündeki nesne başlatıldı
    // varsayılan kurucu
    dizi projesi;
    // ...
    }

    İlk değer, doğrudan değişken tanımı ifadesinde belirtilebilir. C++'da, iki tür değişken başlatmaya izin verilir - açık, bir atama işleci kullanılarak:

    Başlangıç ​​= 1024; string projesi = "Fantasia 2000";

    ve zımni, başlangıç ​​değeri parantez içinde belirtilmiş olarak:

    Intival(1024); string projesi("Fantasia 2000");

    Her iki seçenek de eşdeğerdir ve ival tamsayı değişkeni için başlangıç ​​değerlerini 1024 ve proje dizisi için "Fantasia 2000" olarak ayarlayın.
    Açık başlatma, değişkenleri bir liste olarak tanımlarken de kullanılabilir:

    Çift maaş = 9999,99, maaş = maaş + 0,01; int ay = 08; gün=07, yıl=1955;

    Değişken tanımlandığı anda görünür (ve programda geçerli) hale gelir, böylece ücret değişkenini yeni tanımlanan maaş değişkeninin bir miktar sabitle toplamı ile başlatabiliriz. Yani tanım:

    // doğru ama anlamsız int tuhaf = tuhaf;

    anlamsız olmasına rağmen sözdizimsel olarak geçerlidir.
    Yerleşik veri türleri, boş değer ayarlamak için özel bir sözdizimine sahiptir:

    // ival 0'a ayarlanır ve dval 0.0'a ayarlanır int ival = int(); çift ​​dval = çift();

    Aşağıdaki tanımda:

    // int(), vektörün 10 öğesinin her birine uygulanır< int >ivec(10);

    vektörün on öğesinin her biri int() ile başlatılır. (Bölüm 2.8'de vektör sınıfından zaten bahsetmiştik. Bununla ilgili daha fazla bilgi için bkz. Bölüm 3.10 ve Bölüm 6.)
    Bir değişken, işlev çağrıları da dahil olmak üzere herhangi bir karmaşıklığın ifadesi ile başlatılabilir. Örneğin:

    #katmak #katmak
    çift ​​fiyat = 109,99, indirim = 0,16;
    çift ​​satış_fiyatı(fiyat * indirim);
    string pet("kırışıklıklar"); harici int get_value(); intval = get_value();
    işaretsiz abs_val = abs(val);

    abs(), bir parametrenin mutlak değerini döndüren standart bir işlevdir.
    get_value(), bir tamsayı değeri döndüren kullanıcı tanımlı bir işlevdir.

    Alıştırma 3.3

    Aşağıdaki değişken tanımlarından hangisi sözdizimi hataları içerir?

    (a) int car = 1024, auto = 2048; (b) int val = ival; (c) int ival(int()); (d) çift maaş = ücret = 9999,99; (e) cin >> int girdi_değeri;

    Alıştırma 3.4

    l-değeri ile r-değeri arasındaki farkı açıklayın. Örnekler ver.

    Alıştırma 3.5

    Her örneğin birinci ve ikinci satırlarında ad ve öğrenci değişkenlerinin kullanımındaki farklılıkları bulun:

    (a) harici dizi adı; stringname("alıştırma 3.5a"); (b) dış vektör öğrenciler; vektör öğrenciler;

    Alıştırma 3.6

    C++'da hangi nesne adlarına izin verilmez? Bunları sözdizimsel olarak doğru olacak şekilde değiştirin:

    (a) int çift = 3,14159; (b) vektör< int >_; (c) dizi ad alanı; (d) dizi yakalama-22; (e) karakter 1_veya_2 = "1"; (f) yüzer Yüzer = 3.14f;

    Alıştırma 3.7

    Aşağıdaki global ve yerel değişken tanımları arasındaki fark nedir?

    Dizge global_class; int global_int; int ana() (
    int yerel_int;
    string local_class; // ...
    }

    3.3. İşaretçiler

    İşaretçiler ve dinamik bellek ayırma, bölüm 2.2'de kısaca tanıtıldı. Işaretçi başka bir nesnenin adresini içeren ve o nesneyi dolaylı olarak değiştirmenize izin veren bir nesnedir. Tipik olarak işaretçiler, dinamik olarak oluşturulmuş nesnelerle çalışmak, bağlantılı listeler ve hiyerarşik ağaçlar gibi ilgili veri yapılarını oluşturmak ve büyük nesneleri (diziler ve sınıf nesneleri) işlevlere parametre olarak geçirmek için kullanılır.
    Her işaretçi bir tür veriyle ilişkilendirilir ve dahili temsilleri dahili türe bağlı değildir: hem işaretçi türündeki bir nesnenin kapladığı belleğin boyutu hem de değer aralığı onlar için aynıdır. Fark, derleyicinin adreslenen nesneyi nasıl ele aldığıdır. Farklı türlere işaretçiler aynı değere sahip olabilir, ancak karşılık gelen türlerin bulunduğu bellek konumu farklı olabilir:

    • 1000 adres değerini içeren int işaretçisi 1000-1003 bellek alanına yönlendirilir (32 bitlik bir sistemde);
    • 1000 adres değerini içeren çift işaretçi, 1000-1007 bellek alanına yönlendirilir (32 bitlik bir sistemde).

    İşte bazı örnekler:

    int *ip1, *ip2; karmaşık *cp; dizi*pdizgi; vektör *pvec; çift ​​*dp;

    İşaretçi, adın önünde bir yıldız işaretiyle gösterilir. Liste değişkenlerini tanımlarken, her işaretçiden önce bir yıldız işareti gelmelidir (yukarıya bakın: ip1 ve ip2). Aşağıdaki örnekte, lp, long türünde bir nesneye işaretçidir ve lp2, long türünde bir nesnedir:

    uzun *lp, lp2;

    Aşağıdaki durumda, fp bir kayan nesne olarak yorumlanır ve fp2 bunun bir işaretçisidir:

    Float fp, *fp2;

    Başvuru işleci (*), addan boşluklarla ayrılabilir ve hatta bir tür anahtar sözcüğüne doğrudan bitişik olabilir. Bu nedenle, aşağıdaki tanımlar sözdizimsel olarak doğrudur ve tamamen eşdeğerdir:

    //uyarı: ps2 bir diziye işaretçi değildir! dizi* ps, ps2;

    İşaretçi bunlardan yalnızca ilki olmasına rağmen, hem ps hem de ps2'nin işaretçiler olduğu varsayılabilir.
    İşaretçi değeri 0 ise, herhangi bir nesne adresi içermez.
    int türünde bir değişken verilsin:

    Başlangıç ​​= 1024;

    Aşağıda, int pi ve pi2 işaretçilerini tanımlama ve kullanma örnekleri verilmiştir:

    //pi adresi sıfır olarak başlatıldı int *pi = 0;
    // pi2 adresi ival ile başlatıldı
    int *pi2 =
    // doğru: pi ve pi2, ival adresini içerir
    pi = pi2;
    // pi2 sıfır adresini içerir
    pi2 = 0;

    İşaretçiye adres olmayan bir değer atanamaz:

    // hata: pi int olamaz pi = ival

    Benzer şekilde, bir tür işaretçiyi başka türdeki bir nesnenin adresi olan bir değere atayamazsınız. Aşağıdaki değişkenler tanımlanmışsa:

    çift ​​dval; çift ​​*ps =

    o zaman aşağıdaki her iki atama ifadesi de bir derleme hatasına neden olur:

    // derleme hataları // geçersiz veri türü ataması: int*<== double* pi = pd pi = &dval;

    Pi değişkeni bir dval nesnesinin adresini içeremez demek değildir - farklı tipteki nesnelerin adresleri aynı uzunluğa sahiptir. Bu tür adres karıştırma işlemleri kasıtlı olarak yasaklanmıştır, çünkü derleyicinin nesneleri yorumlaması onlara yönelik işaretçinin türüne bağlıdır.
    Elbette, adresin işaret ettiği nesneyle değil, adresin değeriyle ilgilendiğimiz zamanlar vardır (diyelim ki bu adresi başka bir adresle karşılaştırmak istiyoruz). Bu tür durumları çözmek için, herhangi bir veri türünü işaret edebilen özel bir geçersiz işaretçi tanıtılır ve aşağıdaki ifadeler doğru olur:

    // doğru: void* herhangi bir türden adres içerebilir void *pv = pi; pv=pd;

    void* tarafından işaret edilen nesnenin türü bilinmiyor ve bu nesneyi değiştiremiyoruz. Böyle bir işaretçiyle yapabileceğimiz tek şey, değerini başka bir işaretçiye atamak veya onu bir adres değeriyle karşılaştırmaktır. (Void işaretçisi hakkında Bölüm 4.14'te daha fazla konuşacağız.)
    Adresine sahip bir nesneye atıfta bulunmak için, bir yıldız işaretiyle (*) gösterilen başvuru işlemini veya dolaylı adreslemeyi uygulamanız gerekir. Aşağıdaki değişken tanımlarına sahip olmak:

    int değişken = 1024;, değişken2 = 2048; int*pi =

    // ival'in ival2'ye dolaylı atanması *pi = ival2;
    // ival'in değer ve değer olarak dolaylı kullanımı
    *pi = abs(*pi); // val = abs(ival);
    *pi = *pi + 1; // val = val + 1;

    int türündeki bir nesneye adres işlemini (&) uyguladığımızda, int* türünün sonucunu alırız.
    int*pi =
    Aynı işlem int* (işaretçiden int'e) türünde bir nesneye uygulanırsa, int'e işaretçiye bir işaretçi elde ederiz, yani. int**. int**, int türündeki bir nesnenin adresini içeren bir nesnenin adresidir. ppi başvurusunu kaldırarak, ival adresini içeren int* türünde bir nesne elde ederiz. ival nesnesinin kendisini elde etmek için, ppi dereference işlemi iki kez uygulanmalıdır.

    int **ppi = π int *pi2 = *ppi;
    cout<< "Значение ival\n" << "явное значение: " << ival << "\n"
    << "косвенная адресация: " << *pi << "\n"
    << "дважды косвенная адресация: " << **ppi << "\n"

    İşaretçiler aritmetik ifadelerde kullanılabilir. İki ifadenin tamamen farklı eylemler gerçekleştirdiği aşağıdaki örneğe dikkat edin:

    Int i, j, k; int *pi = // ben = ben + 2
    *pi = *pi + 2; // pi'nin içerdiği adresi 2 arttır
    pi = pi + 2;

    Bir işaretçiye bir tamsayı değeri ekleyebilir, ondan da çıkarabilirsiniz. Bir işaretçiye 1 eklemek, değerini karşılık gelen türdeki bir nesneye tahsis edilen bellek alanının boyutu kadar artırır. char 1 bayt alırsa, int 4 alır ve double 8 alırsa, char, int ve double'a işaretçilere 2 eklemek sırasıyla değerlerini 2, 8 ve 16 artırır.Bu nasıl yorumlanabilir? Aynı türdeki nesneler arka arkaya bellekte bulunuyorsa, işaretçiyi 1 artırmak, bir sonraki nesneyi göstermesine neden olur. Bu nedenle, işaretçi aritmetiği en çok dizi işlemede kullanılır; başka herhangi bir durumda, pek haklı sayılmazlar.
    Bir yineleyici kullanarak bir dizinin öğeleri üzerinde yineleme yaparken, adres aritmetiğini kullanmanın tipik bir örneği şöyle görünür:

    iç; int *iter = int *iter_end =
    while (iter != iter_end) (
    do_something_with_value(*iter);
    ++iter;
    }

    Alıştırma 3.8

    Değişken tanımları verilmiştir:

    int değişken = 1024, değişken2 = 2048; int *pi1 = &ival, *pi2 = &ival2, **pi3 = 0;

    Aşağıdaki atama işlemleri yapıldığında ne olur? Bu örneklerde hatalar var mı?

    (a) ival = *pi3; (e) pi1 = *pi3; (b) *pi2 = *pi3; (f) ival = *pi1; (c) ival = pi2; (g) pi1 = ival; (d) pi2 = *pi1; (h) pi3 =

    Alıştırma 3.9

    İşaretçi manipülasyonu, C ve C++'nın en önemli yönlerinden biridir, ancak hata yapmak kolaydır. Örneğin, kod

    pi = pi = pi + 1024;

    neredeyse kesinlikle pi'nin rastgele bir hafıza alanını işaret etmesine neden olacaktır. Bu atama operatörü ne yapar ve hangi durumda hataya yol açmaz?

    Alıştırma 3.10

    Bu program, işaretçilerin yanlış kullanımıyla ilgili bir hata içeriyor:

    int foobar(int *pi) ( *pi = 1024; dönüş *pi; )
    int ana() (
    int*pi2 = 0;
    int ival = foobar(pi2);
    0 dönüşü;
    }

    hata nedir? Nasıl düzeltebilirim?

    Alıştırma 3.11

    Önceki iki alıştırmadaki hatalar, C++'ın çalışma zamanında işaretçi değerlerini doğrulamaması nedeniyle ortaya çıkıyor ve ölümcül sonuçlara yol açıyor. Sizce neden böyle bir kontrol yapılmadı? İşaretçi kullanımını daha güvenli hale getirmek için bazı genel yönergeler sunabilir misiniz?

    3.4. Dize türleri

    C++, C'den devralınan yerleşik tür ve C++ standart kitaplığından dize sınıfı olmak üzere iki tür dizeyi destekler. String sınıfı daha birçok özellik sağlar ve bu nedenle kullanımı daha uygundur, ancak pratikte genellikle yerleşik bir tür kullanmanız veya nasıl çalıştığını iyi anlamanız gereken durumlar vardır. (Bir örnek, main() işlevine iletilen komut satırı seçeneklerini ayrıştırmak olabilir. Bunu Bölüm 7'de ele alacağız.)

    3.4.1. yerleşik dize türü

    Daha önce bahsedildiği gibi, C'den devralınan yerleşik dize türü C++'a aktarılır. Bir karakter dizisi bellekte bir dizi olarak saklanır ve buna char* türünde bir işaretçi kullanılarak erişilir. C standart kitaplığı, dizeleri işlemek için bir dizi işlev sağlar. Örneğin:

    // dizgenin uzunluğunu döndürür int strlen(const char*);
    // iki diziyi karşılaştırır
    int strcmp(const char*, const char*);
    // bir diziyi diğerine kopyalar
    char* strcpy(char*, const char*);

    C standart kitaplığı, C++ kitaplığının bir parçasıdır. Kullanmak için bir başlık dosyası eklemeliyiz:

    #katmak

    Dizeye erişmemizi sağlayan char işaretçisi, dizeye karşılık gelen karakter dizisini işaret eder. Gibi bir dize değişmezi yazdığımızda bile

    Const char *st = "Bir şişe şarabın fiyatı\n";

    derleyici, dizideki tüm karakterleri bir diziye koyar ve ardından dizideki ilk öğenin adresini st'ye atar. Böyle bir işaretçi kullanarak bir dizeyle nasıl çalışılabilir?
    Bir dizgenin karakterlerini sıralamak için genellikle adres aritmetiği kullanılır. Dize her zaman boş bir karakterle bittiği için, sonraki karakter boş olana kadar işaretçiyi 1 artırabilirsiniz. Örneğin:

    iken (*st++) ( ... )

    st referansı kaldırılır ve elde edilen değerin doğruluğu kontrol edilir. Sıfır olmayan herhangi bir değer doğru kabul edilir ve bu nedenle döngü, 0 kodlu bir karaktere ulaşıldığında sona erer Arttırma işlemi ++ st işaretçisine 1 ekler ve böylece onu bir sonraki karaktere ilerletir.
    Bir dizgenin uzunluğunu döndüren bir işlevin uygulanması şöyle görünebilir. Bir işaretçi boş bir değer içerebileceğinden (hiçbir şeye işaret etmeyebilir), başvuru işleminden önce kontrol edilmesi gerektiğini unutmayın:

    int string_length(const char *st) ( int cnt = 0; if (st) while (*st++) ++cnt; dönüş cnt; )

    Yerleşik bir dizge iki durumda boş kabul edilebilir: dizgenin işaretçisi bir boş değere sahipse (bu durumda hiç dizgemiz yoktur) veya bir boş karakterden oluşan bir diziye işaret ediyorsa (yani, bir tek bir önemli karakter içermeyen dize).

    // pc1 herhangi bir karakteri adreslemiyor dizi char *pc1 = 0; // pc2 adresleri boş karakter const char *pc2 = "";

    Acemi bir programcı için, yerleşik türdeki dizeleri kullanmak, çok düşük uygulama düzeyi ve adres aritmetiği olmadan yapamama nedeniyle hatalarla doludur. Aşağıda yeni başlayanlar tarafından yapılan bazı tipik hataları göstereceğiz. Görev basit: bir dizenin uzunluğunu hesaplayın. İlk versiyon yanlış. Düzelt onu.

    #katmak const char *st = "Bir şişe şarabın fiyatı\n"; int ana() (
    iç = 0;
    while (st++) ++len; cout<< len << ": " << st;
    0 dönüşü;
    }

    Bu sürümde, st işaretçisinin referansı kaldırılmaz. Bu nedenle, 0'a eşitlik açısından kontrol edilen st tarafından işaret edilen karakter değil, işaretçinin kendisidir. Bu işaretçi başlangıçta sıfır olmayan bir değere (satır adresi) sahip olduğundan, hiçbir zaman sıfır olmayacak ve döngü süresiz olarak çalışacaktır.
    Programın ikinci sürümünde bu hata ortadan kaldırılmıştır. Program başarıyla sona eriyor, ancak sonuç yanlış. Bu sefer nerede yanıldık?

    #katmak const char *st = "Bir şişe şarabın fiyatı\n"; int ana()
    {
    iç = 0;
    while (*st++) ++len; cout<< len << ": " << st << endl;
    0 dönüşü;
    }

    Hata, döngünün bitiminden sonra, st işaretçisinin orijinal karakter değişmezine değil, bu değişmez değerin son sıfırından sonra bellekte bulunan karaktere hitap etmesidir. Bu yerde her şey olabilir ve programın çıktısı rastgele bir karakter dizisi olacaktır.
    Bu hatayı düzeltmeyi deneyebilirsiniz:

    St = st - len; cout<< len << ": " << st;

    Şimdi programımız anlamlı bir şey üretiyor ama tamamen değil. Cevap şöyle görünür:

    18: bir şişe şarap

    Sonlandırıcı null karakterinin hesaplanan uzunluğa dahil edilmediğini hesaba katmayı unuttuk. st, dizenin uzunluğu artı 1 ile kaydırılmalıdır. İşte sonunda doğru ifade:

    St \u003d st - len - 1;

    ve işte doğru sonuç:

    18: Bir şişe şarabın fiyatı

    Ancak programımızın zarif göründüğünü söyleyemeyiz. Şebeke

    St \u003d st - len - 1;

    program tasarımının erken bir aşamasında yapılan bir hatayı düzeltmek için eklendi - doğrudan st işaretçisini artırıyor. Bu ifade programın mantığına uymamakta ve kodun anlaşılması artık zorlaşmaktadır. Bu tür düzeltmelere genellikle yamalar denir - mevcut bir programda bir delik açmak için tasarlanmış bir şey. Çok daha iyi bir çözüm, mantığı yeniden düşünmek olacaktır. Bizim durumumuzdaki seçeneklerden biri, st değeriyle başlatılan ikinci bir işaretçi tanımlamak olacaktır:

    Sabit karakter *p = st;

    Artık uzunluk döngüsünde p kullanılabilir ve st değeri değişmeden kalır:

    iken (*p++)

    3.4.2. dize sınıfı

    Az önce gördüğümüz gibi, yerleşik dize türünün kullanılması hataya açık ve çok düşük bir düzeyde uygulandığı için pek uygun değil. Bu nedenle, bir dize türünü temsil etmek için kendi sınıfınızı veya sınıflarınızı geliştirmek oldukça yaygındır - hemen hemen her şirket, departman veya bireysel projenin kendi dize uygulaması vardır. Ne diyebilirim ki, bu kitabın önceki iki baskısında da aynı şeyi yaptık! Bu, programların uyumluluk ve taşınabilirlik sorunlarına yol açtı. Standart string sınıfının C++ standart kitaplığı tarafından uygulanması, bisikletlerin bu icadına bir son vermeyi amaçlıyordu.
    Dize sınıfının sahip olması gereken minimum işlem kümesini belirlemeye çalışalım:

    • bir karakter dizisi (yerleşik türde dize) veya dize türünde başka bir nesne ile başlatma. Yerleşik türde ikinci bir yetenek yoktur;
    • bir satırı diğerine kopyalamak. Yerleşik bir tür için strcpy() işlevini kullanmanız gerekir;
    • okuma ve yazma için bir dizgenin bireysel karakterlerine erişim. Yerleşik dizide bu, alt simge işlemi veya dolaylı adresleme kullanılarak yapılır;
    • eşitlik için iki diziyi karşılaştırmak. Yerleşik bir tür için strcmp() işlevini kullanın;
    • iki diziyi birleştirme, sonucu ya üçüncü bir dizi olarak ya da orijinal dizilerden biri yerine alma. Yerleşik bir tür için strcat() işlevi kullanılır, ancak sonucu yeni bir satırda almak için strcpy() ve strcat() işlevlerini sırayla kullanmalısınız;
    • bir dizinin uzunluğunu hesaplamak. Yerleşik bir dizgenin uzunluğunu strlen() işlevini kullanarak öğrenebilirsiniz;
    • bir dizenin boş olup olmadığını bulma yeteneği. Yerleşik dizeler için, bu amaçla iki koşulun kontrol edilmesi gerekir: char str = 0; //... if (! str || ! *str) dönüş;

    C++ standart kitaplık dize sınıfı, tüm bu işlemleri (ve Bölüm 6'da göreceğimiz gibi çok daha fazlasını) uygular. Bu bölümde, bu sınıfın temel işlemlerini nasıl kullanacağımızı öğreneceğiz.
    String sınıfının nesnelerini kullanmak için uygun başlık dosyasını eklemelisiniz:

    #katmak

    Burada, önceki bölümden, string türünde bir nesne tarafından temsil edilen ve bir karakter dizisi ile ilklendirilen bir dizi örneği verilmiştir:

    #katmak string st("Bir şişe şarabın fiyatı\n");

    Dizenin uzunluğu size() üye işlevi tarafından döndürülür (uzunluk, sonlandırıcı boş karakteri içermez).

    Cout<< "Длина " << st << ": " << st.size() << " символов, включая символ новой строки\n";

    Dize tanımının ikinci biçimi, boş bir dize belirtir:

    Dizi st2; // boş satır

    Bir stringin boş olup olmadığını nasıl anlarız? Elbette uzunluğunu 0 ile karşılaştırabilirsiniz:

    if (! st.size()) // doğru: boş

    Ancak, boş bir dize için true ve boş olmayan bir dize için false döndüren özel bir empty() yöntemi de vardır:

    if (st.empty()) // doğru: boş

    Yapıcının üçüncü biçimi, string türündeki bir nesneyi aynı türden başka bir nesneyle başlatır:

    Dizi st3(st);

    st3 dizisi, st dizisi ile başlatılır. Bu dizelerin eşleştiğinden nasıl emin olabiliriz? Karşılaştırma operatörünü (==) kullanalım:

    If (st == st3) // başlatma başarılı oldu

    Bir satır diğerine nasıl kopyalanır? Her zamanki atama operatörüyle:

    st2 = st3; // st3'ü st2'ye kopyala

    Dizeleri birleştirmek için toplama işlecini (+) veya toplama atama işlecini (+=) kullanın. İki satır verilsin:

    String s1("merhaba, "); string s2("dünya\n");

    İlk ikisinin birleşiminden oluşan üçüncü bir dizi elde edebiliriz, böylece:

    Dizi s3 = s1 + s2;

    s1'in sonuna s2 eklemek istiyorsak şunu yazmalıyız:

    S1 += s2;

    Toplama işlemi, string sınıfındaki nesneleri yalnızca kendi aralarında değil, aynı zamanda yerleşik tipteki stringlerle de birleştirebilir. Yukarıdaki örneği, özel karakterler ve noktalama işaretleri yerleşik bir türle temsil edilecek ve anlamlı sözcükler string sınıfının nesneleri olacak şekilde yeniden yazabilirsiniz:

    Const char *pc = ", "; string s1("merhaba"); string s2("dünya");
    dizi s3 = s1 + pc + s2 + "\n";

    Bu tür ifadeler işe yarar, çünkü derleyici yerleşik türdeki nesneleri otomatik olarak dize nesnelerine nasıl dönüştüreceğini bilir. Bir string nesnesine basitçe yerleşik bir string atamak da mümkündür:

    Dizi s1; const char *pc = "bir karakter dizisi"; s1=adet; // Sağ

    Ancak ters dönüşüm işe yaramıyor. Aşağıdaki yerleşik türde dize başlatmayı gerçekleştirmeye çalışmak bir derleme hatasına neden olur:

    karakter*str = s1; // Derleme Hatası

    Bu dönüştürmeyi yapmak için, üye işlevini açıkça c_str() adlı biraz garip bir adla çağırmalısınız:

    Karakter *str = s1.c_str(); // neredeyse doğru

    c_str() işlevi, yerleşik dize türünde olacağı gibi, dize nesnesinin dizesini içeren bir karakter dizisine bir işaretçi döndürür.
    Yukarıdaki bir char *str işaretçisini başlatma örneği hala tam olarak doğru değil. c_str(), nesnenin içeriğinin türü olan bu işaretçi aracılığıyla doğrudan değiştirilmesini önlemek için bir const dizisine bir işaretçi döndürür.

    Sabit karakter *

    (Bir sonraki bölümde const anahtar sözcüğünden bahsedeceğiz.) Doğru başlatma şöyle görünür:

    Const char *str = s1.c_str(); // Sağ

    Yerleşik bir tür gibi bir dize nesnesinin tek tek karakterlerine, dizin işlemi kullanılarak erişilebilir. Örneğin, tüm noktaları alt çizgi ile değiştiren bir kod parçacığı aşağıda verilmiştir:

    Stringstr("fa.disney.com"); int boyut = str.size(); için (int ix = 0; ix< size; ++ix) if (str[ ix ] == ".")
    dizi[ix] = "_";

    Şu anda string sınıfı hakkında söylemek istediklerimiz bu kadar. Aslında, bu sınıfın daha birçok ilginç özelliği ve yeteneği vardır. Diyelim ki önceki örnek tek bir replace() işlevi çağrılarak da uygulandı:

    Değiştir(str.begin(), str.end(), ".", "_");

    replace(), Bölüm 2.8'de tanıttığımız genelleştirilmiş algoritmalardan biridir ve Bölüm 12'de ayrıntılı olarak ele alınacaktır. string ve üçüncü parametresine eşit olan elemanları dördüncü parametreye değiştirir.

    Alıştırma 3.12

    Aşağıdaki ifadelerde hata arayın:

    (a) char ch = "Uzun ve dolambaçlı yol"; (b) int ival = (c) char *pc = (d) string st(&ch); (e) pc = 0; (i) adet = "0";
    (f) st = adet; (j) st =
    (g) ch = adet; (k) ch = *pc;
    (h) pc = st; (l) *pc = ival;

    Alıştırma 3.13

    Aşağıdaki döngü ifadelerinin davranışındaki farkı açıklayın:

    (st++) ++cnt iken;
    iken (*st++)
    ++snt;

    Alıştırma 3.14

    Anlamsal olarak eşdeğer iki program verilmiştir. İlki yerleşik dize türünü kullanır, ikincisi dize sınıfını kullanır:

    // ***** C dizeleri kullanılarak uygulama ***** #include #katmak
    int ana()
    {
    int hataları = 0;
    const char *pc = "çok uzun bir sabit değer dizisi"; için (int ix = 0; ix< 1000000; ++ix)
    {
    int len ​​= strlen(pc);
    char *pc2 = yeni karakter[ len + 1 ];
    strcpy(pc2, bilgisayar);
    eğer (strcmp(pc2, pc))
    ++hatalar; pc2'yi sil;
    }
    cout<< "C-строки: "
    << errors << " ошибок.\n";
    ) // ***** Dize sınıfı kullanılarak uygulama ***** #include
    #katmak
    int ana()
    {
    int hataları = 0;
    string str("çok uzun bir sabit değer dizisi"); için (int ix = 0; ix< 1000000; ++ix)
    {
    int len ​​= str.size();
    dizi dizi2 = dizi;
    eğer (str != str2)
    }
    cout<< "класс string: "
    << errors << " ошибок.\n;
    }

    Bu programlar ne işe yarar?
    İkinci uygulamanın birinciden iki kat daha hızlı olduğu ortaya çıktı. Böyle bir sonuç bekliyor muydunuz? Bunu nasıl açıklarsın?

    Alıştırma 3.15

    Son bölümde string sınıfındaki işlemler kümesine ekleyebileceğiniz veya geliştirebileceğiniz herhangi bir şey var mı? önerilerinizi açıklayın

    3.5. const belirtici

    Aşağıdaki kod örneğini ele alalım:

    For(int dizin = 0; dizin< 512; ++index) ... ;

    512 değişmezini kullanmanın iki sorunu vardır. Birincisi, program metninin algılanma kolaylığıdır. Döngü değişkeninin üst sınırı neden tam olarak 512 olsun? Bu değerin arkasında ne gizli? Rastgele görünüyor...
    İkinci sorun, kodun değiştirilmesi ve bakımının kolaylığı ile ilgilidir. Programın 10.000 satırdan oluştuğunu ve bunların %4'ünde sabit değerin 512 olduğunu varsayalım. Diyelim ki vakaların %80'inde 512 sayısı 1024 olarak değiştirilmelidir. Bu tür işlerin zahmetini ve yanlış değeri düzelterek yapılabilecek hata sayısını hayal edebiliyor musunuz?
    Bu sorunların ikisi de aynı anda çözülüyor: 512 değerinde bir nesne oluşturmamız gerekiyor. Buna bufSize gibi anlamlı bir isim vererek programı çok daha anlaşılır hale getireceğiz: döngünün tam olarak ne olduğu belli oluyor değişken ile karşılaştırılır.

    dizin< bufSize

    Bu durumda, bufSize'ı yeniden boyutlandırmak, bunların 320'sini değiştirmek için 400 kod satırına bakmayı gerektirmez. Sadece bir nesne eklemek pahasına hata olasılığı ne kadar azalır! Şimdi değer 512 lokalize.

    bufBoyutu = 512; // giriş tamponu boyutu // ... için (int dizin = 0; dizin< bufSize; ++index)
    // ...

    Geriye küçük bir sorun kalıyor: buradaki bufSize değişkeni, programda yanlışlıkla değiştirilebilen ve bulması zor bir hataya yol açabilen bir l değeridir. İşte en yaygın hatalardan biri - karşılaştırma (==) yerine atama operatörünü (=) kullanmak:

    // bufSize değerinin rastgele değişimi if (bufSize = 1) // ...

    Bu kodu çalıştırmanın bir sonucu olarak, bufSize değeri 1'e eşit olacak ve bu da programın tamamen öngörülemeyen davranışına yol açabilir. Bu tür hataların tespit edilmesi genellikle çok zordur, çünkü görünür değildirler.
    const belirleyicisini kullanmak bu sorunu çözer. Bir nesneyi şöyle bildirerek

    Sabit int bufSize = 512; // arabellek boyutunu girin

    değişkeni değeri değiştirilemeyen 512 değerinde bir sabite çeviriyoruz: bu tür girişimler derleyici tarafından durduruluyor: yukarıdaki örnekte olduğu gibi karşılaştırma yerine atama operatörünün yanlış kullanılması derleme hatasına neden olacaktır.

    // hata: (bufSize = 0) ise bir sabite değer atamaya çalışın ...

    Bir sabite değer atanamayacağı için, tanımlandığı yerde başlatılmalıdır. Bir sabiti başlatmadan tanımlamak ayrıca bir derleme hatasına neden olur:

    Sabit çift pi; // hata: başlatılmamış sabit

    Sabit çift asgari Ücret = 9.60; // Sağ? hata?
    çift ​​*ptr =

    Derleyici bu atamaya izin vermeli mi? MinWage bir sabit olduğu için bir değer atanamaz. Öte yandan, hiçbir şey yazmamızı yasaklamıyor:

    *ptr += 1,40; // minWage nesnesini değiştirin!

    Kural olarak, derleyici, işaretçilerin kullanımına karşı koruma sağlayamaz ve bu şekilde kullanılırsa bir hata sinyali veremez. Bu, program mantığının çok fazla analizini gerektirir. Bu nedenle, derleyici, sabitlerin adreslerinin sıradan işaretçilere atanmasını basitçe yasaklar.
    Peki, işaretçileri sabitlere kullanma fırsatından mahrum muyuz? HAYIR. Bunu yapmak için, const belirleyicisi ile bildirilmiş işaretçiler vardır:

    Sabit çift *cptr;

    burada cptr, const double türünde bir nesnenin işaretçisidir. İncelik, işaretçinin kendisinin bir sabit olmaması gerçeğinde yatmaktadır, bu da onun değerini değiştirebileceğimiz anlamına gelir. Örneğin:

    Sabit çift *pc = 0; const çift minWage = 9.60; // doğru: asgari ücret bilgisayarla değiştirilemiyor
    pc = çift dval = 3.14; // doğru: asgari ücret bilgisayarla değiştirilemiyor
    // dval bir sabit olmamasına rağmen
    pc = // doğru dval = 3.14159; //Sağ
    *adet = 3,14159; // hata

    Bir const nesnesinin adresi yalnızca bir sabite işaretçiye atanır. Bununla birlikte, böyle bir işaretçiye sıradan bir değişkenin adresi de atanabilir:

    Bilgisayar =

    Sabit bir işaretçi, adreslediği nesnenin dolaylı adresleme ile değiştirilmesine izin vermez. Yukarıdaki örnekte dval sabit olmasa da derleyici dval'in pc üzerinden değiştirilmesine izin vermeyecektir. (Yine, programın yürütülmesi sırasında herhangi bir noktada işaretçinin hangi adresi tutabileceğini belirleyemediği için.)
    Gerçek programlarda, sabitlere yönelik işaretçiler çoğunlukla işlevlerin biçimsel parametreleri olarak kullanılır. Kullanımları, işleve gerçek bağımsız değişken olarak iletilen nesnenin bu işlev tarafından değiştirilmeyeceğini garanti eder. Örneğin:

    // Gerçek programlarda, sabitlere işaretçiler çoğunlukla // fonksiyonların resmi parametreleri olarak kullanılır int strcmp(const char *str1, const char *str2);

    (İşlevlerden bahsederken Bölüm 7'de sabitlere işaretçiler hakkında daha fazla konuşacağız.)
    Sabit işaretçiler de vardır. (Sabit işaretçi ile sabit işaretçi arasındaki farka dikkat edin!). Bir sabit işaretçi hem bir sabiti hem de bir değişkeni adresleyebilir. Örneğin:

    Inter errNumb = 0; int *const currErr =

    Burada curErr, const olmayan bir nesneye yönelik sabit bir işaretçidir. Bu, nesnenin kendisi değiştirilebilmesine rağmen ona başka bir nesnenin adresini atayamayacağımız anlamına gelir. curErr işaretçisi şu şekilde kullanılabilir:

    Bir şey yap(); eğer (*curErr) (
    errorHandler();
    *curErr = 0; // düzelt: errNumb değerini sıfırla
    }

    Sabit bir işaretçiye bir değer atamaya çalışmak bir derleme hatasına neden olur:

    CurErr = // hata

    Sabit bir sabit işaretçi, dikkate alınan iki durumun birleşimidir.

    Sabit çift pi = 3,14159; sabit çift *sabit pi_ptr = π

    Ne pi_ptr tarafından işaret edilen nesnenin değeri ne de işaretçinin kendisinin değeri programda değiştirilemez.

    Alıştırma 3.16

    Aşağıdaki beş tanımın anlamını açıklayınız. Herhangi biri yanlış mı?

    (a) int i; (d) int *sabit cpi; (b) sabit iç ic; (e) const int *const cpic; (c) const int *pic;

    Alıştırma 3.17

    Aşağıdaki tanımlardan hangisi doğrudur? Neden?

    (a) int ben = -1; (b) sabit int ic = i; (c) const int *pic = (d) int *const cpi = (e) const int *const cpic =

    Alıştırma 3.18

    Önceki alıştırmadaki tanımları kullanarak, doğru atama işleçlerini belirleyin. Açıklamak.

    (a) ben = ic; (d) pic = cpic; (b) pic = (i) cpic = (c) cpi = pic; (f) ic = *cpic;

    3.6. referans tipi

    Bir nesneye ek bir ad vermek için bazen takma ad olarak adlandırılan bir referans türü kullanılır. Referans, tıpkı bir işaretçinin yaptığı gibi, bir nesneyi dolaylı olarak değiştirmenize olanak sağlar. Ancak bu dolaylı manipülasyon, işaretçiler için gereken özel sözdizimini gerektirmez. Genellikle referanslar, fonksiyonların biçimsel parametreleri olarak kullanılır. Bu bölümde, referans tipi nesneleri kendi başlarına kullanmaya bakacağız.
    Bir referans türü, değişken adının önünde adres işleci (&) belirtilerek belirtilir. Bağlantı başlatılmalıdır. Örneğin:

    Başlangıç ​​= 1024; // düzeltin: refVal - ival'e başvuru int &refVal = ival; // hata: başvuru int olarak başlatılmalıdır

    Başlangıç ​​= 1024; // hata: refVal int türündedir, int* değil int &refVal = int *pi = // doğru: ptrVal bir int işaretçisine referanstır *&ptrVal2 = pi;

    Bir referansı tanımladıktan sonra, onu farklı bir nesneyle çalışacak şekilde değiştiremezsiniz (bu nedenle referansın tanımlandığı yerde başlatılması gerekir). Aşağıdaki örnekte, atama ifadesi refVal'in değerini değiştirmez, yeni değer refVal'in adreslediği ival değişkenine atanır.

    int min_değer = 0; // ival, min_val değerini alır, // refVal değil, değeri min_val olarak değiştirir refVal = min_val;

    RefVal += 2; refVal tarafından başvurulan değişken olan ival'e 2 ekler. Benzer şekilde int ii = refVal; ii'yi ival'in geçerli değerine ayarlar, int *pi = pi'yi ival'in adresiyle başlatır.

    // int türünde iki nesne tanımlanır int ival = 1024, ival2 = 2048; // bir referans ve bir nesne tanımlı int &rval = ival, rval2 = ival2; // bir nesne, bir işaretçi ve bir referans tanımlandı
    int inal3 = 1024, *pi = ival3, &ri = ival3; // iki referans tanımlanmış int &rval3 = ival3, &rval4 = ival2;

    Sabit bir referans, başka bir türden bir nesneyle (tabii ki bir türü diğerine dönüştürme olasılığı varsa) ve aynı zamanda değişmez bir sabit gibi adres olmayan bir değerle başlatılabilir. Örneğin:

    çift ​​dval = 3,14159; // sadece const referansları için geçerlidir
    sabit int &ir = 1024;
    sabit int &ir2 = dval;
    sabit çift &dr = dval + 1.0;

    Const belirticisini belirtmezsek, üç başvuru tanımının tümü bir derleme hatasına neden olur. Ancak derleyicinin bu tür tanımları neden atlamadığı açık değildir. Anlamaya çalışalım.
    Değişmez değerler için bu aşağı yukarı açıktır: işaretçiler veya referanslar kullanarak bir değişmez değerin değerini dolaylı olarak değiştirememeliyiz. Başka türdeki nesnelere gelince, derleyici orijinal nesneyi yardımcı bir nesneye dönüştürür. Örneğin, yazarsak:

    Çift dval = 1024; sabit int &ri = dval;

    derleyici bunu şuna benzer bir şeye dönüştürür:

    int sıcaklık = dval; sabit int &ri = geçici;

    Ri referansına yeni bir değer atayabilseydik, aslında dval'i değil temp'yi değiştirirdik. dval'in değeri aynı kalır ki bu programcı için tamamen açık değildir. Bu nedenle, derleyici bu tür eylemleri yasaklar ve farklı türde bir nesneyle bir başvuruyu başlatmanın tek yolu onu const olarak bildirmektir.
    İşte ilk seferde anlaşılması zor olan başka bir bağlantı örneği. Bir const nesnesinin adresine bir başvuru tanımlamak istiyoruz, ancak ilk seçeneğimiz bir derleme hatasına neden oluyor:

    Sabit int değişken = 1024; // hata: sabit referans gerekli
    int *&pi_ref =

    Const belirticisini ekleyerek sorunu çözme girişimi de başarısız olur:

    Sabit int değişken = 1024; // hala hata const int *&pi_ref =

    Nedeni ne? Tanımı dikkatlice okuyarak, pi_ref'in int tipi bir nesneye sabit bir işaretçiye referans olduğunu göreceğiz. Ve bir const nesnesine const olmayan bir işaretçiye ihtiyacımız var, bu nedenle aşağıdaki giriş doğru olacaktır:

    Sabit int değişken = 1024; // Sağ
    int *const &piref=

    Referans ve işaretçi arasında iki temel fark vardır. İlk olarak, referans, tanımlandığı yerde başlatılmalıdır. İkinci olarak, referanstaki herhangi bir değişiklik onu değil, gönderme yaptığı nesneyi dönüştürür. Örneklere bakalım. Eğer yazarsak:

    int*pi = 0;

    pi işaretçisini sıfıra sıfırlarız, bu da pi'nin herhangi bir nesneyi göstermediği anlamına gelir. Aynı zamanda rekor

    sabit int &ri = 0;
    bunun gibi bir anlama gelir:
    int sıcaklık = 0;
    sabit int &ri = geçici;

    Atama işlemine gelince, aşağıdaki örnekte:

    int değişken = 1024, değişken2 = 2048; int *pi = &ival, *pi2 = pi = pi2;

    pi'nin işaret ettiği ival değişkeni değişmeden kalır ve pi, ival2'nin adresinin değerini alır. Hem pi hem de pi2 hala aynı ival2 nesnesine işaret ediyor.
    Bağlantılarla çalışırsak:

    Int &ri = ival, &ri2 = ival2; ri = ri2;

    // referans kullanma örneği // Değer sonraki_değer parametresinde döndürülür
    bool get_next_value(int &sonraki_değer); // aşırı yüklenmiş Matrix operatörü+(const Matrix&, const Matrix&);

    Intival; while (get_next_value(ival)) ...

    int &sonraki_değer = ival;

    (Referansların işlevlere resmi parametreler olarak kullanımı Bölüm 7'de daha ayrıntılı olarak ele alınmıştır.)

    Alıştırma 3.19

    Bu tanımlamalarda herhangi bir hata var mı? Açıklamak. Onları nasıl düzeltirsiniz?

    (a) int değişken = 1.01; (b) int &rval1 = 1.01; (c) int &rval2 = ival; (d) int &rval3 = (e) int *pi = (f) int &rval4 = pi; (g) int &rval5 = pi*; (h) int &*prval1 = pi; (i) sabit int &ival2 = 1; (j) sabit int &*prval2 =

    Alıştırma 3.20

    Aşağıdaki atamalardan herhangi biri hatalı mı (önceki alıştırmadaki tanımlar kullanılarak)?

    (a) rval1 = 3,14159; (b) prval1 = prval2; (c) prval2 = rval1; (d) *prval2 = ival2;

    Alıştırma 3.21

    Aşağıdaki talimatlardaki hataları arayın:

    (a) int değeri = 0; sabit int *pi = 0; sabit int &ri = 0; (b) pi =
    ri =
    pi =

    3.7. bool yazın

    bool türündeki bir nesne iki değerden birini alabilir: true ve false. Örneğin:

    // string başlatma string search_word = get_word(); // bulunan değişkenin başlatılması
    bool bulundu = yanlış; string next_word; while (cin >> next_word)
    eğer (sonraki_kelime == arama_kelime)
    bulunan = doğru;
    // ... // steno: if (bulundu == doğru)
    Eğer bulunursa)
    cout<< "ok, мы нашли слово\n";
    başka türlü<< "нет, наше слово не встретилось.\n";

    bool tamsayı türlerinden biri olmasına rağmen, işaretli, işaretsiz, kısa veya uzun olarak bildirilemez, bu nedenle yukarıdaki tanım hatalıdır:

    // hata kısa bool bulundu = false;

    bool türündeki nesneler dolaylı olarak int türüne dönüştürülür. true değeri 1 olur ve false 0 olur. Örneğin:

    bool bulundu = yanlış; int oluşum_sayısı = 0; while (/* mırıldanma */)
    {
    bulunan = ara(/* bir şey */); // bulunan değer 0 veya 1'e dönüştürülür
    instance_count += bulundu; )

    Aynı şekilde tamsayı türlerinin ve işaretçilerin değerleri bool değerlerine dönüştürülebilir. Bu durumda, 0 yanlış olarak yorumlanır ve diğer her şey doğru olarak yorumlanır:

    // gerçekleşme sayısını döndürür extern int find(const string&); bool bulundu = yanlış; if (bulundu = bul("gül goncası")) // doğru: bulundu == doğru // öğeye bir işaretçi döndürür
    extern int* find(int değeri); if (bulundu = bul(1024)) // doğru: bulundu == doğru

    3.8. Numaralandırmalar

    Çoğu zaman belirli bir kümeden değer alan bir değişken tanımlamak gerekir. Diyelim ki bir dosya üç moddan herhangi birinde açıldı: okuma, yazma ve ekleme için.
    Tabii ki, bu modları belirtmek için üç sabit tanımlanabilir:

    Sabit int girişi = 1; sabit int çıktı = 2; const int ekleme = 3;

    ve şu sabitleri kullanın:

    bool open_file(string file_name, int open_mode); // ...
    open_file("Phoenix_and_the_Crane", ekle);

    Bu çözüm kabul edilebilir, ancak tamamen kabul edilemez çünkü open_file() işlevine iletilen bağımsız değişkenin yalnızca 1, 2 veya 3 olduğunu garanti edemeyiz.
    Numaralandırılmış bir tür kullanmak bu sorunu çözer. Yazdığımızda:

    Enum open_modes(giriş = 1, çıkış, ekleme );

    yeni bir tür open_modes tanımlıyoruz. Bu tür bir nesne için geçerli değerler 1, 2 ve 3 ile sınırlıdır ve belirtilen değerlerin her birinin anımsatıcı bir adı vardır. Bu yeni türün adını, hem bu türden bir nesneyi hem de işlevin biçimsel parametrelerinin türünü tanımlamak için kullanabiliriz:

    Void open_file(string file_name, open_modes om);

    giriş, çıkış ve ekleme numaralandırma elemanları. Numaralandırma öğeleri kümesi, belirli bir türdeki bir nesne için geçerli değer kümesini belirtir. open_modes türünde bir değişken (bizim örneğimizde) bu değerlerden biriyle başlatılır, ayrıca bunlardan herhangi biri atanabilir. Örneğin:

    Open_file("Anka Kuşu ve Turna", ekle);

    Numaralandırma öğelerinden farklı olan bu türden bir değişkene değer atama (veya bunu bir işleve parametre olarak iletme) denemesi derleme hatasına neden olur. Numaralandırma öğelerinden birine karşılık gelen bir tamsayı değeri iletmeye çalışsak bile yine de bir hata alırız:

    // hata: 1 bir numaralandırma üyesi değil open_modes open_file("Jonah", 1);

    open_modes türünde bir değişken tanımlamanın, ona numaralandırma öğelerinden birinin değerini atamanın ve onu işleve bir parametre olarak iletmenin bir yolu vardır:

    open_modes om=giriş; // ...om = ekle; open_file("TailTell", om);

    Ancak bu tür elementlerin isimlerini almak mümkün değildir. Bir çıktı ifadesi yazarsak:

    Cout<< input << " " << om << endl;

    hala alıyoruz:

    Bu sorun, enum öğesinin değerine eşit bir dizine sahip öğenin adını içereceği bir dize dizisi tanımlanarak çözülür. Böyle bir dizi ile şunu yazabiliriz:

    Cout<< open_modes_table[ input ] << " " << open_modes_table[ om ] << endl Будет выведено: input append

    Ayrıca, tüm numaralandırma değerleri üzerinde yineleme yapamazsınız:

    // (open_modes iter = input; iter != append; ++inter) için desteklenmiyor // ...

    Enum anahtar sözcüğü, bir numaralandırmayı tanımlamak için kullanılır ve öğe adları, virgüllerle ayrılmış kaşlı ayraçlar içinde belirtilir. Varsayılan olarak, ilki 0'dır, sonraki 1'dir vb. Atama operatörü bu kuralı değiştirebilir. Bu durumda, açıkça belirtilmemiş bir sonraki her öğe, listede bir önceki öğeden 1 fazla olacaktır. Örneğimizde, girdi için 1 değerini açıkça belirttik ve çıktı ile ekleme 2 ve 3 olacaktır. İşte başka bir örnek:

    // şekil == 0, küre == 1, silindir == 2, çokgen == 3 enum Formlar( paylaşım, spe, silindir, çokgen );

    Aynı numaralandırmanın farklı öğelerine karşılık gelen tamsayı değerlerinin farklı olması gerekmez. Örneğin:

    // point2d == 2, point2w == 3, point3d == 3, point3w == 4 enum Puan ( point2d=2, point2w, point3d=3, point3w=4 );

    Türü enum olan bir nesne tanımlanabilir, ifadelerde kullanılabilir ve bir işleve argüman olarak iletilebilir. Böyle bir nesne, yalnızca numaralandırma öğelerinden birinin değeriyle başlatılır ve yalnızca bu değer, açıkça veya aynı türden başka bir nesnenin değeri olarak ona atanır. Geçerli numaralandırma öğelerine karşılık gelen tamsayı değerleri bile ona atanamaz:

    Void mumble() ( Puanlar pt3d = point3d; // doğru: pt2d == 3 // error: pt3w int yazmak için başlatıldı Puanlar pt3w = 3; // hata: çokgen Puanlarda değil enum pt3w = çokgen; // düzelt : her iki nesne türü de Nokta pt3w = pt3d; )

    Ancak, aritmetik ifadelerde, bir enum otomatik olarak int türüne dönüştürülebilir. Örneğin:

    const int dizi_boyutu = 1024; // doğru: pt2w int'e dönüştürülür
    int yığın_boyutu = dizi_boyutu * pt2w;

    3.9. "dizi" yazın

    Bölüm 2.1'de dizilere değinmiştik. Bir dizi, dizideki öğenin sıra numarası olan dizin tarafından erişilen, aynı türden bir dizi öğedir. Örneğin:

    Intival;

    ival'i int türünde bir değişken olarak tanımlar ve talimat

    int ia[ 10 ];

    on int nesnesi dizisini belirtir. Bu nesnelerin her biri için veya dizi öğeleri, dizini alma işlemi kullanılarak erişilebilir:

    Ival = ia[ 2 ];

    ival değişkenine dizin 2 ile ia dizi öğesinin değerini atar. Benzer şekilde

    Ia[ 7 ] = ival;

    7 dizinindeki öğeyi ival olarak ayarlar.

    Bir dizi tanımı, bir tür belirtici, bir dizi adı ve bir boyuttan oluşur. Boyut, dizi öğelerinin sayısını (en az 1) belirtir ve köşeli parantez içine alınır. Bir dizinin boyutu, derleme zamanında zaten bilinmelidir ve bu nedenle sabit bir ifade olması gerekmese de sabit bir ifade olmalıdır. İşte doğru ve yanlış dizi tanımlarının örnekleri:

    harici int get_size(); // buf_size ve max_files sabitleri
    const int buf_size = 512, max_files = 20;
    intstaff_size = 27; // düzelt: sabit char input_buffer[ buf_size ]; // düzelt: sabit ifade: 20 - 3 char *fileTable[ max_files-3 ]; // hata: sabit bir çift maaş değil[ staff_size ]; // hata: sabit bir ifade değil int test_scores[ get_size() ];

    buf_size ve max_files nesneleri sabittir, dolayısıyla input_buffer ve fileTable dizi tanımları doğrudur. Ancak staff_size bir değişkendir (27 sabitiyle başlatılmış olsa da), bu nedenle maaşlar yasa dışıdır. (Derleyici, maaş dizisi tanımlandığı sırada staff_size değişkeninin değerini bulamıyor.)
    max_files-3 ifadesi derleme zamanında değerlendirilebilir, bu nedenle fileTable dizi tanımı sözdizimsel olarak doğrudur.
    Öğe numaralandırma 0'dan başlar, bu nedenle 10 öğelik bir dizi için doğru dizin aralığı 1 - 10 değil, 0 - 9'dur. Dizinin tüm öğelerini yinelemeye bir örnek:

    int main() ( const int dizi_boyutu = 10; int ia[ dizi_boyutu ]; for (int ix = 0; ix)< array_size; ++ ix)
    ia[ ix ] = ix;
    }

    Bir diziyi tanımlarken, öğelerinin değerlerini virgülle ayırarak kaşlı ayraçlar içinde listeleyerek açıkça başlatabilirsiniz:

    const int dizi_boyutu = 3; int ia[ dizi_boyutu ] = ( 0, 1, 2 );

    Açıkça bir değerler listesi belirtirsek, dizinin boyutunu belirtemeyiz: derleyici, öğelerin sayısını kendisi hesaplar:

    // 3 boyutlu dizi int ia = ( 0, 1, 2 );

    Hem boyut hem de değer listesi açıkça belirtildiğinde, üç seçenek vardır. Değerlerin boyutu ve sayısı eşleştiğinde her şey ortadadır. Değer listesi belirtilen boyuttan daha kısaysa, dizinin geri kalan öğeleri sıfır olarak başlatılır. Listede daha fazla değer varsa, derleyici bir hata mesajı görüntüler:

    // ia ==> ( 0, 1, 2, 0, 0 ) const int dizi_boyutu = 5; int ia[ dizi_boyutu ] = ( 0, 1, 2 );

    Bir karakter dizisi, yalnızca kaşlı ayraçlar içindeki bir karakter değerleri listesiyle değil, aynı zamanda bir dize değişmeziyle de başlatılabilir. Ancak, bu yöntemler arasında bazı farklılıklar vardır. Diyelimki

    Const char cal = ("C", "+", "+" ); const char cal2 = "C++";

    ca1 dizisinin boyutu 3'tür, ca2 dizisinin boyutu 4'tür (dize sabitlerinde sonlandırıcı boş karakter dikkate alınır). Aşağıdaki tanım bir derleme hatasına neden olur:

    // hata: string "Daniel" 7 elemandan oluşuyor const char ch3[ 6 ] = "Daniel";

    Bir diziye başka bir dizinin değeri atanamaz ve bir dizinin başka bir dizi tarafından başlatılması da yasa dışıdır. Ayrıca, bir referans dizisi kullanılmasına izin verilmez. İşte dizilerin doğru ve yanlış kullanımına ilişkin örnekler:

    const int dizi_boyutu = 3; int ix, jx, kx; // doğru: int* int *iar = ( &ix, &jx, &kx ); // hata: referans dizileri geçersiz int &iar = ( ix, jx, kx ); int ana()
    {
    int ia3( dizi_boyutu ]; // doğru
    // hata: yerleşik diziler kopyalanamaz
    ia3 = ia;
    0 dönüşü;
    }

    Bir diziyi diğerine kopyalamak için bunu her eleman için ayrı ayrı yapmanız gerekir:

    const int dizi_boyutu = 7; int ia1 = ( 0, 1, 2, 3, 4, 5, 6 ); int ana() (
    int ia3[dizi_boyutu]; için (int ix = 0; ix< array_size; ++ix)
    ia2[ ix ] = ia1[ ix ]; 0 dönüşü;
    }

    Bir dizi dizini, bir tamsayı türü sonucu veren herhangi bir ifade olabilir. Örneğin:

    int bazıDeğer, get_index(); ia2[ get_index() ] = bazıDeğer;

    C++ dilinin ne derleme zamanında ne de çalışma zamanında dizi indeksleri üzerinde kontrol sağlamadığını vurguluyoruz. Programcının kendisi, dizinin dizinin sınırlarının ötesine geçmemesini sağlamalıdır. İndeks hataları oldukça yaygındır. Ne yazık ki, derleyen ve hatta çalıştıran, ancak yine de er ya da geç bir çökmeye yol açan ölümcül hatalar içeren program örnekleri bulmak o kadar da zor değil.

    Alıştırma 3.22

    Aşağıdaki dizi tanımlarından hangisinde hata var? Açıklamak.

    (a) int ia[buf_size]; (d) int ia[ 2 * 7 - 14 ] (b) int ia[ get_size() ]; (e) char st[11] = "temel"; (c) intia[4 * 7 - 14];

    Alıştırma 3.23

    Aşağıdaki kod parçacığı, her dizi öğesini bir dizin değeriyle başlatmalıdır. Yaptığınız hataları bulun:

    int main() ( const int dizi_boyutu = 10; int ia[ dizi_boyutu ]; for (int ix = 1; ix)<= array_size; ++ix)
    ia[ia] = ix; // ...
    }

    3.9.1. çok boyutlu diziler

    C++'da, her boyutun sağ sınırı ayrı köşeli parantezler içinde bildirilmesi gereken çok boyutlu diziler kullanmak mümkündür. İşte iki boyutlu bir dizinin tanımı:

    Int ia[ 4 ][ 3 ];

    İlk değer (4) satır sayısını, ikinci değer (3) sütun sayısını belirtir. ia nesnesi, her biri üç öğe içeren dört dizeden oluşan bir dizi olarak tanımlanır. Çok boyutlu diziler ayrıca başlatılabilir:

    Intia[ 4 ][ 3 ] = ( ( 0, 1, 2 ), ( 3, 4, 5 ), ( 6, 7, 8 ), ( 9, 10, 11 ) );

    Değer listesini satırlara bölen iç kaşlı ayraçlar isteğe bağlıdır ve kural olarak kodun okunabilirliği için kullanılır. Aşağıdaki başlatma, daha az net olmasına rağmen önceki örnekle tam olarak aynıdır:

    int ia = ( 0,1,2,3,4,5,6,7,8,9,10,11 );

    Aşağıdaki tanım, her satırın yalnızca ilk öğelerini başlatır. Kalan öğeler sıfır olacaktır:

    Intia[ 4 ][ 3 ] = ( (0), (3), (6), (9) );

    İçteki kıvrık parantezleri atlarsanız, sonuç tamamen farklıdır. İlk satırın üç öğesi ve ikincinin ilk öğesi belirtilen değeri alacak ve geri kalanı dolaylı olarak 0 olarak başlatılacaktır.

    Intia[ 4 ][ 3 ] = ( 0, 3, 6, 9 );

    Çok boyutlu bir dizinin öğelerine erişirken, her boyut için indeksleri kullanmalısınız (bunlar köşeli parantez içindedir). İç içe döngüler kullanılarak iki boyutlu bir dizinin başlatılması şu şekilde görünür:

    int main() ( const int satırBoyutu = 4; const int colBoyutu = 3; int ia[ satırBoyutu ][ colBoyutu ]; for (int = 0; i< rowSize; ++i)
    için (int j = 0; j< colSize; ++j)
    ia[ ben ][ j ] = ben + jj;
    }

    Tasarım

    ben[ 1, 2 ]

    C++ sözdizimi açısından geçerlidir, ancak deneyimsiz bir programcının bekleyeceği anlamına gelmez. Bu kesinlikle iki boyutlu bir 1'e 2 dizisinin bildirimi değildir. Köşeli parantez içindeki bir toplama, nihai değer 2 ile sonuçlanacak ifadelerin virgülle ayrılmış bir listesidir (Bölüm 4.2'deki "virgül" operatörüne bakın) ). Bu nedenle, ia bildirimi ia'ya eşdeğerdir. Bu hata yapmak için başka bir fırsat.

    3.9.2. Diziler ve işaretçiler arasındaki ilişki

    Bir dizi tanımımız varsa:

    Intia = ( 0, 1, 1, 2, 3, 5, 8, 13, 21 );

    o zaman programda adının basit bir şekilde belirtilmesi ne anlama geliyor?

    Bir programda bir dizi tanımlayıcısı kullanmak, ilk öğesinin adresini belirtmekle eşdeğerdir:

    Benzer şekilde, bir dizinin ilk öğesinin değerine erişmenin iki yolu vardır:

    // her iki ifade de ilk öğeyi döndürür *ia; yani;

    Dizinin ikinci elemanının adresini almak için şunu yazardık:

    Daha önce de belirttiğimiz gibi, ifade

    ayrıca ikinci dizi öğesinin adresini verir. Buna göre, değeri bize aşağıdaki iki yöntemle verilir:

    *(ya+1); yani;

    İfadelerdeki farka dikkat edin:

    *ia+1 ve *(ia+1);

    başvuru işlemi daha yüksek bir öncelik toplama işleminden daha fazla (operatör önceliği hakkında daha fazla bilgi için bkz. Bölüm 4.13). Bu nedenle, ilk ifade önce ia değişkenini başvurudan kaldırır ve dizinin ilk elemanını alır, sonra ona 1 ekler, ikinci ifade ikinci elemanın değerini verir.

    Önceki bölümde yaptığımız gibi bir dizin kullanarak veya işaretçiler kullanarak bir diziyi yineleyebilirsiniz. Örneğin:

    #katmak int main() ( int ia = ( 0, 1, 1, 2, 3, 5, 8, 13, 21 ); int *pbegin = ia; int *pend = ia + 9; while (pbegin != pend) ( cout<< *pbegin <<; ++pbegin; } }

    pbegin işaretçisi, dizinin ilk öğesinin adresiyle başlatılır. Döngü boyunca her yineleme, bu işaretçiyi 1 artırır, bu da bir sonraki öğeye taşındığı anlamına gelir. Nerede kalınacağını nasıl bilebilirim? Örneğimizde, ikinci bir askı işaretçisi tanımladık ve onu ia dizisinin son elemanından sonraki adresle başlattık. pbegin'in değeri pend'e eşit olur olmaz dizinin bittiğini anlarız. Bu programı, dizinin başı ve sonu, herhangi bir boyuttaki diziyi yazdırabilen bazı genel işlevlere parametre olarak iletilecek şekilde yeniden yazalım:

    #katmak void ia_print(int *pbegin, int *pend) (
    while (pbegin != beklemede) (
    cout<< *pbegin << " ";
    ++pbaşla;
    }
    ) int ana()
    {
    int ia = ( 0, 1, 1, 2, 3, 5, 8, 13, 21 );
    ia_print(ia, ia + 9);
    }

    Fonksiyonumuz daha evrensel hale geldi, ancak sadece int dizileriyle çalışabiliyor. Bu sınırlamayı kaldırmanın da bir yolu vardır: verilen işlevi bir şablona dönüştürmek (şablonlar kısaca bölüm 2.5'te tanıtılmıştır):

    #katmak şablon void print(elemType *pbegin, elemType *pend) ( while (pbegin != pend) ( cout<< *pbegin << " "; ++pbegin; } }

    Artık herhangi bir türdeki dizileri yazdırmak için print() işlevimizi çağırabiliriz:

    int main() ( int ia = ( 0, 1, 1, 2, 3, 5, 8, 13, 21 ); double da = ( 3.14, 6.28, 12.56, 25.12 ); string sa = ("domuz yavrusu", " eyore", "pooh"); print(ia, ia+9);
    yazdır(da,da+4);
    yazdır(sa, sa+3);
    }

    Biz yazdık genelleştirilmiş işlev. Standart kitaplık, bu şekilde uygulanan bir dizi jenerik algoritma sağlar (bundan Bölüm 3.4'te daha önce bahsetmiştik). Bu tür işlevlerin parametreleri, belirli eylemleri gerçekleştirdikleri dizinin başlangıcına ve sonuna işaretçilerdir. Örneğin, genelleştirilmiş sıralama algoritmasına yapılan çağrılar şu şekilde görünür:

    #katmak int main() ( int ia = ( 107, 28, 3, 47, 104, 76 ); string sa = ("domuz yavrusu", "eeyore", "pooh" ); sort(ia, ia+6);
    sıralama(sa, sa+3);
    };

    (Genelleştirilmiş algoritmaları Bölüm 12'de ayrıntılı olarak tartışacağız; bunların kullanım örnekleri Ek'te verilecektir.)
    C++ Standart Kitaplığı, kapsayıcıların ve işaretçilerin kullanımını kapsayan bir dizi sınıf içerir. (Bunu bölüm 2.8'de ele aldık.) Bir sonraki bölümde, bir dizinin nesne yönelimli uygulaması olan standart kap tipi vektöre bakacağız.

    3.10. vektör sınıfı

    Vektör sınıfını kullanmak (bkz. bölüm 2.8), yerleşik dizileri kullanmaya bir alternatiftir. Bu sınıf daha birçok özellik sağlar, bu nedenle kullanımı tercih edilir. Ancak, yerleşik türdeki dizilerden vazgeçilemeyeceği durumlar vardır. Bu durumlardan biri, Bölüm 7.8'de ele alacağımız programa iletilen komut satırı parametrelerinin işlenmesidir. Dize sınıfı gibi vektör sınıfı da C++ Standart Kitaplığının bir parçasıdır.
    Bir vektör kullanmak için bir başlık dosyası eklemeniz gerekir:

    #katmak

    Vektör kullanmanın tamamen farklı iki yaklaşımı vardır, hadi bunlara dizi deyimi ve STL deyimi diyelim. İlk durumda, vektör sınıfındaki bir nesne, yerleşik türdeki bir diziyle aynı şekilde kullanılır. Belirli bir boyuttaki bir vektör tanımlanır:

    Vektör< int >ivec(10);

    bu, yerleşik türde bir dizi tanımlamakla aynıdır:

    int ia[ 10 ];

    Vektörün bireysel öğelerine erişmek için indeks alma işlemi kullanılır:

    Void simp1e_examp1e() ( const int e1em_size = 10; vektör< int >ivec(e1em_size); int ia[ e1em_size ]; için (int ix = 0; ix< e1em_size; ++ix)
    ia[ ix ] = ivec[ ix ]; // ...
    }

    Size() işlevini kullanarak bir vektörün boyutunu bulabilir ve empty() işlevini kullanarak vektörün boş olup olmadığını kontrol edebiliriz. Örneğin:

    Geçersiz print_vector(vektör ivec) ( if (ivec.empty()) döndürür; for (int ix=0; ix< ivec.size(); ++ix)
    cout<< ivec[ ix ] << " ";
    }

    Vektör öğeleri, varsayılan değerlerle başlatılır. Sayısal türler ve işaretçiler için bu değer 0'dır. Sınıf nesneleri öğeler olarak işlev görürse, onlar için başlatıcı varsayılan kurucu tarafından belirtilir (bkz. bölüm 2.3). Bununla birlikte, başlatıcı, aşağıdaki form kullanılarak da açıkça belirtilebilir:

    Vektör< int >ivec(10, -1);

    Vektörün on öğesinin tümü -1'e eşit olacaktır.
    Yerleşik türdeki bir dizi, bir listeyle açıkça başlatılabilir:

    int ia[ 6 ] = ( -2, -1, 0, 1, 2, 1024 );

    Vektör sınıfındaki bir nesne için benzer bir eylem mümkün değildir. Ancak, böyle bir nesne yerleşik türde bir dizi ile başlatılabilir:

    // ia'nın 6 elemanı ivec vektörüne kopyalanıyor< int >ivec(ya, ia+6);

    ivec vektör yapıcısına iki işaretçi iletilir - ia dizisinin başlangıcına ve sonuncuyu takip eden öğeye işaretçi. İlk değerlerin bir listesi olarak, tüm dizinin değil, aralığının bir kısmının belirtilmesine izin verilir:

    // 3 eleman kopyalanır: ia, ia, ia vektörü< int >ivec(&ia[ 2 ], &ia[ 5 ]);

    Bir vektör ile yerleşik türdeki bir dizi arasındaki diğer bir fark, vektör türündeki bir nesneyi diğeriyle başlatma ve nesneleri kopyalamak için atama işlecini kullanma yeteneğidir. Örneğin:

    Vektör< string >svec; void init_and_assign() ( // bir vektör başka bir vektör tarafından başlatılır< string >kullanıcı_adları(svec); // ... // bir vektör diğerine kopyalanır
    svec = kullanıcı_adları;
    }

    STL deyiminden bahsetmişken, bir vektör kullanmaya çok farklı bir yaklaşımı kastediyoruz. Doğru boyutu hemen ayarlamak yerine boş bir vektör tanımlarız:

    Vektör< string >metin;

    Sonra çeşitli işlevleri kullanarak ona öğeler ekleriz. Örneğin, push_back() işlevi, bir vektörün sonuna bir öğe ekler. İşte standart girdiden bir satır dizisini okuyan ve bunları bir vektöre ekleyen bir kod parçası:

    telli kelime; while (cin >> kelime) ( text.push_back(kelime); // ... )

    Dizini alma işlemini bir vektörün öğeleri üzerinde yineleme yapmak için kullanabilsek de:

    Cout<< "считаны слова: \n"; for (int ix =0; ix < text.size(); ++ix) cout << text[ ix ] << " "; cout << endl;

    bu deyim içinde daha tipik olan, yineleyicileri kullanmak olacaktır:

    Cout<< "считаны слова: \n"; for (vector::iterator it = text.begin(); o != text.end(); ++bu) cout<< *it << " "; cout << endl;

    Yineleyici, aslında bir dizinin bir öğesine işaretçi olan standart bir kitaplık sınıfıdır.
    İfade

    yineleyiciyi kaldırır ve vektör öğesinin kendisini verir. Talimat

    İşaretçiyi bir sonraki öğeye ilerletir. Bu iki yaklaşımı karıştırmaya gerek yok. Boş bir vektör tanımlarken STL deyimini takip etmek:

    Vektör ivec;

    Şunları yazmak bir hata olur:

    Henüz ivec vektörünün tek bir elemanına sahip değiliz; eleman sayısı size() işlevi kullanılarak bulunur.

    Bunun tersi hatayı da yapabilirsiniz. Belirli bir boyutta bir vektör tanımladıysak, örneğin:

    Vektör ia(10);

    Daha sonra eleman eklemek, mevcut olanlara yeni elemanlar ekleyerek boyutunu büyütür. Bu bariz görünse de, yine de acemi bir programcı şunları yazabilir:

    Sabit int boyutu = 7; int ia[ boyut ] = ( 0, 1, 1, 2, 3, 5, 8 ); vektör< int >ivec(boyut); için (int ix = 0; ix< size; ++ix) ivec.push_back(ia[ ix ]);

    Bu, ivec vektörünün ia öğelerinin değerleriyle başlatılması anlamına geliyordu, bunun yerine 14 boyutunda bir ivec vektörümüz oldu.
    STL deyimini takiben, bir vektörün öğelerini yalnızca ekleyemez, aynı zamanda kaldırabilirsiniz. (Bütün bunları ayrıntılı olarak ve örneklerle 6. Bölümde ele alacağız.)

    Alıştırma 3.24

    Aşağıdaki tanımlamalarda hata var mı?
    int ia[ 7 ] = ( 0, 1, 1, 2, 3, 5, 8 );

    (a) vektör< vector< int >>ivec;
    (b) vektör< int >ivec = ( 0, 1, 1, 2, 3, 5, 8 );
    (c) vektör< int >ivec(ya, ia+7);
    (d) vektör< string >svec = ivec;
    (e)vektör< string >svec(10, string("boş"));

    Alıştırma 3.25

    Aşağıdaki işlevi uygulayın:
    bool is_equal(const int*ia, int ia_size,
    sabit vektör &ivec);
    is_equal() işlevi, iki kapsayıcı öğesini öğe bazında karşılaştırır. Farklı büyüklükteki kaplar söz konusu olduğunda, daha uzun olanın "kuyruğu" dikkate alınmaz. Açıktır ki, karşılaştırılan tüm öğeler eşitse, işlev doğru, en az biri farklıysa yanlış döndürür. Öğeler üzerinde yineleme yapmak için bir yineleyici kullanın. is_equal() işlevini çağıran bir main() işlevi yazın.

    3.11. karmaşık sınıf

    Karmaşık sayılar sınıfı, standart kitaplıktan başka bir sınıftır. Her zamanki gibi, kullanmak için başlık dosyasını eklemeniz gerekir:

    #katmak

    Karmaşık bir sayının iki kısmı vardır - gerçek ve sanal. Sanal kısım, negatif bir sayının kareköküdür. Bir karmaşık sayı genellikle şu şekilde yazılır:

    burada 2 gerçek kısım ve 3i hayali kısımdır. Karmaşık nesne tanımlarına örnekler:

    // saf hayali sayı: 0 + 7-i kompleksi< double >safi(0, 7); // hayali kısım 0:3 + Oi kompleksidir< float >gerçek1_num(3); // hem gerçek hem de hayali kısımlar 0: 0 + 0-i kompleksidir< long double >sıfır; // bir karmaşık sayının başka bir karmaşık tarafından başlatılması< double >safi2(saf);

    Karmaşık, vektör gibi bir şablon olduğundan, onu yukarıdaki örneklerde olduğu gibi float, double ve long double ile başlatabiliriz. Bir dizi karmaşık öğeyi de tanımlayabilirsiniz:

    karmaşık< double >eşlenik[ 2 ] = ( karmaşık< double >(2, 3), karmaşık< double >(2, -3) };

    karmaşık< double >*ptr = karmaşık< double >&ref = *ptr;

    3.12. typedef yönergesi

    typedef yönergesi, yerleşik veya kullanıcı tanımlı bir veri türü için bir eşanlamlı belirtmenizi sağlar. Örneğin:

    Typedef çifte ücret; typedef vektörü vec_int; typedef vec_int test_scores; typedef bool in_attendance; typedef int *Pint;

    typedef yönergesiyle tanımlanan adlar, tür belirticilerle aynı şekilde kullanılabilir:

    // çift saatlik, haftalık; saatlik, haftalık ücretler; // vektör vecl(10);
    vec_int vecl(10); // vektör test0(c1ass_size); const int c1ass_size = 34; test_scores test0(c1ass_size); // vektör< bool >katılım; vektör< in_attendance >katılım(c1ass_size); // int *tablo[ 10 ]; Bira bardağı tablosu [ 10 ];

    Bu yönerge, typedef anahtar sözcüğüyle başlar, ardından bir tür belirtici gelir ve belirtilen tür için takma ad haline gelen bir tanımlayıcıyla biter.
    typedef yönergesi ile tanımlanan adlar ne için kullanılır? Veri türleri için anımsatıcı adlar kullanarak programınızın daha kolay anlaşılmasını sağlayabilirsiniz. Ayrıca, bir sınıfın işlevlerine ve üye işlevlerine işaretçiler bildirmek için karmaşık, başka türlü anlaşılması zor olan karmaşık türler için (bkz. Bölüm 3.14'teki bir örneğe bakın) bu tür adları kullanmak da adettendir (bkz.
    Aşağıda hemen hemen herkesin yanlış cevap verdiği bir soruya örnek verilmiştir. Hata, typedef yönergesinin basit bir metinsel makro ikamesi olarak yanlış anlaşılmasından kaynaklanır. Tanım verilir:

    Typedef char *cstring;

    Aşağıdaki bildirimdeki cstr değişkeninin türü nedir:

    extern const cstring cstr;

    Açık görünen cevap şudur:

    Sabit karakter *cstr

    Ancak bu doğru değil. const belirleyicisi cstr'ye atıfta bulunur, bu nedenle doğru cevap, char için bir const işaretçisidir:

    Char *const cstr;

    3.13. geçici belirtici

    Bir nesne, sistem saati tarafından güncellenen bir değişken gibi, derleyici fark etmeden değeri değiştirilebiliyorsa uçucu (kararsız, eşzamansız olarak değişken) olarak tanımlanır. Bu belirtici, derleyiciye kodu verilen nesneyle çalışacak şekilde optimize etmemesini söyler.
    Uçucu belirtici, const belirtici gibi kullanılır:

    Uçucu int disp1ay_register; uçucu Görev *curr_task; uçucu int ixa[ max_size ]; uçucu Ekran bitmap_buf;

    display_register, int türünde kararsız bir nesnedir. curr_task, kararsız bir Görev nesnesine işaretçidir. ixa kararsız bir tamsayı dizisidir ve böyle bir dizinin her elemanı kararsız kabul edilir. bitmap_buf, Screen sınıfının geçici bir nesnesidir, veri üyelerinin her biri aynı zamanda geçici olarak kabul edilir.
    volatile tanımlayıcısını kullanmanın tek amacı, derleyiciye verilen bir nesnenin değerini kimin ve nasıl değiştirebileceğini belirleyemeyeceğini söylemektir. Bu nedenle, derleyici bu nesneyi kullanan kodu optimize etmemelidir.

    3.14. çift ​​sınıfı

    C++ standart kütüphanesinin pair sınıfı, aralarında herhangi bir anlamsal ilişki varsa, bir nesne ile bir değer çifti tanımlamamızı sağlar. Bu değerler aynı veya farklı tipte olabilir. Bu sınıfı kullanmak için bir başlık dosyası eklemeniz gerekir:

    #katmak

    Örneğin, talimat

    Çift< string, string >yazar("James", "Joyce");

    iki dize değerinden oluşan çift türünde bir yazar nesnesi oluşturur.
    Bir çiftin ayrı parçaları, birinci ve ikinci üyeler kullanılarak alınabilir:

    String firstBook; if (Joyce.first == "James" &&
    Joyce.second == "Joyce")
    firstBook = "Stephen Kahraman";

    Bu sınıftan aynı türden birkaç nesne tanımlamanız gerekirse, typedef yönergesini kullanmak uygundur:

    Yazı tipi çifti< string, string >Yazarlar; Yazarlar proust("marcel", "proust"); Yazarlar joyce("James", "Joyce"); Yazarlar musil("robert", "musi1");

    İşte bir çift kullanmanın başka bir örneği. İlk değer, bir nesnenin adını içerir, ikincisi, bu nesneye karşılık gelen tablo öğesinin bir işaretçisidir.

    Sınıf Giriş Yuvası; harici Giriş Yuvası* 1ook_up(dize); typedef çifti< string, EntrySlot* >Sembol Girişi; SymbolEntry current_entry("yazar", 1ook_up("yazar"));
    // ... if (EntrySlot *it = 1ook_up("düzenleyici")) (
    current_entry.first = "düzenleyici";
    current_entry.second = o;
    }

    (Kapsayıcı türleri ve jenerik algoritmalar üzerine Bölüm 6'daki ikili sınıf tartışmasına Bölüm 12'de geri döneceğiz.)

    3.15. Sınıf türleri

    Sınıf mekanizması, yeni veri türleri oluşturmanıza olanak tanır; yukarıda tartışılan dizi, vektör, karmaşık ve çift türlerini tanıttı. Bölüm 2'de Array sınıfının uygulanmasını örnek olarak kullanarak nesne ve nesne yönelimli yaklaşımı destekleyen kavramlar ve mekanizmalardan bahsettik. Burada, nesne yaklaşımına dayalı olarak, uygulaması özellikle operatör aşırı yüklemesini anlamaya yardımcı olacak basit bir String sınıfı oluşturacağız - bundan 2.3. bölümde bahsetmiştik. (Sınıflar 13, 14 ve 15. bölümlerde ayrıntılı olarak ele alınmıştır). Daha ilginç örnekler verebilmek için sınıfın kısa bir tanımını yaptık. C++ öğrenmeye yeni başlayan okuyucu, bu bölümü atlayabilir ve sonraki bölümlerde sınıfların daha sistematik bir tanımını bekleyebilir.)
    String sınıfımız, bir String sınıfı nesnesi, bir dize sabit değeri ve yerleşik bir dize türü ile başlatmanın yanı sıra bu türlerin değerlerini ona atama işlemini desteklemelidir. Bunun için sınıf oluşturucuları ve aşırı yüklenmiş bir atama operatörü kullanıyoruz. Bir String'in tek tek karakterlerine erişim, aşırı yüklenmiş bir dizin alma işleci olarak uygulanacaktır. Ek olarak şunlara ihtiyacımız var: dizenin uzunluğu hakkında bilgi almak için size() işlevi; String türündeki nesneleri ve bir String nesnesini yerleşik türdeki bir dizeyle karşılaştırma işlemi; yanı sıra nesnemizin I/O işlemleri. Son olarak, dizimizin dahili temsiline yerleşik bir tür dizesi olarak erişme yeteneğini uygularız.
    Bir sınıf tanımı, class anahtar kelimesiyle başlar ve ardından bir tanımlayıcı, sınıfın adı veya türü gelir. Genel olarak, bir sınıf, önünde public (açık) ve private (kapalı) sözcüklerinin bulunduğu bölümlerden oluşur. Açık bir bölüm tipik olarak sınıf tarafından desteklenen, sınıfın yöntemleri veya üye işlevleri olarak adlandırılan bir dizi işlem içerir. Bu üye işlevler, sınıfın genel arabirimini, başka bir deyişle, o sınıfın nesneleri üzerinde gerçekleştirilebilecek eylemler kümesini tanımlar. Özel bir bölüm genellikle dahili bir uygulama sağlayan veri üyelerini içerir. Bizim durumumuzda, dahili üyeler, char için bir işaretçi olan _string ve ayrıca int türünde _size içerir. _size, dizenin uzunluğu hakkında bilgi depolar ve _string, dinamik olarak ayrılmış bir karakter dizisidir. İşte sınıf tanımının nasıl göründüğü:

    #katmak sınıf Dize; istream& operatörü>>(istream&, String&);
    akış ve operatör<<(ostream&, const String&); class String {
    halk:
    // yapıcılar kümesi
    // otomatik başlatma için
    // Stringstrl; // sicim()
    // String str2("sabit değer"); // String(const char*);
    // Dizi str3(str2); // Dizgi(sabit Dizge&); sicim();
    string(const char*);
    Dizgi(sabit Dizge&); // yok edici
    ~dizi(); // atama işleçleri
    // strl = str2
    // str3 = "bir dizge hazır değeri" String& operatör=(const String&);
    String& operatörü=(const char*); // eşitlik operatörleri
    // strl == str2;
    // str3 == "bir dizge hazır değeri"; bool operatörü==(const String&);
    bool operatörü==(const char*); // erişim operatörünü dizine göre aşırı yükleme
    // strl[ 0 ] = str2[ 0 ]; char& operatörü(int); // sınıf üyelerine erişim
    int size() ( dönüş _size; )
    char* c_str() ( dönüş _string; ) özel:
    int_size;
    char*_string;
    }

    String sınıfının üç kurucusu vardır. Bölüm 2.3'te tartışıldığı gibi, aşırı yükleme mekanizması, hepsi parametrelerinin sayısı ve/veya türü bakımından farklı olduğu sürece, aynı ada sahip birden çok işlev uygulamasının tanımlanmasına izin verir. İlk kurucu

    Açık bir başlangıç ​​değeri gerektirmediği için varsayılan kurucudur. Yazdığımızda:

    str1 için böyle bir yapıcı çağrılır.
    Kalan iki yapıcının her birinin bir parametresi vardır. Evet, için

    String str2("karakter dizisi");

    Yapıcı denir

    string(const char*);

    dizi str3(str2);

    Yapıcı

    Dizgi(sabit Dizge&);

    Çağrılan yapıcının türü, gerçek bağımsız değişkenin türüne göre belirlenir. Son oluşturucu String(const String&), bir nesneyi başka bir nesnenin kopyasıyla başlattığı için kopya oluşturucu olarak adlandırılır.
    Eğer yazarsanız:

    stringstr4(1024);

    int tipi parametresi olan bir oluşturucu olmadığı için bu bir derleme hatasına neden olur.
    Aşırı yüklenmiş bir işleç bildirimi aşağıdaki biçime sahiptir:

    dönüş_türü işleci op(parametre_listesi);

    Operatörün bir anahtar kelime olduğu ve op'un önceden tanımlanmış operatörlerden biri olduğu durumlarda: +, =, ==, vb. (Sözdiziminin kesin tanımı için Bölüm 15'e bakın.) Aşırı yüklenmiş alt simge operatörünün bildirimi şöyledir:

    Char& operatörü(int);

    Bu işleç, int türünde tek bir parametreye sahiptir ve char'a bir başvuru döndürür. Bireysel örneklemelerin parametre listeleri farklıysa, aşırı yüklenmiş bir işlecin kendisi aşırı yüklenebilir. String sınıfımız için ikişer farklı atama ve eşitlik operatörü oluşturacağız.
    Bir üye işlevi çağırmak için üye erişim işleçlerini nokta (.) veya oku (->) kullanın. Diyelim ki String türünde nesne bildirimlerimiz var:

    Dize nesnesi ("Danny");
    String *ptr = new String("Anna");
    dizi dizisi;
    Bu nesneler için size() işlev çağrısı şöyle görünür:
    vektör boyutlar(3);

    // nesneler için üye erişimi (.); // nesneler 5 boyuttadır[ 0 ] = object.size(); // işaretçiler için üye erişimi (->)
    // ptr'nin boyutu 4'tür
    boyutlar[ 1 ] = ptr->size(); // üye erişimi (.)
    // dizinin boyutu 0
    boyutlar[ 2 ] = dizi.size();

    Sırasıyla 5, 4 ve 0 döndürür.
    Aşırı yüklenmiş işleçler, bir nesneye normal işleçlerle aynı şekilde uygulanır:

    String namel("Yadie"); Stringname2("Yodie"); // bool operatörü==(const String&)
    eğer (isim == isim2)
    geri dönmek;
    başka
    // String& operatör=(const String&)
    isim=isim2;

    Bir üye işlev bildirimi, bir sınıf tanımının içinde olmalıdır ve bir işlev tanımı, sınıf tanımının içinde veya dışında olabilir. (Hem size() hem de c_str() bir sınıf içinde tanımlanır.) Bir işlev bir sınıfın dışında tanımlanırsa, diğer şeylerin yanı sıra hangi sınıfa ait olduğunu belirtmemiz gerekir. Bu durumda, işlev tanımı bir kaynak dosyaya, örneğin String.C'ye yerleştirilir ve sınıfın tanımı, kaynak dosyaya dahil edilmesi gereken bir başlık dosyasına (bizim örneğimizde String.h) yerleştirilir:

    // kaynak dosyanın içeriği: String.C // String sınıfının tanımı dahil
    #include "String.h" // strcmp() işlev tanımını içerir
    #katmak
    bool // dönüş türü
    String:: // fonksiyonun ait olduğu sınıf
    operatör== // fonksiyon adı: eşitlik operatörü
    (const String &rhs) // parametre listesi
    {
    eğer (_size != rhs._size)
    yanlış dönüş;
    dönüş strcmp(_stringq, rhs._string)?
    yanlış doğru;
    }

    strcmp()'in standart bir C kitaplığı işlevi olduğunu hatırlayın.Dizgiler eşitse 0, değilse sıfır olmayan bir değer döndürerek iki yerleşik tür dizesini karşılaştırır. Koşullu operatör (?:) soru işaretinden önceki değeri test eder. Doğruysa, iki nokta üst üstenin solundaki ifadenin değeri döndürülür, aksi takdirde iki nokta üst üstenin sağındadır. Örneğimizde, strcmp() null olmayan bir değer döndürürse ifadenin değeri false, null ise true olur. (Koşullu işleç, Bölüm 4.7'de ele alınmıştır.)
    Karşılaştırma işlemi oldukça sık kullanılır, onu uygulayan işlevin küçük olduğu ortaya çıktı, bu nedenle bu işlevi yerleşik (satır içi) olarak bildirmek yararlıdır. Derleyici, işlevi çağırmak yerine metnini değiştirir, böylece böyle bir çağrı için zaman harcanmaz. (Yerleşik işlevler Bölüm 7.6'da ele alınmıştır.) Bir sınıf içinde tanımlanan üye işlev, varsayılan olarak yerleşiktir. Sınıfın dışında tanımlanmışsa satır içi olarak bildirmek için inline anahtar sözcüğünü kullanmanız gerekir:

    Satır içi bool String::operator==(const String &rhs) ( // aynen )

    Satır içi işlevin tanımı, sınıf tanımını içeren bir başlık dosyasında olmalıdır. == işlecini yerleşik bir işleç olarak yeniden tanımlayarak, işlev metninin kendisini String.C dosyasından String.h dosyasına taşımamız gerekir.
    Aşağıda, bir String nesnesini yerleşik bir dizge türüyle karşılaştırma işleminin bir uygulaması yer almaktadır:

    Satır içi bool String::operator==(const char *s) ( dönüş strcmp(_string, s) ? false: true; )

    Yapıcının adı, sınıfın adıyla aynıdır. Bir değer döndürdüğü düşünülmediğinden, tanımında veya gövdesinde bir dönüş değeri belirtmeniz gerekmez. Birden fazla kurucu olabilir. Diğer tüm işlevler gibi satır içi olarak bildirilebilirler.

    #katmak // varsayılan yapıcı satır içi String::String()
    {
    _size=0;
    _string=0;
    ) satır içi String::String(const char *str) ( if (! str) ( _size = 0; _string = 0; ) else ( _size = str1en(str); strcpy(_string, str); ) // yapıcıyı kopyala
    satır içi String::String(const String &rhs)
    {
    boyut = rhs._size;
    eğer(!rhs._string)
    _string=0;
    başka(
    _string = yeni karakter[_size + 1];
    } }

    Yeni operatörle dinamik olarak bellek tahsis ettiğimiz için, artık String nesnesine ihtiyacımız olmadığında onu silmek için bir çağrı ile boşaltmamız gerekir. Başka bir özel üye işlevi, bu nesnenin varlığı sona erdiğinde bir nesne için otomatik olarak çağrılan yıkıcı işlevi bu amaca hizmet eder. (Nesne ömrü ile ilgili Bölüm 7'ye bakın.) Bir yıkıcının adı, yaklaşık işareti (~) ve sınıfın adından oluşur. İşte String sınıfının yıkıcısının tanımı. Yapıcıda ayrılan belleği boşaltmak için silme işlemini burada çağırıyoruz:

    Satır İçi Dize: :~String() ( _string'i sil; )

    Her iki aşırı yüklenmiş atama işleci de this özel anahtar sözcüğünü kullanır.
    Yazdığımızda:

    String namel("orville"), name2("wilbur");
    isim = "Orville Wright";
    bu, atama işleminin işlev gövdesi içindeki ad1 nesnesini adresleyen bir işaretçidir.
    bu her zaman işlevin çağrıldığı sınıf nesnesine işaret eder. Eğer
    ptr->size();
    nesne[1024];

    Sonra size() içinde bunun değeri, ptr'de saklanan adres olacaktır. Get index işleminin içinde bu, obj'nin adresini içerir. Bunu referanstan kaldırarak (*this kullanarak), nesnenin kendisini elde ederiz. (Bu işaretçi, Bölüm 13.4'te ayrıntılı olarak açıklanmaktadır.)

    Inline String& String::operator=(const char *s) ( if (! s) ( _size = 0; delete _string; _string = 0; ) else ( _size = str1en(s); _string'i sil; _string = new char[ _size + 1 ]; strcpy(_string, s); ) dönüş *bu; )

    Atama işlemini uygularken, sıklıkla bir hata yapılır: kopyalanan nesnenin, kopyanın yapıldığı nesneyle aynı olup olmadığını kontrol etmeyi unuturlar. Bu kontrolü aynı this işaretçisini kullanarak gerçekleştireceğiz:

    Inline String& String::operator=(const String &rhs) ( // ifadede // namel = *pointer_to_string // bu name1, // rhs - *pointer_to_string.if (this != &rhs) (

    Aynı türden bir nesneyi bir String nesnesine atamak için yapılan işlemin tam metni:

    Inline String& String::operator=(const String &rhs) ( if (this != &rhs) ( _string'i sil; _size = rhs._size; if (! rhs._string)
    _string=0;
    başka(
    _string = yeni karakter[_size + 1];
    strcpy(_string, rhs._string);
    }
    }
    *bunu geri ver;
    }

    Alt simge işlemi, Bölüm 2.3'te oluşturduğumuz Dizi uygulamasıyla hemen hemen aynıdır:

    #katmak satır içi karakter&
    String::operatör (int öğesi)
    {
    iddia(öğe >= 0 && öğe< _size);
    _string[öğe] döndürür;
    }

    Girdi ve çıktı işleçleri, sınıf üyeleri olarak değil, ayrı işlevler olarak uygulanır. (Bunun nedenlerini Bölüm 15.2'de tartışacağız. Bölüm 20.4 ve 20.5, iostream kitaplığının giriş ve çıkış operatörlerinin aşırı yüklenmesini tartışacaktır.) Giriş operatörümüz 4095 karakterden fazlasını okuyamaz. setw() önceden tanımlanmış bir manipülatördür, giriş akışından verilen karakter sayısı eksi 1'i okur, böylece dahili inBuf tamponumuzun taşmamasını sağlar. (Bölüm 20 setw() manipülatörünü ayrıntılı olarak ele almaktadır.) Manipülatörleri kullanmak için uygun başlık dosyasını eklemeniz gerekir:

    #katmak satır içi istream& operatörü>>(istream &io, String &s) ( // yapay limit: 4096 karakter const int 1imit_string_size = 4096; char inBuf[ limit_string_size ]; // setw() iostream kitaplığının bir parçasıdır // okuma bloğunu sınırlar boyut 1imit_string_size -l io >> setw(1imit_string_size) >> inBuf; s = mBuf; // String::operator=(const char*); dönüş io; )

    Çıktı ifadesinin, String'in dahili temsiline erişmesi gerekir. Operatörden beri<< не является функцией-членом, он не имеет доступа к закрытому члену данных _string. Ситуацию можно разрешить двумя способами: объявить operator<< дружественным классу String, используя ключевое слово friend (дружественные отношения рассматриваются в разделе 15.2), или реализовать встраиваемую (inline) функцию для доступа к этому члену. В нашем случае уже есть такая функция: c_str() обеспечивает доступ к внутреннему представлению строки. Воспользуемся ею при реализации операции вывода:

    Satır içi akış ve operatör<<(ostream& os, const String &s) { return os << s.c_str(); }

    Aşağıda, String sınıfını kullanan örnek bir program bulunmaktadır. Bu program, girdi akışından sözcükler alır ve bunların toplam sayısı ile "the" ve "it" sözcüklerinin sayısını sayar ve karşılaşılan sesli harfleri kaydeder.

    #katmak #include "String.h" int main() ( int aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0, theCnt = 0, itCnt = 0, wdCnt = 0, notVowel = 0; / / "The" ve "It" kelimeleri
    // operatörle kontrol edeceğiz==(const char*)
    String but, the("the"), it("it"); // operatör>>(ostream&, String&)
    while (cin >> buf) (
    ++wdCnt; // Şebeke<<(ostream&, const String&)
    cout<< buf << " "; if (wdCnt % 12 == 0)
    cout<< endl; // String::operator==(const String&) and
    // String::operator==(const char*);
    if (buf == the | | buf == "The")
    ++Cnt;
    başka
    if (buf == o || buf == "O")
    ++itCnt; // String::s-ize()'yi çağırır
    için (int ix =0; ix< buf.sizeO; ++ix)
    {
    // String:: operatörünü(int) çağırır
    anahtar(buf[ix])
    {
    durum "a": durum "A": ++aCnt; kırmak;
    durum "e": durum "E": ++eCnt; kırmak;
    durum "i": durum "I": ++iCnt; kırmak;
    durum "o": durum "0": ++oCnt; kırmak;
    durum "u": durum "U": ++uCnt; kırmak;
    varsayılan: ++notVowe1; kırmak;
    }
    }
    ) // Şebeke<<(ostream&, const String&)
    cout<< "\n\n"
    << "Слов: " << wdCnt << "\n\n"
    << "the/The: " << theCnt << "\n"
    << "it/It: " << itCnt << "\n\n"
    << "согласных: " < << "a: " << aCnt << "\n"
    << "e: " << eCnt << "\n"
    << "i: " << ICnt << "\n"
    << "o: " << oCnt << "\n"
    << "u: " << uCnt << endl;
    }

    Programı test edelim: Bu kitabın yazarlarından biri tarafından yazılmış bir çocuk hikayesinden bir paragraf sunacağız (bu hikaye ile 6. Bölümde buluşacağız). İşte programın çıktısı:

    Alice Emma'nın uzun, dalgalı kızıl saçları var. Babası, rüzgar saçlarının arasından estiğinde, uçmakta olan ateşli bir kuş gibi neredeyse canlı göründüğünü söylüyor. Güzel, ateşli bir kuş, diyor ona, büyülü ama evcilleştirilmemiş. "Baba şşşt, öyle bir şey yok" diyor, aynı zamanda ona daha fazlasını anlatmasını istiyor. Utangaç bir şekilde, "Yani babacığım, var mı?" diye soruyor. kelimeler: 65
    /: 2
    o: 1
    ünsüzler: 190
    bir:22
    e:30
    ben:24
    yaklaşık 10
    sen:7

    Alıştırma 3.26

    Kurucu ve atama operatörlerinin uygulamalarımızda çok fazla tekrar var. Bölüm 2.3'te yaptığımız gibi, yinelenen kodu ayrı bir özel üye işlevine taşımayı düşünün. Yeni seçeneğin çalıştığından emin olun.

    Alıştırma 3.27

    Test programını b, d, f, s, t ünsüzlerini de sayacak şekilde değiştirin.

    Alıştırma 3.28

    Aşağıdaki bildirimi kullanarak bir karakterin bir String'de tekrarlanma sayısını sayan bir üye fonksiyon yazın:

    Class String ( public: // ... int say(char ch) const; // ... );

    Alıştırma 3.29

    Dize birleştirme operatörünü (+) iki dizeyi birleştirecek ve sonucu yeni bir String nesnesinde döndürecek şekilde uygulayın. İşte işlev bildirimi:

    Class String ( public: // ... String operatörü+(const String &rhs) const; // ... );

    Programlamadaki bir veri türü, iki kümeden oluşan bir koleksiyondur: bir dizi değer ve bunlara uygulanabilecek bir dizi işlem. Örneğin, sonlu bir doğal sayılar kümesinden oluşan negatif olmayan tamsayı veri türü, toplama (+), çarpma (*), tamsayı bölme (/), kalan (%), ve çıkarma (-).

    Bir programlama dili tipik olarak bir dizi ilkel veri türüne sahiptir - programlama dili tarafından temel bir yerleşik birim olarak sağlanan türler. C++'da, dilin yaratıcısı bu tiplere temel tipler adını verir. C++'daki temel türler şunlardır:

    • Boole (bool);
    • karakter (örneğin, char);
    • tamsayı (ör. int);
    • kayan nokta (örn. kayan nokta);
    • numaralandırmalar (programcı tarafından tanımlanır);
    • geçersiz .

    Yukarıdakilere ek olarak, aşağıdaki türler oluşturulmuştur:

    • işaretçiler (örneğin, int*);
    • diziler (örn. char);
    • referans (ör. çift&);
    • diğer yapılar.

    Sabit bilgi kavramına geçelim (örneğin, 1, 2.4F, 25e-4, 'a', vb.): sabit değer, bir programın kaynak kodunda sabit bir değeri temsil eden bir girdidir. Başka bir deyişle, hazır bilgi, program kodundaki bir tür nesnenin (değerin) temsilidir. C++, tamsayı değerleri, kayan noktalı değerler, karakter, boolean, string değerleri yazma yeteneğine sahiptir.

    Bir tamsayı türü sabit değeri şu şekilde yazılabilir:

    • 10. sayı sistemi. Örneğin, 1205;
    • 0 + sayı biçiminde 8. sayı sistemi. Örneğin, 0142 ;
    • 0x + sayı biçiminde 16. sayı sistemi. Örneğin, 0x2F .

    24, 030, 0x18, aynı sayının farklı sayı sistemlerindeki girişleridir.
    Kayan noktalı sayıları yazmak için nokta gösterimi kullanılır: 0.1, .5, 4. - ya
    üstel gösterim - 25e-100. Böyle bir kayıtta boşluk olmamalıdır.

    Değişmezlerin yazdığı değerleri ilişkilendirebileceğimiz isme değişken denir. Değişken, adresi verilere erişmek için kullanılabilen, bellekte adlandırılmış veya başka şekilde adreslenebilir bir konumdur. Bu veriler, programın yürütülmesi sırasında belirli bir şekilde belleğe yazılır, üzerine yazılır ve silinir. Değişken, verilere istediğiniz zaman erişmenizi ve gerekirse değiştirmenizi sağlar. Bir değişken adından alınabilen verilere değişkenin değeri denir.
    Bir değişkeni bir programda kullanabilmek için bildirilmesi gerekir ve gerekirse tanımlanabilir (= başlatılabilir). Program metnindeki bir değişken bildirimi zorunlu olarak 2 kısım içerir: temel tip ve bildirici. Belirleyici ve başlatıcı isteğe bağlı parçalardır:

    const int örnek = 3; // burada const - belirtici // int - temel tür // örnek - değişken adı // = 3 - başlatıcı.

    Değişken adı, Latin alfabesindeki harflerden (küçük ve büyük harf), rakamlardan ve/veya alt çizgilerden oluşan bir karakter dizisidir, ancak ilk karakter bir rakam olamaz. Değişken adı, ne depoladığını tahmin etmek her zaman kolay olacak şekilde seçilmelidir, örneğin, "aylıkÖdeme". Özette ve pratikte, değişken yazma kuralları için CamelCase gösterimini kullanacağız. Bir değişkenin adı, dilde ayrılmış sözcüklerle çakışamaz, bu tür sözcüklerin örnekleri: if, while, function, goto, switch, vb.

    Bildirici, değişken adına ek olarak ek karakterler içerebilir:

    • * - Işaretçi; isimden önce
    • *const - sabit işaretçi; isimden önce
    • & - bağlantı; isimden önce
    • - sıralamak; isimden sonra;
    • () - işlev; isimden sonra.

    Başlatıcı, bildirimden hemen sonra bir değişkenin değerini tanımlamanıza olanak tanır. Başlatıcı eşittir sabit değeri (=) ile başlar ve ardından değişkenin değerini ayarlama işlemi gerçekleşir. Genel olarak konuşursak, C++'daki eşittir işareti bir atama işlemini belirtir; bir değişkenin değerini ayarlamak ve değiştirmek için kullanılabilir. Farklı türler için farklı olabilir.

    Belirleyici, tür dışındaki ek öznitelikleri belirtir. Örnekte verilen const belirteci, değişkenin değerinde sonraki değişiklikleri yasaklamanıza izin verir. Bu tür değişmez değişkenlere sabit veya sabit denir.

    Başlatma olmadan bir sabit bildirmek, mantıksal nedenlerden dolayı işe yaramaz:

    Sabit int EMPTY_CONST; // hata, sabit değişken başlatılmadı const int ÖRNEK = 2; // değeri 2 olan sabit ÖRNEK = 3; // hata, sabit bir değişkene değer atama girişimi

    Sabitleri adlandırmak için, sözcükleri alt çizgi karakteriyle ayırarak yalnızca büyük harflerin kullanılması alışılmış bir durumdur.

    C++'daki Temel Veri Türleri

    Her türü incelerken, okuyucu veri türünü tanımlamayı hatırlamalıdır.

    1. Tamsayı türü (char, short(int), int, long(int), long long)

    Adından, değer kümesinin tam sayılardan oluştuğunu anlamak kolaydır. Ayrıca, listelenen türlerin her birinin değer kümesi imzalı (imzalı) veya imzasız (imzasız) olabilir. Bir kümede bulunan öğelerin sayısı, o türün değerini depolamak için kullanılan belleğin boyutuna bağlıdır. Örneğin, char türünde bir değişken için 1 bayt bellek ayrılır, dolayısıyla toplam öğeler şöyle olacaktır:

    • 2 8N = 2 8 * 1 = 256, burada N, değeri depolamak için bayt cinsinden bellek miktarıdır

    Böyle bir durumda, kullanılabilir tam sayıların aralıkları aşağıdaki gibidir:

    • - imzasız karakter için
    • [-128..127] - imzalı karakter için

    Varsayılan olarak, tamsayı türündeki bir değişken işaretli kabul edilir. Kodda bir değişkenin işaretsiz olması gerektiğini belirtmek için, soldaki temel türe işaretli bir işaret atanır, yani. imzasız:

    işaretsiz uzun değerler; // bir tamsayı (uzun) işaretsiz tip tanımlar.

    Listelenen türler, yalnızca depolama için gereken bellek miktarında farklılık gösterir. C++ dili oldukça makineye özgü bir dil standardı olduğundan, yalnızca aşağıdaki koşulu garanti eder:

    • 1 = karakter boyutu ≤ kısa boyut ≤ int boyutu ≤ uzun boyut.

    Genellikle türlerin boyutları şu şekildedir: char - 1, short - 2, int - 4, long - 8, long long - 8 bayt.

    Tamsayı tipi değerlerle aritmetik işlemler yapabilirsiniz: +, -, *, /, %; karşılaştırma işlemleri: ==, !=,<=, <, >, >=; bit işlemleri: &, |, xor,<<, >>.
    Toplama, çarpma, çıkarma ve karşılaştırma işlemleri gibi çoğu işlemin anlaşılması kolaydır. Bazen, aritmetik işlemleri gerçekleştirdikten sonra sonuç, değer aralığının dışında olabilir; bu durumda program hata verecektir.
    Tamsayı bölme (/), bir tam sayının diğerine bölünmesinin tam sayı kısmını bulur. Örneğin:

    • 6 / 4 = 1;
    • 2 / 5 = 0;
    • 8 / 2 = 4.

    Yüzde simgesi (%) iki tamsayının bölümünden kalanı belirleme işlemini ifade eder:

    • 6 % 4 = 2;
    • 10 % 3 = 1.

    Anlaması daha zor olan işlemler bitseldir: & (AND), | (VEYA), xor (özel VEYA),<< (побитовый сдвиг влево), >> (bit düzeyinde sağa kaydırma).

    AND, OR ve XOR bit işlemleri, her bilgi bitine karşılık gelen mantıksal işlemi uygular:

    • 1 10 = 01 2
    • 3 10 = 11 2
    • 1 10 & 3 10 = 01 2 & 11 2 = 01 2
    • 1 10 | 3 10 = 01 2 | 11 2 = 11 2
    • 1 10 x veya 3 10 = 01 2 x veya 11 2 = 10 2

    Görüntü işlemede, renk için 3 kanal kullanılır: kırmızı, mavi ve yeşil - artı şeffaflık, int tipi bir değişkende saklanır, çünkü her kanalın 0 ile 255 arasında bir değer aralığı vardır. Onaltılık olarak şu şekilde bir değer yazılır: 0x180013FF; 18 16 değeri kırmızı kanala karşılık gelir, 00 16 - mavi, 13 16 - yeşil, FF - alfa kanalı (şeffaflık). Böyle bir tam sayıdan belirli bir kanalı seçmek için sözde kullanın. bizi ilgilendiren konumların F 16 veya 1 2 olduğu maske. Yani, mavi kanalın değerini vurgulamak için bir maske kullanmalısınız, yani. bitsel VE:

    int blue_channel = 0x180013FF & 0x00FF0000;

    Bundan sonra, alınan değer gerekli sayıda bit kadar sağa kaydırılır.

    Bitsel kaydırma işlemi, bir sayının işlemin sağ tarafında belirtilen bit sayısı kadar sola veya sağa kaydırır. Örneğin ikili formdaki char tipi için 39 sayısı şu şekilde yazılır: 00100111. Ardından:

    Karakter ikiliÖrnek = 39; // 00100111 char sonucu = ikiliÖrnek<< 2; // сдвигаем 2 бита влево, результат: 10011100

    Değişken işaretsiz tipteyse, sonuç 156 olacaktır, işaretli için -100'e eşittir. İşaretli tamsayı türleri için, bit temsilinin yüksek sırasındaki birimin, sayının negatif olduğunun bir işareti olduğuna dikkat edin. Bu durumda, tüm birlerden oluşan ikili formdaki bir değer -1'e karşılık gelir; 1 yalnızca en önemli basamaktaysa ve kalan basamaklar sıfırsa, böyle bir sayı belirli bir değer türü için minimum değere sahiptir: char için -128'dir.

    2. Kayan nokta tipi (kayan, çift (kayan))

    Kayan nokta değerleri kümesi, gerçek sayılar, ancak her gerçek sayı ikili olarak temsil edilemez, bu da bazen aptalca hatalara yol açar:

    kayan değer = 0,2; değer == 0,2; // hata, burada değer 0.2'ye eşit olmayacak.

    Kayan noktalı değişkenlerle çalışırken, programcı eşitlik veya eşitsizlik işlemini kullanmamalıdır, bunun yerine genellikle belirli bir aralığa ulaşmak için bir test kullanır:

    Değer - 0,2< 1e-6; // ok, подбирать интервал тоже нужно осторожно

    Karşılaştırma işlemlerine ek olarak, kayan nokta türü, gerçek sayılarla matematiksel işlemlerle tamamen tutarlı olan 4 aritmetik işlemi destekler.

    3. Boolean (mantıksal) tip (bool)

    Yalnızca iki değerden oluşur: doğru (doğru) ve yanlış (yanlış). Bu tür değişkenlerle çalışmak için mantıksal işlemler kullanılır: ! (DEĞİL), == (eşitlik), != (eşitsizlik), && (mantıksal VE), || (mantıksal VEYA). Her işlemin sonucu ilgili doğruluk tablosunda bulunabilir. Örneğin:

    X Y XOR0 0 0 0 1 1 1 0 1 1 1 0

    4. Karakter türü (char, wchar_t)

    Char türü yalnızca bir tamsayı türü değil (genellikle böyle bir türe bayt denir), aynı zamanda tablodaki karakter numarasını ASCII karakteri olarak depolayan bir karakter türüdür. Örneğin, 0x41 kodu 'A' karakterine ve 0x71 - 't' karakterine karşılık gelir.

    Bazen ASCII tablosunda sabit olmayan ve bu nedenle saklanması için 1 bayttan fazlasını gerektiren karakterlerin kullanılması gerekli hale gelir. Onlar için geniş bir karakter (wchar_t) vardır.

    5.1. diziler

    Diziler, aynı türden sıralı bir dizi öğeyi depolamanıza izin verir. Bir dizi, bitişik bir blokta bellekte saklanır, bu nedenle boyutunu belirtmeden bir dizi bildiremezsiniz. Bir dizi bildirmek için, değişken adından sonra boyutunu belirten köşeli parantezler () yazın. Örneğin:

    int dizim; // Tamsayı türünde 5 elemanlı dizi

    Bir diziyi başlatmak için değerler kaşlı ayraçlar içinde listelenir. Bu şekilde yalnızca değişken bildirimi sırasında başlatabilirsiniz. Bu arada, bu durumda dizinin boyutunu belirtmek gerekli değildir:

    int oranlar = (1, 3, 7, 9, 11); // Dizi 5 değerle başlatılır

    Bir dizideki (dizi öğesi) belirli bir değere erişmek için, öğe numarasıyla (sayılar 0'dan başlar) dizin erişim işlemini () kullanın. Örneğin:

    oranlar; // dizinin ilk elemanına erişim. 1 oran değerini döndürür; // üçüncü öğeye erişim. 7 oran = 13 değerini döndürür; // oran dizisinin 5. elemanına yeni bir değer atayın; // erişim hatası

    5.3. Teller

    Bir dizi yazmak için programcılar, bir dizinin ardışık bir karakter dizisi (dizisi) olduğu fikrini kullanırlar. Bir satırın sonunu belirlemek için şunu kullanın: özel karakter satır sonu: '\0'. Bir ters eğik çizgi ve tanımlayıcı bir karakterden oluşan bu özel karakterlere kontrol veya kaçış karakterleri denir. Hala, örneğin, '\n' - yeni bir satırın başlangıcı, '\t' - tablolama vardır. Bir satırda ters eğik çizgi kaydetmek için kaçış kullanılır - işaretin önüne başka bir eğik çizgi konur: '\'. Çıkış, tırnak işaretleri yazmak için de kullanılır.

    Bir dizi değişkeni oluşturalım:

    Char textExample = ('T', 'e', ​​​​'s', 't', '\0'); // "Test" stringi yazılır

    Dize başlatma için basitleştirilmiş bir gösterim vardır:

    Char textExample = "Test"; // Son karakter yazılmamış ama boyutu hala 5

    Ayrıntılara girmeden, işte başka bir yararlı veri türü - string. Teller
    bu türden, örneğin şunları ekleyebilirsiniz:

    String merhaba = "Merhaba,"; stringname = "Maksimum!"; string merhaba_adı = merhaba + isim; // "Merhaba, Max!" dizesini alın

    6. Bağlantı

    int a = 2; // "a" değişkeni 2 değerini gösteriyor int &b = a; // "b" değişkeni "a" ile aynı yeri gösteriyor b = 4; // b'nin değerini değiştirerek, programcı a'nın değerini değiştirir. Şimdi a = 4 int &c = 4; // hata, bunu yapamazsınız, çünkü referansa bir değer atanamaz

    7. İşaretçi

    Bu tür verilerle başa çıkmak için, bu türdeki değerler kümesinin, verilerin başladığı bellek hücrelerinin adresleri olduğunu hatırlamak gerekir. İşaretçi ayrıca toplama (+), çıkarma (-) ve başvuru kaldırma (*) işlemlerini de destekler.

    0x0 adresleri, işaretçinin boş olduğu anlamına gelir, yani. herhangi bir veriye işaret etmez. Bu adresin kendi hazır değeri var - NULL:

    Int *nullPtr = NULL; // boş işaretçisi

    Bir tamsayı veya başka bir adres ile bir adresin eklenmesi ve çıkarılması,
    programın kullanabileceği bellekte dolaşın.

    Bir işaretçide saklanan adresten başlayarak veri alma işlemine başvuru kaldırma (*) denir. Program, gerekli sayıda bellek hücresini okur ve bellekte depolanan değeri döndürür.

    Int değeriInMemory = 2; // tamsayı türünde bir değişken ayarlayın int *bazıPtr = // değişkenin adresini kopyalayın, burada & - bazıPtr değişkeninin adresini döndürür; // bellek hücresinin adresi, örneğin, 0x2F *birPtr; // değer 4 hücrede saklanır: 0x2F, 0x30, 0x31 ve 0x32

    İmleçler için sözdizimsel olarak kopyalama işlemiyle aynı olan atama işlemi kullanılamaz. Başka bir deyişle, başka bir işaretçinin adresini veya bir değişkenin adresini kopyalayabilirsiniz, ancak adresin değerini kendiniz belirleyemezsiniz.

    İşaretçinin kendisi, diğer türlerdeki değişkenlerin değerleri gibi bellekte saklanır ve 4 bayt alır, böylece bir işaretçi için bir işaretçi oluşturabilirsiniz.

    8. Transferler

    Numaralandırmalar, programcı tarafından tanımlanan tek temel türdür. Genel olarak, bir numaralandırma, numaralandırma adının temel tip olduğu sıralı bir adlandırılmış tamsayı sabitleri kümesidir.

    Enumcolor(KIRMIZI, MAVİ, YEŞİL);

    Varsayılan olarak KIRMIZI = 0, MAVİ = 1, YEŞİL = 2. Bu nedenle değerler birbiriyle karşılaştırılabilir, yani. KIRMIZI< BLUE < GREEN. Программист при объявлении перечисления может самостоятельно задать значения каждой из констант:

    Numaralandırma erişimi(READ=1, WRITE=2, EXEC=4);

    Değerleri ikinin gücü olan numaralandırmaları kullanmak genellikle uygundur, çünkü ikili gösterimde, 2'nin kuvveti olan bir sayı 1. birim ve sıfırlardan oluşacaktır. Örneğin:

    8 10 = 00001000 2

    Bu sayıları toplamanın sonucu, hangi sayıların eklendiğini her zaman açıkça gösterir:

    37 10 = 00100101 2 = 00000001 2 + 00000100 2 + 00100000 2 = 1 10 + 4 10 + 32 10

    Geçersiz

    Sözdizimsel olarak, boşluk türü temel türlerden biridir, ancak yalnızca daha karmaşık türlerin bir parçası olarak kullanılabilir, çünkü void türünde nesneler mevcut değil. Tipik olarak, bu tür, bir işlevin dönüş değeri olmadığını bildirmek için veya tanımsız türdeki nesnelere yönelik bir işaretçinin temel türü olarak kullanılır:

    geçersiz nesne; // hata, void void türünde nesne yok // error, void void *ptr'ye referans yok; // tamam, bilinmeyen bir türe işaretçi kaydet

    Genellikle bir fonksiyonun herhangi bir değer döndürmediğini belirtmek için özellikle void kullanırız. Bir geçersiz tür işaretçisi, programcı bellek bütünlüğü ve doğru tür ataması için tam sorumluluk aldığında işlenir.

    Döküm

    Genellikle bir tür değişkenin değerini diğerine atamak gerekir. Orijinal türün değer kümesinin daha büyük bir türün alt kümesi olduğu durumda (örneğin, int, uzun bir alt kümedir ve uzun bir çifttir), derleyici dolaylı olarak ( dolaylı olarak) değer türünü değiştirin.

    int tamsayı = 2; kayan kayan = tamsayı; // kayan = 2.0

    Tür dönüştürme bilgi kaybı ile gerçekleştirilecektir, bu nedenle kayan noktalı sayının sadece tamsayı kısmı kalacak, kesirli kısmı kaybolacaktır.

    Açık (açıkça) tür dönüştürme olasılığı vardır, bunun için değişkenin soluna veya orijinal türün bir değerine, parantez içinde, dönüştürmenin yapılacağı türü yazın:

    int değeri = (int) 2.5;

    Birli ve ikili işlemler

    Daha önce yaptığımız işlemlere ikili denir: işlem sembolünün solunda ve sağında değerler veya değişkenler vardır, örneğin 2 + 3. İkili işlemlere ek olarak, programlama dilleri tekli işlemleri de kullanır. değişkenler için geçerlidir. Bir değişkenin solunda veya sağında olabilirler, daha önce bu tür birkaç işlemle karşılaşılmıştır - dereference işlemi (*) ve bir değişkenin adresini alma (&) tektir. "++" ve "-" operatörleri, bir tamsayı değişkenin değerini sırasıyla 1 artırır ve azaltır ve değişkenin soluna veya sağına yazılabilir.

    C++, bir ifadenin sol ve sağ tarafları aynı değişkeni içerdiğinde ikili işlemler için bir kestirme notasyon kullanır; değişkenin değeri ile bazı işlemler gerçekleştirilir ve işlemin sonucu aynı değişkende saklanır:

    += 2; // a = a + 2 ile aynı; b /= 5; // b = b / 5 ile aynı; c &= 3; // c = c & 3 ile aynı;

    C'deki veri türleri, değerleri benzer özelliklere sahip bir veri sınıfıdır. Tür, bellekteki verilerin dahili temsilini tanımlar. En temel veri türleri boolean, tamsayı, kayan nokta, string, pointer'dır.

    Dinamik yazımda, bir değişken, başlatma sırasında bir türle ilişkilendirilir. Kodun farklı bölümlerindeki bir değişkenin farklı türleri olabileceği ortaya çıktı. Dinamik yazma Java Script, Python, Ruby, PHP tarafından desteklenir.

    Statik yazma, dinamik yazmanın tersidir. Bildirildiğinde, bir değişken gelecekte değişmeyen bir tür alır. C ve C++ dilleri tam da budur. Bu yöntem, karmaşık kod yazmak için en uygundur ve derleme aşamasında birçok hata ortadan kaldırılır.

    Diller gayri resmi olarak güçlü bir şekilde yazılmış ve zayıf bir şekilde yazılmıştır. Güçlü yazma, beklenen ve gerçek türler eşleşmezse derleyicinin bir hata vereceği anlamına gelir.

    x = 1 + "2"; //hata - sayıya karakter işareti ekleyemezsiniz

    Zayıf yazma örneği.

    Tip tutarlılığı kontrolü, tip güvenlik sistemi tarafından gerçekleştirilir. Örneğin, bir sayıyı işlev olarak kullanmaya çalışırken bir yazım hatası oluşur. Yazılmamış diller var. Yazmanın aksine, her nesne üzerinde herhangi bir işlem yapmanıza izin verirler.

    Bellek sınıfları

    Türleri ne olursa olsun değişkenlerin kapsamları ve ömürleri vardır.

    Bellek sınıfları:

    • Oto;
    • statik;
    • harici;
    • kayıt olmak.

    C'deki tüm değişkenler varsayılan olarak yereldir. Yalnızca bir işlev veya blok içinde kullanılabilirler. Fonksiyon sona erdiğinde değerleri yok olur.

    Bir statik değişken de yereldir, ancak bloğunun dışında farklı bir değere sahip olabilir ve işlev çağrıları arasında değer korunur.

    Dış değişken globaldir. Kodun herhangi bir bölümünde ve hatta başka bir dosyada mevcuttur.

    C'deki veri tipi tanımlayıcıları aşağıdaki durumlarda atlanabilir:

    1. Bloğun içindeki tüm değişkenler değişken değildir; buna göre, bu belirli depolama sınıfının kullanılması gerekiyorsa, o zaman otomatik belirtici belirtilmez.
    2. Bir bloğun veya işlevin dışında bildirilen tüm işlevler varsayılan olarak geneldir, bu nedenle extern belirtici isteğe bağlıdır.

    Basit türleri belirtmek için int, char, float veya double belirticileri belirtilir. Değişkenlerin yerine unsigned (unsigned), Signed (signed), short, long, long long değiştiricileri kullanılabilir.

    Varsayılan olarak, tüm sayılar işaretlidir, bu nedenle yalnızca pozitif sayılar aralığında olabilirler. char türünde bir değişkeni işaretli olarak tanımlamak için, işaretli char yazın. Uzun, uzun uzun ve kısa, depolama için ne kadar bellek alanı ayrıldığını gösterir. En büyüğü uzun uzun, en küçüğü kısa.

    Char, C'deki en küçük veri türüdür. Değerleri depolamak için yalnızca 1 bayt bellek ayrılmıştır. Karakter tipi bir değişkene genellikle karakterler atanır, daha az yaygın olarak rakamlardır. Karakter değerleri tırnak içine alınmıştır.

    int türü tamsayıları depolar, boyutu tanımlanmamıştır - bilgisayar mimarisine bağlı olarak 4 bayta kadar bellek kaplar.

    İşaretsiz bir değişkenin açık dönüşümü şu şekilde verilir:

    Örtülü şuna benzer:

    Sayıları bir nokta ile yüzer ve çift tanımlar. Float'lar -2.3 veya 3.34 olarak temsil edilir. Double, daha fazla kesinlik için kullanılır - ondalık ayırıcıdan sonra daha fazla basamak belirtilir. Bu tip, float'tan daha fazla bellek alanı kaplar.

    Void'in boş bir değeri var. Hiçbir şey döndürmeyen işlevleri tanımlar. Bu belirtici, yöntem bağımsız değişkenlerinde boş bir değer belirtir. Herhangi bir veri türünü alabilen işaretçiler de geçersiz olarak tanımlanır.

    Boole türü

    Durum testleri ve döngülerde kullanılır. Sadece iki anlamı vardır:

    • doğru;
    • yalan.

    Boole değerleri int değerine dönüştürülebilir. True bire eşittir, false sıfıra eşittir. Tip dönüşümü sadece bool ve int arasında sağlanır, aksi halde derleyici hata verir.

    if (x) ( //Hata: "'int' tipi dolaylı olarak 'bool'a dönüştürülemez""

    if (x != 0) // C# yolu

    Dizeler ve diziler

    Diziler, C'deki karmaşık veri türleridir. JS, dizelerle Javascript veya Ruby'nin yaptığı gibi çalışmaz. C'de, tüm dizeler karakter değer öğelerinin dizileridir. Dizeler bir boş bayt ile biter "

    Veri türü, belirtilen türdeki bir değişkenin alabileceği değer aralığının açıklamasıdır. Her veri türü aşağıdakilerle karakterize edilir:
    1. kullanılan bayt sayısı (boyut)
    2. bu tip bir değişkenin alabileceği değer aralığı.

    Tüm veri türleri aşağıdaki türlere ayrılabilir:
    1. basit (skaler) ve karmaşık (vektör) tipler;
    2. temel (sistem) ve kullanıcı (kullanıcı tarafından tanımlanır).
    C dilinde, temel tip sistemi dört veri tipinden oluşur:
    1. sembolik,
    2. tamsayı,
    3. gerçek tek hassasiyet,
    4. gerçek çift hassasiyet.

    C dilinde veri türlerinin ayrıntılı açıklaması

    tip Karakter türü tamsayı türü Tek hassasiyetli gerçek tip Çift hassasiyetli gerçek tip
    Tanım karakter int batmadan yüzmek çift
    boyut 1 bayt (8 bit) 4 bayt (32 bit) 4 bayt (32 bit)
    23 bit - mantis;
    8 bit - sipariş;
    1 bit - işaret.
    8 bayt (64 bit)
    52 bit - mantis;
    11 bit - sipariş;
    1 bit - işaret.
    değer aralığı -128 ... 127 2147483648 ... 2147483647 ±3,4E±38
    7 ondalık basamağa kadar doğruluk
    ±1,7E±308
    17 ondalık basamağa kadar doğruluk

    C dili iki tür veri türü değiştirici sağlar:
    1. işaret değiştiriciler: imzalı ve imzasız.
    2. boyut değiştiriciler: kısa ve uzun.
    Tip değiştiriciler tabloda daha ayrıntılı olarak açıklanmaktadır:

    SI'daki karmaşık sayılar

    Karmaşık sayılar C99 standardında tanıtıldı.
    kayan _Karmaşık
    çift ​​_Karmaşık
    uzun çift _Karmaşık
    Bütün bu mutluluklar kütüphanede karmaşık.h :)

    SI dilinin tüm temel veri türlerinin minimum ve maksimum değerleri kitaplıklarda açıklanmıştır: limit.h - tamsayı değer aralıklarını içerir, float.h - gerçek değer aralıklarını içerir.

    SI'da Boolean veri türü

    C89 standardı:

    boole tipi - int
    0 - yanlış (yanlış);
    0 değil - doğru (doğru). Yani mantıksal tür oluşturulmaz, bunun yerine int kullanılır.
    C99 standardı:
    Boole tipi - _Bool
    Anahtar kelimeler: bool doğru yanlış
    Ve bu kütüphanedeki mutluluk stdbool.h

    Bildirim Operatörleri

    Değişken, belirli bir türdeki değerleri rastgele bir erişim yöntemiyle depolamak için tasarlanmış bir bilgisayarın adlandırılmış bir bellek alanıdır: okuma ve yazma. Değişken adı, daha önce diğer değişkenlere, türlere, numaralandırma üyelerine veya işlev adlarına başvurmak için kullanılmayan yasal bir C dili tanımlayıcısıdır. Değişken bildirim işleci aşağıdaki sözdizimine sahiptir: type name1[,name2[,...]]; Örnekler:
    int a, b, c;
    çift ​​x, y;
    çarş;
    Bazı söylenmemiş kurallar vardır, yani iyi biçim olan uygulama, ancak bunu yapmak gerekli değildir:
    1. yeni bir türdeki değişkenlerin her bildirimi yeni bir satırda başlar;
    2. değişkenin adından, neden olduğu ve içinde ne saklanacağı açık olmalıdır (ancak bazen bu tür bilgilendirici isimler nedeniyle, kodu yazma hızı düşer, çünkü bazı insanlar kendilerini kaptırır ve değişkenleri çağırırlar. tüm cümleler);
    3. yani bir kural var: değişkenin adı çok uzun olmamalı;
    4. bir değişkeni tanımladıktan sonra, yorumlarda neden olduğunu işaretlemek çok arzu edilir;
    5. Değişken adlarını boşluklarla ayırmak gerekir.
    Başlatmalı değişken bildirim işleci aşağıdaki sözdizimine sahiptir: type name1[=value1][, name2[=value2][,...]]; Örnekler:
    int a=26, b=032, c=0x1A;
    çift ​​x=2.5e2,y=0x1.ffe-3;
    chach='Z';

    SI sabitleri

    C dilinde üç tür sabit vardır:
    1. tamsayı,
    2. gerçek,
    3. sembolik.
    Tamsayı Sabitleri
    1. Bir ondalık sabit, olağan formda bir ondalık sayı ile gösterilir.
    2. Sekizlik bir sabit, sıfır rakamıyla başlayan ve 0...7 rakamlarını içeren bir sayı ile gösterilir.
    3. Onaltılık bir sabit, 0...9 rakamlarını ve Latin alfabesinin a...f, A...F harflerini içeren, 0x veya 0X öneki olan bir tamsayı ile gösterilir.
    Gerçek sabitler ondalık veya onaltılık gösterimde yazılır. Virgülün konumu bir nokta ile gösterilir, üs Latince e (veya E) harfinden sonra gösterilir. Karakter sabitlerinden önce bir \ karakteri gelir, buna "kaçış" denir. C dilinde özel karakterler vardır:
    '\'' - tek alıntı,
    '\”' – çift tırnak,
    '\\' – ters eğik çizgi,
    '\?' - soru işareti,
    '\a' – ses sinyali,
    '\b' – bir karakterin silinmesi,
    '\f' – sayfa kaydırma,
    '\n' – satır besleme,
    '\r' – satırın başına satır başı dönüşü,
    '\t' – yatay sekme,
    '\v' - dikey sekme.

    C'de sabit bir değere sahip değişkenler de oluşturabilirsiniz (değerleri değiştirilemez). Bu tür "değişkenlerin" bildirimi aşağıdaki sözdizimine sahiptir: const type ad1=değer1[,ad2=değer2[,...]]; Örnekler:
    const işaretsiz int x=80, y=25;
    sabit çift pi=3.1415;

    SI'da veri türleri oluşturmak için operatör

    typedef işleci, özel veri türleri oluşturmak için kullanılır, onu kullanmanın sözdizimi şöyledir: typedef eski_tür_adı yeni_tür_adı; Örnek:
    typedef imzasız intword;
    SI'da, standarda göre, tür tanımı programın hemen hemen her yerinde yapılabilir (yani, veri türlerini tanımlamak için kesin olarak tanımlanmış bir blok yoktur).Bir türün boyutunu veya herhangi bir türdeki değişkeni belirlemek için bir işlev: sizeof bellekte kullanılan bayt sayısını döndürür. Örnek:
    sizeof(int) //4 döndürür
    sizeof(char) //sonuç 1
    sizeof(double) // 8 döndürür