• Ne tür veri çizin. Veri türlerinin ağırlığı ne kadardır?

    Veri tipleri. C'nin ait olduğu prosedürel dillerdeki bir program, çeşitli türlerdeki değerler üzerindeki işlemlerin bir açıklamasıdır. 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) tamsayı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. Bir char değişkeninin değeri bir karakter ve bir int değişkeninin değeri bir tamsayı olduğu gibi, bir işaretçi değişkeninin değeri de 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 çıkarma işleminden ö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);

    Sonuç program yürütme: 11 1 0 1

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

    C dili ile diğer diller (PL1, FORTRAN, vb.) arasındaki önemli bir fark, programda kullanılan tüm değişkenlerin karşılık gelen türlerinin bir göstergesi ile birlikte açık bir şekilde bildirilmesi ihtiyacına yol açan varsayılan ilkenin olmamasıdır. .

    Bir değişken bildirimi aşağıdaki biçime sahiptir:

    [depolama-sınıfı-belirleyici] tür belirtici tanımlayıcı [= başlatıcı] [, bildirici [= başlatıcı] ]...

    Tanımlayıcı, basit bir değişken tanımlayıcısı veya köşeli parantezler, parantezler veya yıldız işareti (bir dizi yıldız işareti) içeren daha karmaşık bir yapıdır.

    Bir tür belirteci, bildirilmekte olan değişkenin türünü tanımlayan bir veya daha fazla anahtar sözcüktür. C dili, yeni (benzersiz) veri türleri oluşturmak için kullanılabilen standart bir veri türleri kümesine sahiptir.

    Başlatıcı - bildirildiğinde değişkene atanan (ki) başlangıç ​​​​değerini veya ilk değerlerin listesini belirtir.

    Bellek sınıfı belirleyicisi, SI dilinin dört anahtar sözcüğünden biri tarafından tanımlanır: auto, extern, register, static ve bir yandan, diğer yandan, kapsamın beyan edilen değişken için belleğin nasıl tahsis edileceğini gösterir. bu değişkenin, yani programın hangi bölümlerinden ona erişebileceğiniz.

    1.2.1 Veri türü kategorileri

    Temel veri türlerini tanımlamak için anahtar sözcükler

    Tamsayı türleri: Kayan türler: char float int double short long double longsigned unsigned

    Herhangi bir tür değişken değiştirilemez olarak bildirilebilir. Bu, tür belirticiye const anahtar sözcüğünü ekleyerek elde edilir. const türündeki nesneler salt okunur verileri temsil eder, yani bu değişkene yeni bir değer atanamaz. Const kelimesinden sonra bir tür belirtici yoksa, o zaman tür belirticinin int ima edildiğini unutmayın. Const anahtar sözcüğü, bileşik türlerin (dizi, yapı, karışım, numaralandırma) bildiriminden önce gelirse, bu, her bir öğenin de değiştirilemez, yani değiştirilemez olması gerektiği gerçeğine yol açar. yalnızca bir kez değer atanabilir.

    Sabit çift A=2.128E-2; sabit B=286; (const int B=286 varsayılarak)

    Bileşik verileri bildirme örnekleri aşağıda tartışılacaktır.

    1.2.2. tamsayı veri türü

    Bir tamsayı türündeki verileri tanımlamak için, değer aralığını ve değişkenler için ayrılan bellek alanının boyutunu belirleyen çeşitli anahtar kelimeler kullanılır (Tablo 6).

    Tablo 6

    İmzalı ve imzasız anahtar kelimelerin isteğe bağlı olduğunu unutmayın. Bildirilen değişkenin sıfır bitinin nasıl yorumlanacağını belirtirler, yani unsigned anahtar sözcüğü belirtilirse, sıfır biti sayının bir parçası olarak yorumlanır, aksi takdirde sıfır biti işaret olarak yorumlanır. unsigned anahtar sözcüğünün yokluğunda, tamsayı değişkeni işaretli kabul edilir. Tür belirteci, imzalı veya imzasız bir anahtar türünden ve ardından bir değişken tanımlayıcıdan oluşuyorsa, bir int değişkeni olarak ele alınacaktır. Örneğin:

    imzasız giriş; imzasız int b; int c; (imzalı int c varsayılarak); imzasız d; (işaretsiz int d varsayılarak); f imzalı; (imzalı int f varsayılarak).

    char tür değiştiricisinin bir karakteri temsil etmek (karakterlerin bir dizi temsilinden) veya dize değişmezlerini bildirmek için kullanıldığını unutmayın. Bir char nesnesinin değeri, temsil edilen karaktere karşılık gelen koddur (1 bayt boyutunda). Rus alfabesinin karakterlerini temsil etmek için, veri tanımlayıcı türü değiştiricisi unsigned char'dır, çünkü Rus harflerinin kodları 127 değerini aşmaktadır.

    Aşağıdaki not yapılmalıdır: C dili, int ve unsigned int tip değiştiricilere sahip tanımlayıcılar için bir bellek gösterimi ve değer aralığı tanımlamaz. Signed int tür değiştiricili bir değişken için bellek boyutu, üzerinde farklı bir boyuta sahip olan makine word'ünün uzunluğu tarafından belirlenir. farklı makineler. Yani, 16 bit makinelerde kelime boyutu 2 bayt, 32 bit makinelerde sırasıyla 4 bayt, yani. int türü, kullanılan PC'nin mimarisine bağlı olarak kısa int veya uzun int türlerine eşdeğerdir. Bu nedenle, aynı program bir bilgisayarda doğru çalışırken başka bir bilgisayarda hatalı çalışabilir. Bir değişkenin kapladığı belleğin uzunluğunu belirlemek için, belirtilen tür değiştiricinin uzunluk değerini döndüren C dili sizeof işlemini kullanabilirsiniz.

    Örneğin:

    A = sizeof(int); b = sizeof(uzun int); c = sizeof(işaretsiz uzun); d = sizeof(kısa);

    Ayrıca sekizlik ve onaltılık sabitlerin de işaretsiz değiştiriciye sahip olabileceğini unutmayın. Bu, sabitten sonra bir u veya U öneki belirtilerek elde edilir, bu öneki olmayan bir sabit imzalı olarak kabul edilir.

    Örneğin:

    0xA8C (int imzalı); 01786l (uzun imzalı); 0xF7u (int işaretsiz);

    1.2.3. kayan veri

    Bir kayan noktalı sayıyı temsil eden değişkenler için şu tip değiştiriciler kullanılır: float, double, long double (long double dilinin bazı uygulamalarında SI yoktur).

    Kayan tip değiştiricili bir değer 4 bayt yer kaplar. Bunlardan 1 bayt işarete, 8 bit fazlalık üs için ve 23 bit mantis için ayrılmıştır. Mantisin en önemli bitinin her zaman 1 olduğuna dikkat edin, bu nedenle doldurulmaz, bu nedenle kayan nokta değişkeninin aralığı yaklaşık 3.14E-38 ila 3.14E+38'dir.

    Double değeri bellekte 8 bit yer kaplar. Biçimi, kayan biçime benzer. Bellek bitleri şu şekilde tahsis edilir: işaret için 1 bit, üs için 11 bit ve mantis için 52 bit. Mantisin atlanan yüksek biti göz önüne alındığında, değer aralığı 1.7E-308 ile 1.7E+308 arasındadır.

    Float f, a, b; çift ​​x, y;

    1.2.4. İşaretçiler

    İşaretçi, tanımlayıcıyı yerleştirmek için tahsis edilen belleğin adresidir (bir değişkenin adı, dizi, yapı, dize sabit değeri tanımlayıcı görevi görebilir). Bir değişken işaretçi olarak bildirilirse, herhangi bir türde skaler değerin bulunabileceği bir bellek adresi içerir. İşaretçi tipinde bir değişken bildirirken, adresi değişkenin içereceği veri nesnesinin türünü ve önünde bir yıldız (veya bir yıldız grubu) bulunan işaretçinin adını tanımlamak gerekir. İşaretçi bildirim biçimi:

    tip belirleyici [ değiştirici ] * tanımlayıcı.

    Tip belirleyici, nesnenin tipini belirtir ve herhangi bir temel tip, yapı tipi, karışım olabilir (bu aşağıda tartışılacaktır). Bir tür belirtici yerine void belirterek, işaretçi tarafından başvurulan türün belirtimini bir şekilde erteleyebilirsiniz. Bir geçersiz türe işaretçi olarak bildirilen bir değişken, herhangi bir türdeki bir nesneye başvurmak için kullanılabilir. Ancak, işaretçiler veya işaret ettikleri nesneler üzerinde aritmetik ve mantıksal işlemler yapabilmek için, her işlemi gerçekleştirirken nesnelerin türünü açıkça tanımlamak gerekir. Bu tip belirlemeler, bir döküm işlemi kullanılarak gerçekleştirilebilir.

    const, yakın, uzak, büyük anahtar sözcükleri, bir işaretçi bildirirken değiştiriciler olarak işlev görebilir. Const anahtar sözcüğü, işaretçinin programda değiştirilemeyeceğini belirtir. İşaretçi olarak bildirilen bir değişkenin boyutu, bilgisayar mimarisine ve programın derleneceği bellek modeline bağlıdır. Farklı veri türlerine yönelik işaretçilerin aynı uzunlukta olması gerekmez.

    Yakın, uzak, büyük anahtar kelimeler işaretçi boyutunu değiştirmek için kullanılabilir.

    işaretsiz int * a; /* a değişkeni bir işaretçidir imzasız tip int (işaretsiz tamsayılar) */ double * x; /* x değişkeni çift duyarlıklı bir kayan noktalı veri tipini işaret eder */ char *fuffer ; /* char türünde bir değişkene işaret eden fuffer adlı bir işaretçi bildir */ double numberer; geçersiz *adresler; adresler = &sayı; (çift *) adresler ++; /* adres değişkeni, herhangi bir türdeki nesneye işaretçi olarak bildirilir. Bu nedenle, herhangi bir nesnenin adresi atanabilir (&, adres hesaplama işlemidir). Ancak, yukarıda belirtildiği gibi, işaret ettiği verinin türü açıkça tanımlanana kadar bir işaretçi üzerinde hiçbir aritmetik işlem gerçekleştirilemez. Bu, adresleri bir işaretçiye çifte dönüştürmek için bir atama işlemi (double *) kullanılarak ve ardından adresi artırarak yapılabilir. */ const *dr; /* dr değişkeni, sabit bir ifadeye işaretçi olarak bildirilir, örn. Bir işaretçinin değeri, programın yürütülmesi sırasında değişebilir, ancak işaret ettiği değer değişmez. */ unsigned char * const w = &obj. /* w değişkeni, işaretsiz verileri char için sabit bir işaretçi olarak bildirilir. Bu, w'nin tüm program boyunca aynı hafıza alanını işaret edeceği anlamına gelir. Bu alanın içeriği değişebilir. */

    1.2.5. Numaralandırılmış Değişkenler

    Bazı değerler listesinden değer alabilen bir değişkene numaralandırılmış tip değişkeni veya numaralandırma denir.

    Bir numaralandırma bildirimi, enum anahtar sözcüğüyle başlar ve iki gösterim formatına sahiptir.

    Format 1. enum [enum-tag-name] (enum-list) tanımlayıcı[, tanımlayıcı...];

    Biçim 2. enum enum-etiket-adı tanımlayıcı [, tanımlayıcı..];

    Bir enum bildirimi, bir enum değişkeninin türünü belirtir ve enum-list adı verilen adlandırılmış sabitlerin bir listesini tanımlar. Her liste adının değeri bir tamsayıdır.

    Bir enum tipi değişken, listenin adlandırılmış sabitlerinden birinin değerlerini alabilir. Adlandırılmış liste sabitleri int türündedir. Bu nedenle, bir numaralandırma değişkenine karşılık gelen bellek, bir int değerini depolamak için gereken bellektir.

    Enum tipi bir değişken, dizin ifadelerinde ve aritmetik ve ilişkisel işlemlerde işlenenler olarak kullanılabilir.

    İlk format 1'de, enum adları ve değerleri bir enum listesinde belirtilir. İsteğe bağlı enum-tag-name, enum-list tarafından tanımlanan enum etiketini adlandıran bir tanımlayıcıdır. Tanımlayıcı, bir numaralandırma değişkenini adlandırır. Bir bildirimde birden fazla enum tipi değişken tanımlanabilir.

    Bir numaralandırma listesi, formun bir veya daha fazla yapısını içerir:

    tanımlayıcı [= sabit ifade]

    Her tanımlayıcı bir enum öğesini adlandırır. Bir enum listesindeki tüm tanımlayıcılar benzersiz olmalıdır. Sabit bir ifadenin yokluğunda, 0 değeri ilk tanımlayıcıya, 1 değeri bir sonraki tanımlayıcıya karşılık gelir vb. Bir numaralandırma sabitinin adı, değerine eşdeğerdir.

    Bir sabit ifadeyle ilişkili tanımlayıcı, o sabit ifade tarafından belirtilen değeri alır. Sabit ifade int türünde olmalıdır ve pozitif veya negatif olabilir. Listedeki bir sonraki tanımlayıcıya, bu tanımlayıcının kendi sabit ifadesi yoksa, sabit ifade artı 1'e eşit bir değer atanır. Numaralandırma öğelerinin kullanımı aşağıdaki kurallara uymalıdır:

    1. Bir değişken yinelenen değerler içerebilir.

    2. Bir numaralandırma listesindeki tanımlayıcılar, sıradan değişken adları ve diğer numaralandırma listelerindeki tanımlayıcılar dahil olmak üzere, aynı kapsamdaki diğer tüm tanımlayıcılardan farklı olmalıdır.

    3. Enum türlerinin adları, aynı kapsamdaki diğer enum türlerinin, yapılarının ve karışımlarının adlarından farklı olmalıdır.

    4. Değer, enum listesinin son öğesini takip edebilir.

    Hafta sayımı ( SUB = 0, /* 0 */ VOS = 0, /* 0 */ POND, /* 1 */ VTOR, /* 2 */ SRED, /* 3 */ HETV, /* 4 */ PJAT /* 5 */ ) slave_ned ;

    İÇİNDE bu örnek numaralandırılmış etiket haftası, karşılık gelen değer kümesiyle bildirilir ve hafta türüne sahip rab_ned değişkeni bildirilir.

    İkinci biçim, başka bir yerde tanımlanan bir enum türüne atıfta bulunmak için enum etiketinin adını kullanır. Enum etiketi adı, geçerli kapsam içinde zaten tanımlanmış bir enum etiketine atıfta bulunmalıdır. Enum etiketi başka bir yerde bildirildiğinden, bildirimde enum listesi yoktur.

    Bir enum veri türüne işaretçi bildirirken ve enum türleri için typedef bildirimlerinde, enum etiketi tanımlanmadan önce enum etiket adını kullanabilirsiniz. Ancak, numaralandırma tanımı, kullanılan işaretçinin typedef bildirim türüne yönelik herhangi bir eyleminden önce gelmelidir. Daha sonra tanımlayıcı listesi olmayan bir bildirim, bir etiketi veya deyim yerindeyse, bir numaralandırma şablonunu tanımlar.

    1.2.6. diziler

    Diziler, aynı türden (double, float, int, vb.) bir grup öğedir. Dizi bildiriminden derleyici, dizi öğelerinin türü ve sayıları hakkında bilgi almalıdır. Bir dizi bildiriminin iki biçimi vardır:

    tip tanımlayıcı tanımlayıcı [const - ifade];

    tip belirleyici tanımlayıcı;

    Tanımlayıcı bir dizi tanımlayıcıdır.

    Tip belirteci, bildirilen dizinin elemanlarının tipini belirtir. Dizi öğeleri işlev veya geçersiz öğe olamaz.

    Köşeli parantez içindeki sabit ifade, dizideki öğelerin sayısını belirtir. Aşağıdaki durumlarda bir dizi bildiriminden bir sabit ifade çıkarılabilir:

    Bildirildiğinde, bir dizi başlatılır,

    Bir dizi, biçimsel bir işlev parametresi olarak bildirilir,

    C yalnızca tek boyutlu dizileri tanımlar, ancak bir dizi öğesi bir dizi olabileceğinden, çok boyutlu dizileri de tanımlayabilirsiniz. Her bir sabit ifadenin kendi köşeli parantezleri içine alındığı, dizi tanımlayıcısını izleyen sabit ifadelerin bir listesi olarak resmileştirilirler.

    Köşeli parantez içindeki her sabit ifadesi, belirli bir dizi boyutundaki öğe sayısını tanımlar, bu nedenle iki boyutlu bir dizi bildirimi iki sabit ifade içerir, üç boyutlu bir dizi bildirimi üç içerir vb. C dilinde bir dizinin ilk elemanının 0 indeksine sahip olduğuna dikkat edin.

    int bir; /* a a a a a a matrisi olarak gösterilir */ double b; /* 10 çift elemanlı vektör */ int w = ( ( 2, 3, 4 ), ( 3, 4, 8 ), ( 1, 0, 9 ) );

    Son örnekte, w dizisi bildirildi. Kıvrımlı parantez içindeki listeler dizi dizgelerine karşılık gelir; parantez yoksa, başlatma yanlış yapılır.

    Bir dizinin bölümleri, diğer dillerde olduğu gibi C'de de kullanılabilir. yüksek seviye(PL1, vb.), ancak bölümlerin kullanımına bir takım kısıtlamalar getirilir. Kesitler, bir veya daha fazla çift köşeli parantez çıkarılarak oluşturulur. Köşeli parantez çiftleri yalnızca sağdan sola ve kesinlikle ardışık olarak bırakılabilir. Dizilerin bölümleri, kullanıcı tarafından geliştirilen SI dilinin işlevlerindeki hesaplama sürecinin organizasyonunda kullanılır.

    Bir işlevi çağırırken s yazarsanız, s dizisinin sıfır dizisi geçirilir.

    b dizisine atıfta bulunurken, örneğin b yazabilirsiniz ve dört öğeden oluşan bir vektör iletilir ve b'ye atıfta bulunmak, 3'e 4 boyutunda iki boyutlu bir dizi verir. bir vektör iletilecektir, çünkü bu, kullanım dizisi bölümlerine getirilen kısıtlamaya uymamaktadır.

    Bir karakter dizisi bildirme örneği.

    char str = "karakter dizisi bildirimi";

    Son öğe "\0" çıkış dizisi olduğundan, bir karakter hazır bilgisinde bir öğe daha olduğuna dikkat edin.

    1.2.7. yapılar

    Yapılar, işlevler dışında herhangi bir türden öğe içeren bileşik bir nesnedir. Homojen bir nesne olan dizinin aksine, bir yapı heterojen olabilir. Yapının türü, formun kaydıyla belirlenir:

    yapı (tanımlar listesi)

    Yapı en az bir bileşen içermelidir. Yapı tanımı aşağıdaki gibidir:

    veri türü tanımlayıcısı;

    burada veri tipi, tanımlayıcılarda tanımlanan nesneler için yapı tipini belirtir. En basit haliyle, tanımlayıcılar, tanımlayıcılar veya dizilerdir.

    Yapı ( çift x,y; ) s1, s2, sm; struct ( int yıl; char güvesi, gün; ) tarih1, tarih2;

    s1, s2 değişkenleri, her biri x ve y olmak üzere iki bileşenden oluşan yapılar olarak tanımlanır. Sm değişkeni, dokuz yapıdan oluşan bir dizi olarak tanımlanır. tarih1, tarih2 değişkenlerinin her biri üç bileşenden oluşur: yıl, güve, gün. >p>Bir adı bir yapı tipiyle ilişkilendirmenin başka bir yolu daha vardır, bu da yapı etiketinin kullanılmasına dayanır. Bir yapı etiketi, numaralandırılmış bir tür etiketine benzer. Yapı etiketi aşağıdaki gibi tanımlanır:

    yapı etiketi (açıklama listesi; );

    burada etiket bir tanımlayıcıdır.

    Aşağıdaki örnekte, tanımlayıcı öğrenci bir yapı etiketi olarak tanımlanmaktadır:

    Struct student ( karakter adı; int id, yaş; char prp; );

    Yapı etiketi, bu tür yapıları daha sonra şu biçimde bildirmek için kullanılır:

    yapı etiketi kimlik listesi;

    yapı öğrencisi st1,st2;

    Özyinelemeli yapıları tanımlamak için yapı etiketlerinin kullanılması gereklidir. Aşağıda özyinelemeli yapı etiketlerinin kullanımı açıklanmaktadır.

    Yapı düğümü ( int veri; yapı düğümü * sonraki; ) st1_node;

    Düğüm yapı etiketi, kendi açıklamasında kullanıldığı için gerçekten özyinelemelidir, yani. sonraki işaretçinin biçimselleştirilmesinde. Yapılar doğrudan özyinelemeli olamaz, yani bir yapı düğümü, yapı düğümü olan bir bileşen içeremez, ancak herhangi bir yapı, yukarıdaki örnekte yapıldığı gibi, türünü gösteren bir bileşene sahip olabilir.

    Yapı bileşenlerine, yapı adını ve ardından seçilen bileşenin adını bir noktayla ayırarak belirterek erişilir, örneğin:

    St1.name="Ivanov"; st2.id=st1.id; st1_node.data=st1.age;

    1.2.8. Dernekler (karışımlar)

    Birlik bir yapıya benzer, ancak herhangi bir zamanda birliğin unsurlarından yalnızca biri kullanılabilir (veya başka bir deyişle yanıt verilebilir). Birleşim türü aşağıdaki gibi belirtilebilir:

    Birlik ( öğe açıklaması 1; ... öğe açıklaması n; );

    Birliğin ana özelliği, beyan edilen öğelerin her biri için aynı bellek alanının tahsis edilmesidir, yani. örtüşüyorlar. Herhangi bir eleman kullanılarak bu hafıza alanına erişim mümkün olsa da, sonuç anlamsız olmayacak şekilde bu amaca yönelik eleman seçilmelidir.

    Bir birliğin üyelerine, yapılarla aynı şekilde erişilir. Birleşim etiketi, yapı etiketiyle tam olarak aynı şekilde biçimlendirilebilir.

    Dernek aşağıdaki amaçlar için kullanılır:

    Herhangi bir zamanda birçok nesneden yalnızca biri etkinse, kullanılan bellek nesnesini başlatma;

    Bir türdeki bir nesnenin altında yatan temsilin, nesneye farklı bir tür atanmış gibi yorumlanması.

    Birlik tipi değişkenine karşılık gelen depolama, birliğin en uzun üyesini barındırmak için gereken miktara göre belirlenir. Daha küçük uzunlukta bir öğe kullanıldığında, birleşim türü değişkeni kullanılmayan bellek içerebilir. Tüm birleşim öğeleri, aynı adresten başlayarak aynı bellek alanında saklanır.

    Birlik ( char fio; char adres; int vozrast; int telefon; ) bilgilendirmek; union ( int balta; char al; ) ua;

    Birleşim türünün bilgi nesnesini kullanırken, yalnızca değeri alan öğeyi işleyebilirsiniz, yani. inform.fio öğesine bir değer atadıktan sonra diğer öğelere atıfta bulunmak mantıklı değildir. Ua'yı birleştirmek, ayrı erişim iki baytlık ua.ax sayısının düşük ua.al ve yüksek ua.al baytlarına.

    1.2.9. Bit alanları

    Bir yapı öğesi, tek tek bellek bitlerine erişim sağlayan bir bit alanı olabilir. Bit alanları, yapıların dışında bildirilemez. Ayrıca bit alan dizilerini düzenleyemez ve alanlara bir adres arama işlemi uygulayamazsınız. Genel durumda, bit alanı olan bir yapının tipi aşağıdaki biçimde verilir:

    Yapı ( işaretsiz tanımlayıcı 1: alan uzunluğu 1; işaretsiz tanımlayıcı 2: alan uzunluğu 2; )

    uzunluk - alan bir tamsayı ifadesi veya bir sabit ile belirtilir. Bu sabit, karşılık gelen alana atanan bit sayısını belirtir. Sıfır uzunluklu bir alan, bir sonraki kelime sınırına hizalamayı gösterir.

    Yapı ( unsigned a1: 1; unsigned a2: 2; unsigned a3: 5; unsigned a4: 2; ) prim;

    Bitfield yapıları, imzalanmış bileşenler de içerebilir. Sözcüklerin bazı bitleri kullanılmadan kalabilirken, bu tür bileşenler otomatik olarak uygun sözcük sınırlarına yerleştirilir.

    1.2.10. Değişken yapıya sahip değişkenler

    Çoğu zaman, bazı program nesneleri, yalnızca bazı ayrıntılarda farklılık gösteren aynı sınıfa aittir. Örneğin, geometrik şekillerin temsilini düşünün. Şekillerle ilgili genel bilgiler, alan, çevre gibi unsurları içerebilir. Bununla birlikte, geometrik boyutlarla ilgili karşılık gelen bilgiler, şekillerine bağlı olarak farklı olabilir.

    Geometrik şekiller hakkındaki bilgilerin, yapı ve birleşimin birleşik kullanımına dayalı olarak temsil edildiği bir örneği ele alalım.

    Yapı şekli ( çift alan,çevre; /* ortak bileşenler*/ inttipi; /* bileşenin özniteliği */ birleşim /* bileşenlerin numaralandırılması */ ( çift yarıçap; /* daire */ çift a; /* dikdörtgen */ çift b; /* üçgen */ ) geom_fig; ) şekil 1, şekil 2 ;

    Genel olarak, şekil tipindeki her nesne üç bileşenden oluşacaktır: alan, çevre, tip. Tip bileşeni, aktif bileşenin etiketi olarak adlandırılır, çünkü geom_fig birleşiminin hangi bileşeninin aktif olduğunu belirtmek için kullanılır. şu an. Böyle bir yapıya değişken yapı denir, çünkü bileşenleri aktif bileşenin etiketinin değerine (türün değeri) bağlı olarak değişir.

    int türünün tür bileşeni yerine numaralandırılmış bir tür kullanmanın makul olacağını unutmayın. Örneğin, böyle

    Enum şekil_satranç ( CIRCLE, BOX, TRIANGLE ) ;

    CIRCLE, BOX, TRIANGLE sabitleri sırasıyla 0, 1, 2'ye eşit değerler alacaktır Değişken tipi, numaralandırılmış bir tipe sahip olarak ilan edilebilir:

    enum şekil_satranç türü;

    Bu durumda, C derleyicisi programcıyı potansiyel olarak hatalı atamalar konusunda uyaracaktır, örneğin:

    şekil.tip = 40;

    Genel olarak, bir yapı değişkeni üç bölümden oluşacaktır: bir dizi ortak bileşen, aktif bileşen etiketleri ve değişen bileşenlere sahip parçalar. Bir yapı değişkeninin genel biçimi aşağıdaki gibidir:

    Struct (ortak bileşenler; etkin bileşenin etiketi; union (bileşen bildirimi 1 ; bileşen bildirimi 2 ; ::: bileşen bildirimi n ; ) birleşim tanımlayıcısı; ) yapı tanımlayıcısı;

    helth_record adlı bir yapı değişkeni tanımlama örneği

    Yapı ( /* Genel bilgi*/ karakter ismi ; /* isim */ int yaş; /* yaş */ karakter cinsiyeti; /* cinsiyet */ /* aktif bileşen etiketi */ /* ( Aile durumu) */ numaralandırma merital_status ins; /* değişken kısım */ union ( /* tek */ /* bileşen yok */ struct ( /* evli */ char marripge_date; char eş_adı; int no_children; ) evlilik_bilgisi; /* boşanmış */ char tarih_boşanmış; ) evlilik_bilgisi ; ) sağlık kaydı; enum medeni durum ( SINGLE, /* bekar */ EVLİ, /* evli */ BOŞANMIŞ /* boşanmış */ ) ;

    Yapı bileşenlerine bağlantılar kullanılarak erişilebilir:

    helth_record.neme, helth_record.ins, helth_record.marriage_info.marriage_date .

    1.2.11. Nesneleri ve türleri tanımlama

    Yukarıda belirtildiği gibi, C programlarında kullanılan tüm değişkenlerin bildirilmesi gerekir. Bildirilen bir değişkenin türü, tür belirteci olarak hangi anahtar kelimenin kullanıldığına ve bildiricinin basit bir tanımlayıcı mı yoksa tanımlayıcının bir işaretçi (yıldız), dizi (köşeli parantezler) veya işlev (parantez) değiştirici ile bir kombinasyonu olup olmadığına bağlıdır. .

    Basit bir değişken, yapı, karışım veya birleşim veya numaralandırma bildirirken, tanımlayıcı basit bir tanımlayıcıdır. Bir işaretçi, dizi veya işlev bildirmek için, tanımlayıcı buna göre değiştirilir: solda bir yıldız, sağda kare veya parantez.

    C dilinin önemli bir özelliğine dikkat çekiyoruz, bildirirken, aynı anda birden fazla değiştirici kullanabilirsiniz, bu da birçok farklı karmaşık tip tanımlayıcı oluşturmayı mümkün kılar.

    Ancak, bazı değiştirici kombinasyonlarına izin verilmediğini unutmamalıyız:

    Dizi öğeleri işlev olamaz,

    İşlevler, dizileri veya işlevleri döndüremez.

    Karmaşık tanımlayıcıları başlatırken, kare ve parantezler (tanımlayıcının sağında) ​​yıldız işaretinden (tanımlayıcının solunda) önceliklidir. Kare veya parantezler aynı önceliğe sahiptir ve soldan sağa doğru genişler. Tip belirtici, tanımlayıcı tamamen yorumlandığında son adımda dikkate alınır. Gerektiğinde yorumlama sırasını değiştirmek için parantez kullanabilirsiniz.

    Karmaşık açıklamaların yorumlanması için, "içten dışa" gibi görünen ve dört adımdan oluşan basit bir kural önerilmektedir.

    1. Kimlikten başlayın ve köşeli parantez veya parantez için sağa bakın.

    2. Varsa, tanımlayıcının o kısmını yorumlayın ve ardından yıldız işareti için sola bakın.

    3. Sağda herhangi bir aşamada kapatma parantezi varsa, o zaman önce parantez içindeki tüm bu kuralları uygulamanız ve ardından yorumlamaya devam etmeniz gerekir.

    4. Tip belirleyiciyi yorumlayın.

    int * (* özet ) (); 6 5 3 1 2 4

    Bu örnekte, comp (1) değişkeni, işaretçileri (5) tamsayı değerlerine (6) döndüren işlevlere (4) on (2) işaretçiden (3) oluşan bir dizi olarak bildirilir.

    Karakter * (* (* var) ()) ; 7 6 4 2 1 3 5

    var (1) değişkeni, char değerlerine işaretçiler (6) olan 10 öğelik bir diziye (5) bir işaretçi (4) döndüren bir işleve (3) işaretçi (2) olarak bildirilir.

    Çeşitli türlerdeki değişkenlerin bildirimlerine ek olarak, türlerin bildirilmesi de mümkündür. Bu iki şekilde yapılabilir. İlk yol, bir yapı, birleşim veya numaralandırma bildirirken etiket adını belirtmek ve ardından bu adı değişken ve işlev bildirimlerinde o etikete referans olarak kullanmaktır. İkincisi, türü bildirmek için typedef anahtar sözcüğünü kullanmaktır.

    ile reklam yapıldığında anahtar kelime Tanımlanan nesnenin yerine duran tanımlayıcı olan typedef, dikkate alınan veri tipinin adıdır ve ayrıca bu tip, değişkenleri bildirmek için kullanılabilir.

    İşaretçi, işlev veya dizi türleri dahil olmak üzere herhangi bir türün typedef anahtar sözcüğü kullanılarak bildirilebileceğini unutmayın. İşaretçi, yapı, birleşim türleri için typedef anahtar kelimesiyle bir ad, bu türler tanımlanmadan önce bildirilebilir, ancak bildirici kapsamındadır.

    typedef double(*MATH)(); /* MATH - çift değer döndüren bir işleve işaretçiyi temsil eden yeni tür adı */ MATH cos; /* cos göstericisi double türünde değerler döndüren bir fonksiyon */ /* Eşdeğer bir bildirim yapılabilir */ double (* cos)(); typedef char FIO /* FIO kırk karakterlik bir dizidir */ FIO kişi; /* Kişi değişkeni kırk karakterlik bir dizidir */ /* Bu, */ char kişi ilan etmeye eşdeğerdir;

    Değişkenler ve türler bildirilirken burada tür adları (MATH FIO) kullanılmıştır. Ek olarak, tür adları diğer üç durumda kullanılabilir: biçimsel parametreler listesinde, işlev bildiriminde, tür dönüştürme işlemlerinde ve sizeof işleminde (tür dönüştürme işlemi).

    Temel, numaralandırma, yapı ve karışım türleri için tür adları, bu türler için tür belirleyicileridir. Dizi ve işlev işaretçisi türleri için tür adları, aşağıdaki gibi soyut tanımlayıcılar kullanılarak belirtilir:

    tür belirtici soyut bildirici;

    Soyut bir tanımlayıcı, bir veya daha fazla işaretçi, dizi veya işlev değiştiriciden oluşan, tanımlayıcısı olmayan bir tanımlayıcıdır. İşaretçi değiştiricisi (*) her zaman tanımlayıcıdaki tanımlayıcıdan önce belirtilir ve dizi ve işlev değiştiricileri () her zaman ondan sonra belirtilir. Bu nedenle, soyut bir tanımlayıcıyı doğru bir şekilde yorumlamak için, zımni tanımlayıcı ile başlamak gerekir.

    Soyut tanımlayıcılar karmaşık olabilir. Karmaşık soyut belirticilerdeki parantezler, tıpkı bildirimlerdeki karmaşık belirticileri yorumlarken yaptıkları gibi, yorumlama sırasını belirtir.

    1.2.12. Veri başlatma

    Bir değişken bildirildiğinde, tanıtıcıya bir başlatıcı eklenerek bir başlangıç ​​değeri verilebilir. Başlatıcı bir "=" işaretiyle başlar ve aşağıdaki biçimlere sahiptir.

    Biçim 1: = başlatıcı;

    Biçim 2: = ( liste - başlatıcılar );

    Biçim 1, temel türlerin ve işaretçilerin değişkenlerini başlatırken ve biçim 2'yi bileşik nesneleri başlatırken kullanılır.

    tol değişkeni "N" sembolü ile başlatılır.

    const uzun megabute = (1024 * 1024);

    Değiştirilemez megabute değişkeni, sabit bir ifadeye başlatılır ve bundan sonra değiştirilemez.

    statik int b = (1,2,3,4);

    İki boyutlu bir b tamsayı dizisi başlatılır; dizi öğelerine listeden değerler atanır. Aynı başlatma şu şekilde yapılabilir:

    statik int b = ( ( 1,2 ), ( 3,4 ) );

    Bir diziyi başlatırken bir veya daha fazla boyutu atlayabilirsiniz.

    statik int b == ".")
    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 karmaşıklığını ve yanlış değeri düzelterek yapılabilecek hataların 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, tanımının yapıldığı yerde başlatılmalıdır). 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);

    Bu türden bir değişkene, numaralandırma öğelerinden farklı bir değer atama (veya bunu bir işleve parametre olarak iletme) denemesi, bir 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 elemanları, 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, ca2 dizisinin boyutu 4'tür (dize değişmezlerinde 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 kalanlar 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 + j j;
    }

    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 şu iki şekilde verilmektedir:

    *(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ı tip. 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 değişmezi 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ı, parametrelerin sayısı ve/veya türleri 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; // ... );

    Veri tipleri

    Kesin olarak yazılmış bir dil olduğu için veri türleri C#'ta özel bir öneme sahiptir. Bu, tüm işlemlerin derleyici tarafından kesinlikle tip kontrolünden geçirildiği ve geçersiz işlemlerin derlenmediği anlamına gelir. Bu nedenle, güçlü yazım hataları ortadan kaldırmaya ve programların güvenilirliğini artırmaya yardımcı olur. Tip kontrolünü sağlamak için tüm değişkenler, ifadeler ve değerler belirli bir tipte olmalıdır. Bu programlama dilinde "tipsiz" değişken diye bir şey yoktur. Ayrıca, değer türü, üzerinde gerçekleştirilmesine izin verilen işlemleri belirler. Bir veri türü için izin verilen bir işleme başka bir veri türü için izin verilmeyebilir.

    C#'ta yerleşik veri türlerinin iki genel kategorisi vardır: değer türleri Ve referans türleri. Değişkenin içeriğinde farklılık gösterirler. Kavramsal olarak, ikisi arasındaki fark, bir değer türünün verileri doğrudan depolaması, bir referans türünün ise bir değere yapılan bir başvuruyu depolamasıdır.

    Bu türler, bellekte farklı konumlarda depolanır: değer türleri, yığın olarak bilinen bir alanda depolanırken, referans türleri, yönetilen yığın adı verilen bir alanda depolanır.

    Hadi bir bakalım değer türleri.

    tamsayı türleri

    C#, dokuz tamsayı türü tanımlar: char, bayt, sbyte, kısa, ushort, int, uint, uzun ve ulong. Ancak char türü esas olarak karakterleri temsil etmek için kullanılır ve bu nedenle ayrı olarak ele alınır. Kalan sekiz tamsayı türü, sayısal hesaplamalar içindir. Aşağıda, sayıların temsil aralığı ve bit derinliği verilmiştir:

    C# Tamsayı Türleri
    Tip CTS tipi Bit derinliği Menzil
    bayt Sistem Baytı 8 0:255
    sbyte System.SByte 8 -128:127
    kısa Sistem.Int16 16 -32768: 32767
    ushort System.UInt16 16 0: 65535
    int Sistem.Int32 32 -2147483648: 2147483647
    uint System.UInt32 32 0: 4294967295
    uzun System.Int64 64 -9223372036854775808: 9223372036854775807
    ulong System.UInt64 64 0: 18446744073709551615

    Yukarıdaki tabloda gösterildiği gibi, C#, çeşitli tamsayı türlerinin hem işaretli hem de işaretsiz sürümlerini tanımlar. İşaretli tamsayı türleri, bir tamsayının en önemli bitini yorumlama biçimleri bakımından işaretsiz benzerlerinden farklıdır. Örneğin, bir program işaretli bir tamsayı değeri belirtirse, C# derleyicisi tamsayının yüksek sıralı bitini işaret bayrağı olarak kullanan kod üretecektir. Bir sayı, işaret bayrağı 0 ise pozitif, 1 ise negatif olarak kabul edilir.

    Negatif sayılar hemen hemen her zaman, negatif bir sayının tüm ikili basamaklarının önce ters çevrildiği ve ardından bu sayıya 1 eklendiği, ikisinin tümleyen yöntemiyle temsil edilir.

    Muhtemelen programlamada en yaygın tamsayı türü int yazın. int türündeki değişkenler genellikle döngü kontrolü, dizi indeksleme ve genel amaçlı matematik için kullanılır. int türünden daha geniş bir sayı temsil aralığına sahip bir tamsayı değerine ihtiyacınız olduğunda, bu amaç için bir dizi başka tamsayı türü vardır.

    Bu nedenle, değerin bir işaret olmadan saklanması gerekiyorsa, bunun için seçebilirsiniz birim tipi, büyük işaretli değerler için - uzun tip ve büyük işaretsiz değerler için, tür ulong. Örnek olarak aşağıda Dünya'dan Güneş'e olan mesafeyi santimetre cinsinden hesaplayan bir program var. Bu kadar büyük bir değeri saklamak için, long türünde bir değişken kullanır:

    Sistemin Kullanılması; System.Collections.Generic kullanarak; System.Linq kullanarak; System.Text kullanarak; namespace ConsoleApplication1 ( class Program ( static void Main(string args) ( uzun sonuç; const uzun km = 149800000; // km olarak mesafe. sonuç = km * 1000 * 100; Console.WriteLine(sonuç); Console.ReadLine(); ) ) )

    Tüm tamsayı değişkenlerine ondalık veya onaltılık gösterimde değerler atanabilir. İkinci durumda, 0x öneki gereklidir:

    Uzun x = 0x12ab;

    Bir tamsayı değerinin int, uint, long veya ulong türünde olup olmadığı konusunda herhangi bir belirsizlik varsa, o zaman varsayılan kabul edilen int. Değerin başka hangi tamsayı türüne sahip olması gerektiğini açıkça belirtmek için, sayıya aşağıdaki karakterler eklenebilir:

    Uint ui = 1234U; uzun l = 1234L; ulong ul = 1234UL;

    U ve L küçük harflerle de yazılabilir, ancak küçük L görsel olarak 1 (bir) rakamıyla karıştırılabilir.

    kayan nokta türleri

    Kayan nokta türleri, sayıları kesirli bir kısımla temsil etmenizi sağlar. C#'ta iki tür kayan noktalı veri türü vardır: batmadan yüzmek Ve çift. Sırasıyla tek ve çift duyarlıklı sayısal değerleri temsil ederler. Böylece, kayan tip 32 bittir ve bu da yaklaşık olarak 5E-45 ila 3.4E+38 arasındaki sayıların gösterim aralığına karşılık gelir. Ve çift tipin bit derinliği, yaklaşık olarak 5E-324 ile 1.7E + 308 arasındaki sayıların temsil aralığına karşılık gelen 64 bittir.

    Float veri türü, daha az kesinlik gerektiren daha küçük kayan nokta değerleri içindir. Double veri türü, kayan veri türünden daha büyüktür ve daha yüksek bir kesinlik derecesi (15 bit) sunar.

    Tamsayı olmayan bir değer kaynak kodunda sabit kodlanmışsa (örneğin, 12.3), bu durumda derleyici genellikle double türünde bir değerin kastedildiğini varsayar. Bir değerin kayan nokta olarak belirtilmesi gerekiyorsa, ona bir F (veya f) karakteri eklemeniz gerekir:

    Float f = 12.3F;

    Ondalık veri türü

    Yüksek hassasiyetli kayan noktalı sayıları temsil etmek için bir ondalık türü de sağlanmıştır. ondalık, finansal hesaplamalarda kullanılmak üzere tasarlanmıştır. Bu tip, 1E-28 ile 7.9E+28 arasında değişen sayısal değerleri temsil edecek şekilde 128 bit genişliğindedir. Muhtemelen normal kayan nokta aritmetiğinin ondalık yuvarlama hatalarına eğilimli olduğunu biliyorsunuzdur. Bu hatalar, sayıların 28 (ve bazen 29) ondalık basamağa kadar hassasiyetle temsil edilmesini sağlayan ondalık türü kullanılarak ortadan kaldırılır. Ondalık değerleri yuvarlama hatası olmadan gösterebildiğinden, bu veri türü özellikle finansal hesaplamalar için kullanışlıdır:

    Sistemin Kullanılması; System.Collections.Generic kullanarak; System.Linq kullanarak; System.Text kullanarak; namespace ConsoleApplication1 ( class Program ( static void Main(string args) ( // *** *** // *** sabit getiri oranı*** ondalık para ile bir yatırımın maliyetini hesaplama, yüzde; int i; const bayt yıl = 15 ; para = 1000.0m; yüzde = 0.045m; for (i = 1; i

    Bu programın çıktısı şöyle olacaktır:

    Semboller

    C#'ta karakterler, C++ gibi diğer birçok programlama dilinde olduğu gibi 8 bitlik bir kodla değil, adı verilen 16 bitlik bir kodla temsil edilir. Unicode (Unicode). Unicode'da karakter seti o kadar geniştir ki dünyadaki hemen hemen her doğal dilden karakterleri kapsar. İngilizce, Fransızca ve Almanca da dahil olmak üzere birçok doğal dil, nispeten küçük alfabelerle karakterize edilirken, Çince gibi bazı diğer diller, 8 bitlik bir kodla temsil edilemeyen oldukça büyük karakter kümeleri kullanır. Bu sınırlamanın üstesinden gelmek için C# tanımlar char yazın 0 ile 65,535 arasındaki işaretsiz 16 bitlik değerleri temsil eden A. Ancak standart 8 bitlik ASCII karakter seti, Unicode'un 0 ile 127 arasındaki bir alt kümesidir. Bu nedenle, ASCII karakterleri C#'ta hala geçerlidir.