|
Örnek 1 Açıklaması :
Öncelikle bu faktoriyel fonksiyonu for ile nasıl yapılıyordu bir hatırlayalım f20 double faktoriyel (int n) f21 { f22 double cevap = 1.0; f23 if(!n || n==1) f24 return cevap; f25 for( ; n<1 ; ) f26 cevap *= n--; f27 return cevap; f28 }
Yukarıda f25 ve f26 satırlarının yerine while ile bu işlemi 22 ve 23. satırlarda gerçekleştirdik.
Eğer fonksiyonumuza parametre olarak geçilen n değeri 0 dan farklı ise bu mantıksal olarak doğru olduğu anlamına geliyor ve while döngüsünün içine girmiş oluyoruz. 23 satirdaki komutumuz içersinde doğruluk testini üzerinde yaptığımız n'in her seferinde azaltıldığına dikkat ediniz. Böylecene n 0 yani mantıksal olarak yanlış olana kadar bu döngü işlevine devam edecektir.
Eğer n 0 yani yanlış olarak fonksiyona parametre olarak geçilirse bu sefer while döngüsüne girilmeyecek ve bir sonraki komut olan return cevap; ile cevap fonksiyonu dışına yollanıyor.
Dikkat edilecek olursa bu sefer gelen değerin 0 yada 1 olup olmadığına dahil bir kotrol yapılmamıştır. Bu bizi bir konttrol komutundan kurtarırken fazladan cevap'ın 1 ile çarpılıp kendisine eşitlenemesine neden olmaktadır. Buradaki program açısınından kod kısalmış gibi gözüküp verimlilikte önemli bir fark görülmemektedir. Ancak normalde kodunuzu olabildiğince anlaşılır ve açık yazmanız, ve açıklamalarla desteklemeniz kodunuzun okunabilirliğini arttıracak ve uzun zaman sonra koda tekrara baktığınızda zorluk çekmemenizi sağlayacaktır
do-while : do - while'ın kullanımının while'ın kullanımından tek farkı blok içersindeki komutların durum testi doğruda yanlışta olsa en az bir defa icra edilmesidir. Kullanmı aşağıki gibidir.
do-while kullanımı : do [komut]; while ( [durum testi] ); ya da do { [komut]; [komut]; . . . }while ( [durum testi] );
işlencek komut sırası do'ya geldiği zaman derleyici bundan sonraki komutun döngünün başı olduğunu belirliyor ve hiç bir kontrol yapmadan komutun işlemeye başlıyor. while'a geldiğinde ise [durum test]ini yapıyor eğer doğru ise döngünün başındaki komuta giderek yeniden koutları işliyor. Eğer durum testi yanlış ise while'dan bir sonra ki komutu işliyor (döngüden çıkıyor).
durum testi en başta yanlış olsa bile komutlar en az bir kere işlenmiş oluyor Operatörler Nedir? Önce matematikte operatörün ne olduğunu bir inceleyelim.. Operatörler üzerinde işlem yaptıkları bir veya birden fazla operandı belli kurallara göre işlerler. Operand işleme maruz kalandır. Örneğin; a + b 'de a ve b operand + ise operatördür. Matamatikte işlemler operatörler ve operandlar ile anlatılır.
Bilgisayardada yapılacak işlemleri belli operatörler tarafından gerçekleştirmekteyiz. Türleri: Operatörleri kullanım alanlarına ve kullanımdaki yerlerine (syntaxlarına) göre iki ayrı şekilde açıklamak mümkün iken, anlatımımızda kullanım türlerine göre sınıflandıracağız ve syntaxların göre aldıkları isimleride her operatör altında inceleyeceğiz. Kullanım Alanlarına göre operatörler:: Aritmatiksel operatörler + , - , * , / , % , ++ , -- Karşılaştırma operatörleri < , > , <=, >= , ==, != Bit bazında işlem yapan operatörler : & , | , ^ , ~ , << , >> Eşitleme operatörleri : = , += , -=, *= , /= , %= , <<=, >>=, &=, |= , ^= Mantıksal Operatörler : ! , || , && Diğer Operatörler :
Aritmatik Operatörler Nelerdir? Aritmatik operatörler matamatiksel işlemlerde kullanılan operatörlerdir. Toplama, çıkarma, çarpma , bölme, modül alma, ... Şimdi bu operatörlere sıra ile göz atalım.
Aritmatik operatörlerin kullanımında dikkat edilecek hususlardan bir tanesi operatörün kullandığı operandlar ve değerin atandığı operand aynı türden olursa hassasiyetin korunacağı aksi taktirde sonucun doğru çıkmayabileceğidir. Bunu bize çocukken öğrettikleri şekilde anlatacak olursak elmalarla armutlar toplanmaz.
Toplama Operatörü + Bu operatör ile verilen iki veya daha fazla operand toplanabilir.Genel yazılışı aşağıdaki gibidir değişken1] + [değişken2] Eğer bu iki değişkenin toplamı sonucunda oluşan değeri sonuç isimli bir değişkene atamak istersek eşitleme operatörünüde kullanarak bu işlemi aşağıdaki gibi yazabiliriz. [sonuç] = [değişken1] + [değişken2] Sonuç'u tutacak olan değişken değişken1 ve değişken2 değişkenlerinin değerlerini tutabilecek büyüklükte olmalıdır. Bu konu ile ilgili örneğimizi aşağıda görebilirsiniz. Bu örneğin 32 bitlik bir platform için yazıldığına dikkat ediniz. Veri tiplerinin maksimum ve minumum değerlerini görmek için buraya basınız.
Örnek 1: 01 #include "iostream.h"
02
03 void main()
04 {
05 int sayi1 = 2000000000;
06 int sayi2 = 1500000000;
07 int sonuc = sayi1 + sayi2;
08 double sonuc2 = double(sayi1) + double(sayi2);
09
10 cout << sonuc2 << endl;
11 cout << sayi1 + sayi2 << endl;
12 }
Örnek 1 Ekran Çıktısı : 3.5e+009 -794967296
Örnek 1 İçin Açıklama : 32 bitlik bir platformda int türünden en büyük sayı 2,147,483,647 olabileceğinden sayi1 ve sayi2 nin toplamları bu değeri geçtiğnden dolayı sonucu tutacağımız değişkenin fazladan 1 bite ihtiyacı vardır. Ancak int ile hafızada tutulan bu alana sonuc büyük geldiğinden sadece sonucun yeterli bitleri int'e aktarılabilmiştir. Bu da -794967296'ya denk gelmektedir. Böyle durumlarda yapmamız gereken değişkenleri daha geniş alanlı bir yapıya aktarıp sonucuda bu değeri tutabilecek bir değişkene aktarmaktır. sayi1, sayi2 ve sonuç16 bitlik verilerdir. Ancak 3500000000 17 bitle gösterilebilir. Sonuç ise 16 bitlik olduğundan bu değeri tutamaz. İşte bunun için sonuç2 32 bitlik double olarak tanımlanmış ve 08. satırda sayi1 ve sayi2 de 32 bitlik olarak toplanıp sonuç2'ye aktarilmiştir. Böylecene sonuç doğru olarak çıkabilmektedir
Çıkarma Operatörü -: Bu operatör ile verilen iki operand birbirinden çıkarılabilir. Genel yazılışı aşağıdaki gibidir. [değişken1] - [değişken2] Eğer bu iki değişkenin çıkarılması sonucunda oluşan değeri sonuç isimli bir değişkene atamak istersek eşitleme operatörünüde kullanarak bu işlemi aşağıdaki gibi yazabiliriz. [sonuç] = [değişken1] - [değişken2]
Çarpma Operatörü *: Bu operatör ile verilen operandlar birbiriyle çarpılabilir. Genel yazılışı aşağıdaki gibidir. değişken1] * [değişken2] Eğer bu iki değişkenin çarpılması sonucunda oluşan değeri sonuç isimli bir değişkene atamak istersek eşitleme operatörünüde kullanarak bu işlemi aşağıdaki gibi yazabiliriz. [sonuç] = [değişken1] * [değişken2]
Bölme Operatörü / : Bu operatör ile verilen operandlar birbirine bölünebilir. Genel yazılışı aşağıdaki gibidir. [değişken1] / [değişken2] Eğer bu iki değişkenin çarpılması sonucunda oluşan değeri sonuç isimli bir değişkene atamak istersek eşitleme operatörünüde kullanarak bu işlemi aşağıdaki gibi yazabiliriz [sonuç] = [değişken1] / [değişken2] Bölme işleminin sonucundaki hassasiyet değişkenlerden en hassası ile orantılıdır. Yani 2 int değişkeninin sonucu int'tir. int / double 'ın sonucu double'dır vs...
Modül Alma Operatörü % Bu operatör ile birinci operandın modül ikinic operandı alınır. Genel yazılışı aşağıdaki gibidir. [değişken1] % [değişken2] Sonuçun ta oluşan değeri sonuç isimli bir değişkene atamak istersek eşitleme operatörünüde kullanarak bu işlemi aşağıdaki gibi yazabiliriz [sonuç] = [değişken1] % [değişken2]
0' a göre modül alma yada 0'a bölme programın çalışması sırasında hataya yol açar.
++ Operatörü operandın değerini bir arttırmak amacıyla kullanılır. İki şekilde kullanılabilir. Birincisinde operandın önüne yazılır. ++[değişken1] Bu durumda değişkenin değeri önce arttırılır. Daha sonra işlenir.
İkinci türde ise operanddan sonra yazılır değişken1]++ Bu durumda değişken önce işlenir. Sonra değeri arttırılır.
-- Operatörü operandın değerini bir azaltmak amacıyla kullanılır. İki şekilde kullanılabilir. Birincisinde operandın önüne yazılır. -[değişken1] Bu durumda değişkenin değeri önce azaltılır. Daha sonra işlenir.
İkinci türde ise operanddan sonra yazılır değişken1]-- Bu durumda değişken önce işlenir. Sonra değeri azaltılır.
Örnek 2 : 01 #include "iostream.h"
02
03 void main()
04 {
05 int s1 = 20;
06 int s2 = 15;
07 cout << "s1 = " << s1 << endl;
08 cout << "s2 = " << s2 << endl;
09
10 cout << "++s1 * s2-- = " << ++s1 * s2-- << endl;
11
12 cout << "s1 = " << s1 << endl;
13 cout << "s2 = " << s2 << endl << endl;
14
15 cout << "s1-- " << s1-- << endl;
16 cout << "s1 = " << s1 << endl;
17 cout << "++s1 " << ++s1 << endl;
18 cout << "s1 = " << s1 << endl << endl;
19
20 cout << "5%3 = " << 5 % 3 << endl << endl;
21
22 cout << "8 - 3 * 4 + 5 / 7 = " << 8 - 3 * 4 + 5 / 7 << endl;
23 cout << "8.0 - 3.0 * 4.0 + 5 / 7.0 = " << 8.0 - 3.0 * 4.0 + 5 / 7.0 << endl;
24 }
Örnek 2 Ekran Çıktısı : s1 = 20 s2 = 15 ++s1 * s2-- = 315 s1 = 21 s2 = 14
s1-- 21 s1 = 20 ++s1 21 s1 = 21
5%3 = 2
8 - 3 * 4 + 5 / 7 = -4 8.0 - 3.0 * 4.0 + 5 / 7.0 = -3.28571
Örnek 2 Açıklama: Dikkat edilecek olunursa 10. satırda 21 ile 15 çarpılmştır. Yani ++ s1'in önünde olduğundan dolayı s1 çarpma işleminden önce arttırılmıştır. -- s23'den sonra olduğundan dolayı s2 çarpma işleminden sonra azaltılmıştır.Bir başka deyişle s2 önce işlenmiş sonra azaltılmıştır. Bir diğer dikkat edilecek nokta ise 22 ve 23. satırlardadır. 22 satırdaki işlemde hassasiyet int seviyesinde olduğundan 5 / 7 0 olarak işlenmiştir. 23. satırda ise seviye noktalı sayılar seviyesinde olduğundan 0'dan farklı bir değer almıştır
|
|
|
|
|
Logged
|
|
|
|
| TRForumcu.NeT | Bilgi ve Paylaşım Platformu |
|
Karşılaştırma Operatörleri Nelerdir? Karşılaştırma operatörleri karşılaştırma işlemlerinde kullanılan operatörlerdir. İki operandı birbirleriyle karşılaştırılırlar. Eğer karşılaştırma doğru ise 1 yanlış ise 0 döndürürler. Şimdi bu operatörlere sıra ile göz atalım.
> Operatörü
Kullanımı: [operand1] > [operand2] Eğer operand1 operand2'den küçük ise 1 döndürür. Aksi taktirde 0 döndürür.
< Operatörü
Kullanmı: [operand1] < [operand2] Eğer operand1 operand2'den büyük ise 1 döndürür. Aksi taktirde 0 döndürür.
>= Operatörü
Kullanımı [operand1] >= [operand2] Eğer operand1 operand2'den küçük yada eşit ise 1 döndürür. Aksi taktirde 0 döndürür.
<= Operatörü Kullanımı [operand1] <= [operand2] Eğer operand1 operand2'den büyük yada eşit ise 1 döndürür. Aksi taktirde 0 döndürür.
== Operatörü
Kullanımı [operand1] == [operand2] Eğer operand1 operand2'den eşit ise 1 döndürür. Aksi taktirde 0 döndürür.
!= Operatörü
Kullanımı [operand1] != [operand2] Eğer operand1 operand2'den eşit değil ise 1 döndürür. Aksi taktirde 0 döndürür. Bit Bazında İşlem Yapan Operatörler Nelerdir? Bu operatörler bit bazında işlemler gerçekleştirir. Bir bit'in değeri ya 0 yada 1'dir. Şimdi bu operatörlere sıra ile göz atalım.
& Operatörü Mantık kapılarındaki ve (and) kapısı ile aynı işlemi gerçekleştirir.
Operand1 Operand2 Sonuç 1 1 1 1 0 0 0 1 0 0 0 0
Kullanımı : [operand1] & [operand2] Eğer iki operand'da 1 ise sonuç 1'dir. Diğer durumlar için sonuç 0'dır
I Operatörü Mantık kapılarındaki veya (or) kapısı ile aynı işlemi gerçekleştirir. Operand1 Operand2 Sonuç 1 1 1 1 0 1 0 1 1 0 0 0
Kullanımı : [operand1] | [operand2] Eğer iki operand'dan biri 1 ise sonuç 1'dir. Her ikiside 0 ise sonuç 0'dır
^ Operatörü Mantık kapılarındaki XOR kapısı ile aynı işlemi gerçekleştirir.
Operand1 Operand2 Sonuç 1 1 0 1 0 1 0 1 1 0 0 1
Kullanımı : [operand1] ^ [operand2] Eğer iki operand farklı ise sonuç 1 dir. İki operatör aynı ise sonuç 0'dır.
~Operatörü Operand1 Sonuç 1 0 0 1
Kullanımı : ~[operand1] 1 değeri 0 ; 0 değeri 1 olur.
<< Operatörü Sola kaydırma (left shift) operatörü: Kaydırma operatörünün her iki operandıda int türündene olmalıdır.
Kullanımı : [operand1] < < [operand2] Yukardaki işlem operand1 * 2*operand2'ye eşittir. 101001 şeklinde bir bit dizisi sola kaydırıldığında sonuç 010010 olur.
>> Operatörü Sağa kaydırma (right shift) operatörü.
Kullanımı : [operand1] >> [operand2] Yukardaki işlem operand1 / 2*operand2'ye eşittir. 101001 şeklinde bir bit dizisi sağa kaydırıldığında sonuç 010100 olur. Eşitleme Operatörleri Nelerdir? Bir operandı diğerine eşitleyen yada operandın üzerinde işlem gerçekleştirdikten sonra eşitleyen operatörlerdir. Şimdi bu operatörlere sıra ile göz atalım.
= Operatörü İki operandı birbirine eşitler. Kullanımı : [operand1] = [operand2] operand1'e operand2'nin değeri atanır.
+= Operatörü Kullanımı : [operand1] += [operand2] operand1'e operand1+operand2'nin değeri atanır.
-= Operatörü Kullanımı : [operand1] -= [operand2] operand1'e operand1-operand2'nin değeri atanır.
*= Operatörü Kullanımı : [operand1] *= [operand2] operand1'e operand1*operand2'nin değeri atanır.
/= Operatörü Kullanımı : [operand1] /= [operand2] operand1'e operand1/operand2'nin değeri atanır.
%= Operatörü Kullanımı : [operand1] %= [operand2] operand1'e operand1%operand2'nin değeri atanır.
>>= Operatörü Kullanımı : [operand1] >>= [operand2] operand1'e operand1>>operand2'nin değeri atanır.
<<= Operatörü Kullanımı : [operand1] <<= [operand2] operand1'e operand1<<operand2'nin değeri atanır.
&= Operatörü Kullanımı : [operand1] &= [operand2] operand1'e operand1!operand2'nin değeri atanır.
!= Operatörü Kullanımı : [operand1] != [operand2] operand1'e operand1!operand2'nin değeri atanır.
^= Operatörü Kullanımı : [operand1] ^= [operand2] operand1'e operand1^operand2'nin değeri atanır. Mantıksal Operatörler <Resim><Resim> Nelerdir? Bu operatörler mantıksal durumlarda durumları birleştirmeye yarar. Şimdi bu operatörlere sıra ile göz atalım.
<Resim>! Operatörü <Resim><Resim> Kullanımı :
![operand1]
Eğer operand1'in tersi durum anlamına gelir.
Örnek
if( !operand1)
{
// Bu blok operand1'in tersi durumlarda icra edilecektir.
}
Eğer operand1'in tersi durum anlamına gelir.
<Resim>&& Operatörü <Resim><Resim>
Kullanımı :
operand1] && [operand2]
Eğer operand1 ve operand2 doğru ise
Örnek
if( operand1 && operand2)
{
/* Bu blok operand1 ve operand2'nin doğru
olduğu durumlarda icra edilecektir.
*/
}
Eğer operand1'in tersi durum anlamına gelir.
<Resim>|| Operatörü <Resim><Resim>
Kullanımı :
operand1] || [operand2]
Eğer operand1 veya operand2 doğru ise
Örnek
if( operand1 || operand2)
{
/* Bu blok operand1 ve operand2'den biri doğru
olduğu durumlarda icra edilecektir.
*/
} Diğer Operatörler <Resim><Resim> Nelerdir? Şu ana kadar gördüğümüz operatörler C dilinde de bulunan operatörlerdi. Şimdi ise diğerlerinden farklı tarzdaki ve C++ ile gelen operatörlere göz atacağız. Bunlardan C++'a ait olan operatörler C++ konularının içine girdikçe daha detaylı olarakta anlatılacaktır. Bundan dolayı şu anda bu operatörlerin isimlerini verip geçeceğiz
:: Operatörü Scope Resolution operatörü. Fonksiyonlar konusuna göz atınız.
* Operatörü Yönlendirme operatörü. Göstergeç (pointer)'larla birlikte kulanılıyor.
& Operatörü Adres operatörü. Göstergeç (pointer)'larla birlikte kulanılıyor.
& Operatörü Referans operatörü. Fonksiyonlar konusuna göz atınız.
new, delete ve sizeof Operatörleri Fonksiyonlar konusuna göz atınız.
. yada -< Operatörleri üye seçimi . Sınıflar konusuna göz atınız Pointerlar <Resim><Resim>
Genel Olarak Pointerlar Tanımlama ve Kullanım Şekilleri Pointer Aritmetiği Tip Dönüşümleri Fonksiyonların Referans İle Çağırılması Fonksiyon Pointerlar Genel Olarak Pointerlar <Resim><Resim> Pointerlar en yalın şekilde değer olarak bir hafıza adresini tutan değişkenler olarak tanımlanır. Genelde bir değişkenlerin adreslerini içerirler ve bu değişkenlerin değerlerine dolaylı olarak ulaşılmasını sağlarlar. Başlıca kullanım alanları dinamik olarak büyüyen veri yapılarının oluşturulması (linked list (bağlı liste), kuyruk (queue), yığın (stack) gibi), fonksiyonlara referans ile değer geçilmesidir. Büyük veri yapılarının işlenmesinde performansı arttırmak için bu verilerin adresleri üzerinde işlem yapılması için kullanılırlar.
Pointerlar C ve C++ ‘ın ayrılmaz bir parçasıdır. C ve C++ da pointer kullanmadan program yazmak düşünülemez. Bu kadar sık kullanılmasın yanıda en sık hata yapılan ve en zor anlaşılan kısımlarından biridir. Pointer kullanımında yapılan hataların bulunması genelde zordur ve sistemin kitlenmesine neden olur.
<Resim>Program yazarken programı çalıştırmadan önce yaptıklarınızı kaydetmeyi alışkanlık edinin. Özellikler C/C++ da program yazarken makinanın kitlenmesiyle sıkça karşılaşacaksınız. Saatlerce uğraşıp yazdığınız kodun kaybolması hiç te hoş birşey değil.
Pointer Operatörleri Pointerlardan bahsedilince hemen aklımıza *, & operatörleri gelir. & operatörü kendisinden sonra gelen ifadenin adresini bulur yani ".. nin adresi" olarak ifade edebiliriz. Örneğin &val ifadesi “val’in adresi” anlamına gelir. * operatörü ise kendisinden sonra gelen pointer’in gösterdiği değeri refreans eder. Yani “ .. nin gösterdiği adresteki değer” olarak ifade edilebilr. Örneğin *pVal ifadesi “pVal’in gösterdiği adresteki değer” olarak ifade edilebilir. Tanımlama ve Kullanım Şekilleri <Resim><Resim> Şu ana kadar pointerlardan ve pointerlar ile birlikte anılan *, & operatörlerinden kısaca bahsettik Şimdi artık C++’da bahsettiğimiz bu kavramların nasıl kullanıldıklarından bahsedeceğiz. Basit bir örnek ile işe başlıyalım.
#include <iostream.h>
main()
{
int val = 5;
//integer val değişkeni için hafıza yer ayarla ve 5 değerini ata.
int *pVal;
//integer pointer pVal değişkeni için hafızada yer ayır.
pVal = &val
//pVal değişkenine val değişkeninin adresini ata.
*pVal = 8;
//pVal değişkenin gösterdiği hafıza alanına 8 değerini ata
cout >> "val değiskenin adresi = " >> &val
cout >> "pVal değiskenin adresi=" >> &pVal
cout >> "val değiskenin değeri = " >> val;
cout >> "pVal değiskenin değeri =" >> pVal;
cout >> "pVal değiskenin gösterdiği yerdeki değer=" >> *pVal;
};
Yukarıdaki örnek program pVal ve val değişkenlerinin adreslerini ve içlerindeki değerleri ekrana yazıyor Örnek 2.5.1
Programın ekran çıktısı ise aşağıdaki gibi.
val degiskenin adresi = 0x13df2914 pVal degiskenin adresi=0x13df2910 val degiskenin degeri = 8 pVal degiskenin degeri =0x13df2914 pVal degiskenin gösterdigi yerdeki deger=8
Ekran çıktısından da görüldüğü üzere pVal değişkeni hafızada ayrı bir yer ayrılmış durumda ve bu ayrılan yere program içerisinde val değişkenin adresi atanıyor. val değişkeni ile direkt olarak hiç bir işlem yapılmamasına rağmen val değişkenini dolaylı olarak gösteren pVal değişkenini gösterdiği adrese 8 değeri atandığında val değişkenin içeriği de değişti. Hafızanın durumunu grafik olarak gösterirsek Başlangıçta aşagıdaki gibidir. pVal değişkenin gösterdiği adresteki bilgi hakkında bir fikrimiz yok.
<Resim>C ve C++'da değişkenlere otomatik olarak atanan bir ilk değer yoktur.Değişken tanımlandığında değişkene anlamlı bir değer atamak bizim sorumluluğumuzdadır. Bir pointer değişkenine anlamlı bir adres bilgisi atamadan değişkenin gösterdiği adrese değer atamak genellikler sistemin askında kalması , kitlenmesi veya başka bir programın hata vermesine neden olur.Sadece global ve statik değişkenlere derleyici tarafından ilk değer atanır
pVal 0x13df2910 val 0x13df2914 5
val değişlenin adresi pVal değişkenine atanıyor.
pVal 0x13df2910 0x13df2914 val 0x13df2914 5
pVal değişkenin gösterdiği hafıza alana 5 atanıyor.
pVal 0x13df2910 0x13df2914 val 0x13df2914 8
<Resim>Pointer değişkeninin kendisi de hafızadan belli bir bölge işgal eder. pointer değişkenini adresi ile göstediği adres birbiri ile karıştırılmamalıdır Pointer Aritmetiği <Resim><Resim> Pointerların içerisindeki verinin normal değişkenlerin içindeki verilerden farklı yorumlandığını önceki konularda yukarıda gördük. Aynı durum pointerlar üzerinde yapılan aritmetik işlemlerde de geçerlidir. Öncelikle pointerlar üzerinde toplama , çıkarma ve karşılaştırma işlemlerinin yapılabildiğini belirtelim. Fakat mod alma, bölme, çarpma gibi işlemler pointer değişkenler üzerinde yapılamaz. Aritmetik işlem operatörlerinin pointerlar üzerindeki etkileri normal değişkenlerin üzerindekinden farklıdır. Örnek olarak aşağıdaki kodu inceliyelim.
#include <iostream.h>
main()
{
int *pIntVar, intVar = 5;
pIntVar = &intVar
cout>> "intVar =" >> intVar>> endl;
cout>> "pIntVar =" >> pIntVar>> endl;
cout>> "(intVar + 1) =" >> (intVar + 1)>> endl;
cout>> "(pIntVar + 1) =" >> (pIntVar + 1)>> endl;
};
Örnek 2.5.2
Yukarıdaki program biri integer diğeri ise integer pointer olmak üzere iki tane değişken tanımlar. Bunlardan integer olana 5 değerini pointer olana ise integer değişkenin değerinin atar. Bu değerleri sırası ile ekrana yazdırır. Daha sonra her iki değişkenin içlerindeki değerlerin bir fazlasını ekrana yazdırır. Programın ekran çıktısı aşağıdaki gibidir
intVar =5
pIntVar =0x359728b2
(intVar + 1) =6
(pIntVar + 1) =0x359728b4
intVar ve (intVar + 1) ifadelerinin değerleri kıyaslanırsa (intVar + 1) ifadesinin değerinin intVar ifadesinden bir fazla olduğu görülür. Buraya kadar herşey normal fakat pIntVar ve (pIntVar + 1) ifadelerini kıyaslarsak (pIntVar + 1) ifadesinin pIntVar değişkenin değerinden iki fazla olduğunu görürüz. Peki bunun nedeni nedir? Niden bir, üç, veya dört değil de iki ? Pointer değişknelerinin tanımını yaparken pointerların bir değişkenin adresini tutuğunu söylemiştik. Integer bir pointer hafızadaki bir integer’ın bulunduğu adresi tutar. Bu adres bilgisini bir arttırmak bir sonraki integer’ın bulunduğu adrese gitmek demektir. Bizim oluşturduğumuz proje 16 bitlik program üretiyor dolayısıyla integer’ın büyüklüğü 2 byte olarak kabul ediliyor. Yani bir sonraki integer’ın bulunduğu adrese ulaşmak için pointer değişkenimizin içerdiği değer iki arttırılıyor. Aynı durum (- )operatörü için de geçerlidir. (-) operatöründe ise bir önceki integer’ın adresine konumlanılmaya çalışılacaktır dolayısıyla iki azaltılacaktır.
Olayı genelleştirirsek herhangi bir tipte tanımlanmış bir pointer’ın değerini belli bir m değeri kadar arttırmak değişkenin değerinde (pointer’in tanımlı olduğu tipin uzunluğu) * m kadar bir artıma sebep olur.
Pointer aritmetiği diziler üzerinde yapılan işlemlerde sıkça kullanılırlar. pA ve pB iki pointer değişken olsun eğer pA ve pB aynı dizinin elemanlarını gösteriyorlarsa pA-pB işlemi anlamlı olur . pA nın dizinin i. elemanını pB’nin ise dizinin j. elemanımı gösterdiğini varsayarsak pA-pB = i-j olur.
include
main()
{
int* pA, *pB;
pA = new int[1];
pB = new int[1];
*pA = 5;
*pB = 150;
cout>> "pA pointer'inyn gösterdiği hafiza bölgesi ---< pA =" >> pA >> endl;
cout>> "pB pointer'inın gösterdiği hafiza bölgesi ---< pB =" >> pB >> endl;
cout>> "pA pointer'inın gösterdiği yerdeki değer ---< *pA =" >> *pA >> endl;
cout>> "pB pointer'inın gösterdiği yerdeki değer ---< *pB =" >> *pB >>endl;
pA = pB;
cout>> "pA pointer'inin gösterdiği yerdeki değer ---< *pA =" >> *pA >>endl;
cout>> "pB pointer'inin gösterdiği yerdeki değer ---< *pB =" >> *pB >>endl;
cout>> "pA pointer'inın gösterdiği hafiza bölgesi ---< pA =" >> pA >> endl;
cout>> "pB pointer'inın gösterdiği hafiza bölgesi --< pB =" >> pB >> endl;
delete []pA;
delete []pB;
);
|
|
|
|
|
Logged
|
|
|
|
| TRForumcu.NeT | Bilgi ve Paylaşım Platformu |
|
Örnek 2.5.3
Yukarıdaki örnek program pointerlarda sıkça yapılan bir hatayı göstermektedir. Program pA ve pB isminde iki integer pointer tanımlayıp bunlara dinamik olarak tanımlanan birer integer’ın adresilerini atıyor. Daha sonra pA’nın gösterdiği adrese 5, pB’nin gösterdiği adrese ise 150 değerleri atanıyor. Buraya kadar herşey normal.
Fakat pA = pB satırı çok masumane bir atama işlemi gibi görünmesine karşın sıkça bir hataya karşılık geliyor.
Bu komut çalışınca ne olur ? pA değişkeni de pB’nin gösterdiği adresi göstermeye başlar. Güzel fakat pA’nın gösterdiği dinamik olarak ayırdığımız yerin adresi neydi ? O adrese bir daha nasıl ulaşabiliz? Ulaşamayız. Hafızadan dinamik olarak ayırdığımız o bölge kayboldu. Dinamik olarak ayırıp pA’ya atadığımız hafıza bölgesi bilgisayar kapana kadar kayıp olarak kalacak. Durun daha bu kadarla da bitmedi bu masumame atmama komutu başımıza daha neler açacak.
Dinamik yer ayırmaktan bahsettikya. Dinamik olarak ayırdığımız her yeri işimiz bittiğinde serbest bırakmalıyız ki bir süre sonra sistem, sistem kaynakları çok düstü şeklinde bir uyarı vermesin. Kodu incelemeye devam edersek en son iki komut dinamik olarak ayrılan hafızayı serbest bırakıyor. Fakat dikkatli bakılırsa bu iki komutun başımıza ne gibi büyük sorunlar açacağını görebiliriz. delete []pA
komutu pA
değişkeninin gösterdiği hafıza bölgesini serbest bırakmaya çalışıyor. Burada sorun yok gerçekten de bu bölgeyi program başında dinamik olarak ayırdık. Fakat pB pointer’ı da aynı adresi gösteriyordu o da aynı bölgeyi serbest bırakmaya çalışacak. Ama zaten orası serbest bırakıldı. O bölge artık bize ait değil ki bu durumda ne olacak. ? Kim bilir ? Büyük bir ihtimalle makinamız kitlenecektir. Buradan çıkaracağımız sonuç pointer değişkenleri kullanırken dikkatli olmamız gerektiğidir
<Resim>Özellikle atama işlemlerinde dikkatli olmalıyız. Pointer’ın gösterdiği hafıza bölgesine değer atamaya çalışırken yukarıdaki programda olduğu gibi pointer’ın değerini değiştire biliriz. Bu da bulunması zor hatalara sebep olur.
pA pointer'inın gösterdiği hafiza bölgesi ---< pA =0x39570366
pB pointer'inın gösterdiği hafiza bölgesi ---< pB =0x3957035e
pA pointer'inın gösterdiği yerdeki değer ---< *pA =5
pB pointer'inın gösterdiği yerdeki değer ---< *pB =150
pA pointer'inın gösterdiği yerdeki değer ---< *pA =150
pB pointer'inın gösterdiği yerdeki değer ---< *pB =150
pA pointer'inın gösterdiği hafiza bölgesi ---< pA =0x3957035e
pB pointer'inın gösterdiği hafiza bölgesi ---< pB =0x3957035e
<Resim>Pointerların kullanımında sıkça yapılan diğer bir hata ise pointer değişkenine bize ait olduğundan emin olduğumuz geçerli bir hafıza adresi atamadan pointer’ın gösterdiği adrese bilgi yazılmasıdır.
Bir fonksiyon içinde kullanılan yerel değişkenler otomatik olarak yaratılır. Bu şekilde tanımlanan değişkenlere derleyici tarafından bir ilk değere verilmez, bu değişken pointer tipindeyse gösterdiği adresin ne olacağı önceden kestirilemez. Bu değer işletim sisiteminin bulunduğu bölgelerden birinin adresi olabileceği gibi o an için kullanılmayan bir hafıza bölgesinin adresi de olabilir. Dolayısıyla ilk değer atanmamış pointer (global ve statik değişkenlere derleyici tarafından ilk değer atanır) değişkenlerinin gösterdiği hafıza bölgesine değere atama işleminin davranışı belirsizdir. Hiç bir sorun çıkmayayabileceği gibi makinanın kitlenmesine de sebep olabilir. Kimbilir? Tip Dönüşümleri <Resim><Resim> Pointerlar önceki konularda da ifade edildiği gibi bir hafıza adresinin değerini tutarlar. Pointerların tanımlandıkları tip gösterdikleri yerde bulunan veri tipi ile doğrudan alakalıdır. Aşağıdaki örnek program pointerların tipleri ile gösterdikleri veriler arasındaki ilişkiyi net bir şekilde ortaya koymaktadır.
#include <iostream.h>
main()
{
int i= 5;
double d = 1213;
int *pInt = &i
cout>> "int i değişkenin değeri i = ">> i>>endl;
cout>> "int i değişkenin değeri i = *pInt = ">> *pInt>>endl;
cout>> "double d değişkenin değeri d = ">> d>>endl;
pInt = (int *)&d
cout>> "double d değişkenin değeri d = *pInt = ">> *pInt>>endl;
cout>> "double d değişkenin değeri d = *(double *)pInt = ">> *(double *)pInt>>endl;
};
Yukarıdaki programda öncelikle int, double ve int pointer tiplerinde birer tane değişken tanımlanıyor. pInt int pointer değişkenine i değişkenin adresi atanıyor. Sırası ile i değişkeninin değeri ile pInt değişkenin gösterdiği adresteki değer ekrana yazdırılıyor. pInt değişkeni int bir pointer olarak tanımlandı ve aksi belirtilmediği için derleyici pInt değişkenin dösterdiği yerde bir int değer bulunduğunu kabul edip pointer değişkeninin gösterdiği adresten itibaren bir int 'in uzunluğundaki hafıza bloğunun içindeki değeri int 'e çevirir. Daha sonraki satırlarda pInt değişkenine double tipindeki t değişkeninin adresi atanıyor. (Bu işlemi yaparken derleyiciye d double değişkenin adresinin bir int değişkenin adresi olduğunu bildirerek hata vermesinin engelliyoruz. Artık mesuliyeti tamamen üzerimize almış durumdayız.) Ekrana sırası ile d değişkenin, pInt değişkenini gösterdiği adresteki değeri ve pInt değişkenin gösterdiği adresteki double değişkenin değerleri sırası ile yazdırılıyor. Aşağıdaki ekran çıktısında da görüldüğü gibi pInt değişkenin d değişkenin adresini göstermesine rağmen derleyiciyi uyarmadığımız için derleyici pInt in gösterdiği adresten itibaren bir int boyutu kadarlık alandaki değeri int 'e çevirir. d değişkenin içerdiği değerin hafızadaki gösteriminde bizim değerlendirdiğimiz kısmında 0 değeri bulunuyormuş. pInt değişkeni d değişkenin adresigi gösteriyor dolayısıyla gösterdiği adresteki değerin değişkenin içindeki değer ile aynı olmalı gibi bir yorum yapılabilir Pointer aritmetiği konusunda da belirttiğimiz gibi derleyici pointerlar üzerinde işlem yaparken değişkenin tip bilgisinden yararlanıyor. Yukarıda int int tipindeki bir pointer'a double tipinde bir değişkenin adresini atarken yaptığımız gibi derleyici tekrar kandırıp sen int tipinde bir pointersın fakat gösterdiğin adres bir double 'ın adresi ona göre davran diye biliriz. Aşağıdaki ekran çıktısında da görüldüğü gibi pInt değişkenini double pointer' a cast edip ekrana yazdırdığımızda doğru değeri yazıyor.
int i değişkenin değeri i = 5
int i değişkenin değeri i = *pInt = 5
double d değişkenin değeri d = 1213
double d değişkenin değeri d = *pInt = 0
double d değişkenin değeri d = *(double *)pInt = 1213 Fonksiyonları Referans İle Çağırılması <Resim><Resim> Fonksiyonlara üç değişik şekilde parametre geçilebilir. Değer geçilerek (Call By Value), referans parametreleri ile referans geçerek veya pointer parametreler ile referans geçerek. Bizi burada ilgilendiren pointer tipinde referans geçilmesi ve değer geçerek parametre yollama yöntemi ile arasındaki fark. Aşağıdaki program her iki yöntemin de kullanımını göstermektedir.
include <iostream.h>
void DegerIleCagirma(int parametre)
{
parametre = 8;
}
void PointerReferansIleCagirma(int* parametre)
{
*parametre = 8;
}
main()
{
int i = 100;
cout>> "i = " >> i >> endl;
DegerIleCagirma(i);
cout>> "DegerIleCagirma(i) fonksiyonundan sonra i = " >> i >> endl;
PointerReferansIleCagirma(&i);
cout>> "PointerRefreransIleCagirma(i) fonksiyonundan sonra i = " >> i >> endl;
}
Örnek 2.5.4 Programda main fonksiyonundan başka iki fonksiyon daha bulunmakta. . DegerIleCagirma fonksiyonu parametre olarak bir int alır . Bu çağırım şeklinde derleyici parametrenin otomatik olarak yerel bir kopyasını çıkartır ve parametrenin değerinin atar. Fonksiyon içinde parametre üzerinde yapılan işlemler aslında yerel kopya üzerinde gerçekleştirilir. Foksiyondan çıkılırken tüm yerel değişkenler yok edildiği için parametreler üzerinde yaptığımız değişiklikler yok olur. Diğer fonksiyon ise PointerReferansIleCagirma parametre olarak int tipinde bir pointer alır. Bu fonksiyon çağırıldığında integer pointer tipinde yerel bir değişken oluşturulur ve parametere olarak geçilen değişken adresi bu yerel değişkene atanır.Fonksiyon çıkışında yerel değişkenlerin yokedilmesine karşın yaptığımız değişiklik kaybolmamış olur.Fonksiyon içinde yerel değişkenin gösterdiği hafıza bölgesine 8 değeri atanır. Programın ekran çıktısı aşağıdaki
i = 100
DegerIleCagirma(i) fonksiyonundan sonra i = 100
PointerRefreransIleCagirma(i) fonksiyonundan sonra i = 8
Fonksiyonların pointer referans ile çağırılması çok büyük veri yapılarının foksiyonlara parametre olarak geçilmesi gerektiğinde oldukça büyük avantajlar sağlar. Aşağıdaki program parçasını ele alalım. Kartvizit yapısı 337 byte büyüklüğündedir. F1 ve F2 fonksiyonlarının aynı işi yapan iki farklı fonksiyon olduğunu varsayalım. F2 fonksiyonu F1 fonksiyonundan daha hızlı çalışır? Neden ? F1 fonksiyonu cağırıldığında Kartvizit tipindeki parametrenin bir kopyası yaratılır ve 337 byte’lık bilgi bu yeni yapıya kopyalanır. Halbuki F2 fonksiyonu çağrıldığında Kartvizit pointer tipinde bir parametre yaratılır ve sadece parametere olarak geçilen yapının adresi kopyalanır. Bu adres bilgisinin büyüklüğü kullanılan hafıza modeline göre değişir fakat her durumda 337 byte’tan çok ufaktır.
struct Kartvizit{
char ad[30];
char soyad[30];
char adres[255];
char evTel[11];
char isTel[11];
};
void F1(Kartvizit kart)
{
……….
}
void F2(Kartvizit *kart)
{
……….
} Fonksiyon Pointerlar <Resim><Resim> Foksiyon pointerlar genellikle geneleştirilmiş algoritmalar elde etmek için kullanılırlar. Mesala sıralama yapan bir fonksiyon yazdığımızı düşünelim. Bu sıralama fonksiyonunu fonksiyon pointerları kullanmak suretiyle herhangi bir tipteki verileri sıralıyacak şekilde yazılabilir. Sıkça kullanıldıkları diğer bir alan ise menülerdir. Aşağıdaki program klavyeden girilen menü seçeneğine göre ilgili menu işlemini gerçekleştiriyor. Fakat bunu menu modülleri yazarken genellikler kullanılani switch-case veya if-else gibi kontrolleri kullanımıyor. Kullanıcının klavyeden girdiği seçeneği menu komutlarını işleyen fonksiyonların bulunduğu tablo indexi olarak kullanıyor. Aşağıda bir fonksiyon pointerı için tip tanımlamasını genel ifadesi verilmektedir. typedef geri_döndürdüğü_değer_tipi (*tip_ismi)(parametre_Listesi);
<Resim>Menüler için fonksiyon pointerları dizi kullanıdığımızda çalışma zamanında (run time) dizideki adres değerlerini değiştirmek suretiyle menüleri düzenliyebiliriz
#include <iostream.h>
#include <conio.h>
struct Ogrenci{
char Ad[20];
char Soyad[20];
char OkulNo[20];
char Adres[255];
char KayitTarihi[11];
};
void EkranaYaz(Ogrenci &ogr)
{
cout>>"Ad:">>ogr.Ad>>endl;
cout>>"Soyad:">>ogr.Soyad>>endl;
cout>>"OkulNo:">>ogr.OkulNo>>endl;
cout>>"Adres:">>ogr.Adres>>endl;
cout>>"KayitTarihi">>ogr.KayitTarihi>>endl;
}
void Oku(Ogrenci &ogr)
{
cout>>"Ad:";
cin<<ogr.Ad;
cout>>"Soyad:";
cin<<ogr.Soyad;
cout>>"OkulNo:";
cin<<ogr.OkulNo;
cout>>"Adres:";
cin<<ogr.Adres;
cout>>"KayitTarihi";
cin<<ogr.KayitTarihi;
}
typedef void (*MenuFuncPointer)(Ogrenci &);
MenuFuncPointer islemler[2];
void Menu()
{
clrscr();
cout>> "0-) Oku">> endl;
cout>> "1-) Yaz">> endl;
cout>>endl;
cout>> "2-) Çıkış">> endl;
}
void main()
{
islemler[0] = Oku;
islemler[1] = EkranaYaz;
Ogrenci temp;
int secim = 4;
while (secim != 2)
{
Menu();
cin<< secim;
if (secim < 2 ) (*islemler[secim])(temp);
}
}
Yukarıdaki örnek programda geriye değer döndürmeyen ve Ogrenci yapısı referansı tipinde parametre alan fonksiyonlar için MenuFuncPointer isminde bir fonksiyon pointer tipi tanımlanıyor. Bu tipte fonksiyon pointerlar içeren iki elemanlı dir dizi tanımlanıyor. Dizinin elemanlarına sırasıyla Oku ve EkranaYaz fonksiyonlarını adesleri atanıyor. Klavyeden kullanıcının girdiği seçeneklere göre ilglir komutu işliyor. Diziler <Resim><Resim> Genel Olarak Diziler
Diziler İle Pointerlar Arasındaki İlişki
Dizilerin Fonksiyonlara Parametre Olarak Geçilmesi
Diziler İle Pointerlar Arasındaki İlişki
Çok Boyutlu Dizler Genel Olarak Dizler <Resim><Resim>
Diziler aynı isim ile erişilen değişkenler kümesi olarak adlandırılabilir. Dizinin her elemanına index bilgisi ile ulaşılır. Genel olarak bir dizi tanımlaması aşağıdaki gibidir. Tip dizi_ismi[boyut1][boyut2]….[boyutN] Tip Kullanıcı tanımlı veya standart C++ veri tiplerinden bir. dizi_ismi C++ değişken tanımlama kurallarına uygun olan herhangi bir değişken ismi boyut Dizinin kaç eleman içereceği C’deki dizi kavramı diğer dillerdekinden biraz farklıdır. Örneğin basic de Dim I as integer Input("Bir sayi giriniz") ,I Dim d(I) as integer Şeklinde bir program kodu gayet doğaldır. Yukarıdaki programda d dizisinin boyutu dışarıdan girilir. Dizinin boyutu çalışma zamanında belirlenir. Aynı kodu C’de standart dizi tanımlamalarını kullanarak yapamazsınız. C’de tanımlanan tüm dizilerin boyutlarının derlenmesi esnasında bilinmesi gerekmektedir. Derleyici diziler için statik olarak hafızadan yer ayırır. C’ de boyutu önceden bilinmeyen diziler tanımlamak mümkün değil mi? sorusu gündeme gelebilir. Mümkündür. Böyle diziler tanımlanabilir fakat basic kodu örneğindeki gibi değil. Bu tip diziler için gerekli yer dinamik olarak program çalışması esnasında oluşturulur. Bu yönteme daha sonra deyineceğiz.
Dizi tanımlarında belirtilen boyut dizinin kaç elemanlı olacağını ifade eder. Bazı dillerdeki gibi son elemanın indisini belirtmez. Aşağıda 5 elemanlı bir int dizi tanımlaması verilmiştir. Geçerli olan son dizi elemanı iArray[4]’ 'tür. iArray[5] ifadesi dizinin ardındaki ilk hafıza elemanını gösterir. int iArray[5]; C’de dizilerin boyut kontrolü yoktur. Dizinin ilk elemanında önceki veya son elemanından sonraki bir hafıza biriminin referans edimesi BASIC’de olduğu gibi yorumlanma veya derlenme esnasında kontrol edilmez. Bu kontrol programcıya bırakılmıştır.
Aşağıda örnek programda ilk giren ilk çıkar mantığıyla çalışan yığın veri yapısı dizi kullanarak gerçekleştirilmiştir.
#include <iostream.h>
#include <stdlib.h>
#include <conio.h>
#include <stdio.h>
const int Max_StackLen = 100; // Yy?ynda buluna bilecek max eleman sayysy
int intStack[Max_StackLen]; // Yy?yn olarak kullanylacak dizi
int itemsInStack = 0; // Yy?ynda bulunan eleman sayysy
int Menu();
int Push(int number);
int Pop(int &number);
main()
{
while(1)
{
switch (Menu())
{
case 1: // Yeni bir sayı ekle
{
int numberToAdd;
cout>> "Eklenecek Sayiyi Giriniz";
cin<<numberToAdd;
if (!Push(numberToAdd))
cout>> "Yigin Dolu">>endl;
}
break;
case 2: // Tepedeki elemany çykart
{
int popedNumber;
if (!Pop(popedNumber))
cout>> "Yigin Bos">>endl;
else
cout >> "Çykarylan Eleman :">>popedNumber>>endl;
}
break;
case 3: // Listele
{
for (int i = itemsInStack - 1; i >= 0 ; i--)
cout>>i>>". Pozisyon dayi Sayi :">>intStack>>endl; }
break;
case 4: // Çyky?
exit(0);
break;
}
}
return 0;
}
int Menu()
{
int choice = 0;
while (choice >1 || choice < 4)
{
cout >> "1-) Yeni Sayy Ekle">>endl;
cout >> "2-) Sayy Çykart">>endl;
cout >> "3-) Yy?un'y Listele">>endl;
cout >> "4-) Çyky?">>endl>>endl;
cout >> " Seçenek :";
cout.flush();
cin<<choice;
};
cout>>endl;
cout.flush();
return choice;
}
int Push(int number)
{
// Yy?ynda yer olup olmady?yny kontrol et.
if (itemsInStack == Max_StackLen)
return 0;
intStack[itemsInStack] = number;
itemsInStack++;
return -1;
}
int Pop(int &number)
{
// Yy?yn bo? mu diye kontrol et.
if (itemsInStack == 0)
return 0;
itemsInStack--;
number = intStack[itemsInStack];
return -1;
}
<Resim>C/C++ dillerinde derleyici tarafından dizlere erişimde boyut kontrolu yapılmamaktadır
<Resim>Peki ilk giren ilk çıkar mantığıyla çalışan Kuyruk veri yapısını diziler aracılığıyla nasıl gerçekleştirebiliriz? Çözümlerinizi bekliyoruz.Cevaplarınızı Linklerin Görülmesine İzin Verilmiyor Linki Görebilmek İçin Üye Ol veya Giriş Yap adresine yollayabilirsiniz. Diziler İle Stringler Arasındaki İlişki <Resim><Resim> C/C++’da stringler son elemanları “null terninator” ( ‘\0’ )’) olan belirli uzunluktaki karakter dizileri olarak tanımlanır. Tanımdan da anlaşılacağı üzere Stringler ile bir boyutlu diziler arasında çok sıkı bir bağ vardır. C dilinde stringler için bir veri tipi tanımlanmamasına karşın string sabitleri için bir veri tipi vardır. Bir string sabiti çift tırnak arasında verilmiş karakterler listesidir.
"Örnek bir string " String sabitlerinin sonuna null terminator eklenmesine gerek yoktur. C derleyicisi bizim yerimize otomatik olarak bu işi yapar.
char str[] = "Örnek bir string"; char str[17] = "Örnek bir string"; Yukarıdaki değişken tanımlamalarının her ikisi de geçerlidir. Birinci tanımlamada derleyici str str isminde “Örnek bir string” ifadesini tutabilecek kadar uzunluğa sahip karakter dizisi tanımlar. İkinci tanımlamada ise 17 elamandan oluşan bir karakter dizisi tanımlar ve bu diziye “Örnek bir string” ifadesini atar. İki tanımlamada ilk bakışta aynı işi gerçekleştiriyormuş gibi gözükebilir fakat işleyiş şekillerinde ufak bir nüans farkı vardır. Birinci kulanım şeklinde verdiğiniz string'i tutmnak için gerekli olan yerin uzunluğunu siz hesaplamıyorsunuz. Dolayısıyla ilerde bir şekilde bu string’i değiştirmeniz gerektiğinde ayrılması gereken yerin doğru olarak hesaplanıp hesaplanmadığını kontrol etmeniz gerekmez. Kısacası bir riske girmiyorsunuz kontrolu derleyiciye bırakıyorsunuz. Fakat ikinci tanımlamada aynı durum söz konusu değildir. Eğer ki tanımlamada verilen string 17 karakterden daha fazla yere ihtiyaç duysaydı veya ilerde string'i değiştirip yerine daha uzun bir string girdiğinizde derleyici hata verecekti.
char str[3] = "123456"; Örneğin derleyici yukarıdaki tanımlama ile karşılaştığında hata verecektir.
Sizi ayırmanız gereken yerin boyutunu doğru girmeye zorlayacaktır. C programa dili stringler üzerinde işlem yapabilmek için çok çeşitli foksiyonlar içermektedir. Bu fonksiyonların prototipleri string.h header dosyasında bulunmaktadır
<Resim>Programlarınızda mümkün sihirli rakamlar kullanmaktan kaçının. Sihirli rakamlar yerine #define ile tanımlamış ifadeler veya sabitler kullanın. Bu programınızın anlaşıla iblirliğini arttırırken bakımını da kolaylaştırır.
char mesaj[255];
Yukarıdaki program satırı yerine aşağıdaki kodu kullanın
const int Max_MesajUzunlugu = 255; char mesaj[Max_MesajUzunlugu]; Dizilerin Fonksiyonlara Parametre Olarak Geçilmesi <Resim><Resim> C dilinde diziler fonksiyonlara daima referans olarak geçilirler. Diğer değişken tiplerinde olduğu gibi değer ile çağırma yöntemi diziler için kullanılamaz. Aşağıda fonksiyonlara dizilerin parametre olarak geçilme şekillerinden bazılarını inceliyeceğiz. Print(char dizi[]); Yukarıdaki tanımlama Print fonksiyonuna bir karakter dizinin referansının (Bir string’in başlangıç adresinin) geçileceğinin anlatır.Tanımlamada parametre olarak geçilen dizinin boyutu hakkında bir bilgi bulunmamaktadır. Bir boyutlu bir dizini referansını elde etmek için dizinin boyutunun bilinmesine gerek yoktur.
|
|
|
|
|
Logged
|
|
|
|
| TRForumcu.NeT | Bilgi ve Paylaşım Platformu |
|
İkinci bir çağırma şekli ise direk olarak dizinin tanımlandığı tipte bir pointer’ı fonksiyona parametre olarak geçileceğini ifade eder.. Aşağıda bu kullanım şekli örneklenmektedir. Print(char *diziBaslangici); Bu kullanım şeklinde ise fonksiyonumuz bir char pointer alacak şekilde tanımlanmıştır. Bu tanımlama şeklide yukarıdaki ile aynı işlevi gerçekleştirmekterdir. Print(char dizi[35]); Yukarıdaki tanımlama şekli ise yine Print fonksiyonun char bir dizinin referansını parametre olarak alacağını belirtmektedir. Dizinin boyut bilgisi derleyici tarafından göz ardı edilir çünkü yukarıda da belirttiğimiz gibi dizinin başlangıcına ait bir referans elde etmek için dizinin boyutunun bilinmesine gerek yoktur. Bu bilgi ancak kodu inceleyen programcıya bilgi verebilir.
<Resim>Dizileri fonksiyonlara adreslerinin geçilmesi fonksiyon içinde dizi üzerinde yapılcak değişiklerin dizini içeriğini değiştireceğinin unutmamak gerekir. Yapılan değişiklikler yerle bir dizi kopyası üzerinde gerçekleştirilmez. Orjinal dizi değişir. Diziler İle Pointerlar Arasındaki İlişki <Resim><Resim> C programlama dilinde pointerlar ile diziler arasında çok yakın bir ilişki vardır. Herhangi bir dizinin ismi dizinin ilk elemanına ait bir pointer olarak tanımlanır.
int main(int argc, char **argv)
{
int array[5];
cout>>"Bir değer atanmadan önce array[0] = ">> array[0]>>endl;
*array = 2;
cout>>"Değer atanma işleminden sonra array[0] = ">> array cout>>"array[0]'in adresi (&array[0]) =" >>& array cout>>"array'in değeri array =" >> array >>endl;
getch();
return 0;
}
Yukarıdaki örnek program diziler ile pointerlar arasındaki ilişkiyi açık bir şekilde göstermektedir. Kodu basamak basamak incelersek array isminde 5 elemandan oluşan bir int dizi tanımlanıyor. Bu dizinin ilk elemanının değeri ekrana yazdırılıyor. Sonraki adımda ise array Yukarıdaki örnek program diziler ile pointerlar arasındaki ilişkiyi açık bir şekilde göstermektedir. Kodu basamak basamak incelersek array isminde 5 elemandan oluşan bir int dizi tanımlanıyor. Bu dizinin ilk elemanının değeri ekrana yazdırılıyor. Sonraki adımda ise array değişkeninin gösterdiği adrese 2 değeri atanıyor ve array array dizisinin ilk elemanı tekrar ekrana yazdırılıyor.
Aşağıdaki ekran çıktısına bakarsak ilk eşitleme işleminden önce array[0] ]’ın 0 değerine sahipken dizini isminin gösterdiği adrese değer 2 atanmasında sonra array[0] ]’ın değerinin 2 olduğunu görüyoruz. Buradan da anlaşılacağı üzere array ifadesi array dizisinin ilk elemanını göstermektedir. Sonraki satırlarda ise dizinin ilk elemanının ve dizinin ismimin gösterdiği adresler sırası ile ekrana yazdırılmaktadır.
Bir değer atanmadan önce array[0] = 0
Değer atanma işleminden sonra array[0] = 2
array[0]'in adresi (&array[0]) =0066FDF0
array 'in değeri array =0066FDF0
Pointerlar konusunda da söylediğimiz gibi bir pointer değişkenin değerinin arttırmak suretiyle pointer’ın tanımlandığı tipteki bir sonraki elemanın adresine ulaşabiliriz. Diziler, dizinini boyutu kadar elemanın dizinin başlangıç adresinden itibaren hafızada ardışık oluşturulması şeklinde elde edilirler. Dizinin i. Elemanı i+1’inci elemandan hemen önce gelmektedir. Pointer aritmetiği suretiyle dizi elemanlarınları üzerinde gezine biliriz. Aşağıdaki örnek programda 5 boyutlu bir int dizisinin elemanları hem dizi değişkeninin üzerinde dizi index’i aracılığıyla hem de dizinin başlangıç adresini gösteren bir pointer değişkenine çeşitli artımlar verilmek suretiyle ekrana yazdırılmaktadır.
int main(int argc, char **argv)
{
int array[5] = {0,1,2,3,4};
int *pInt = array;
// Dizi elemanlaryna de?er
for (int i = 0;i < 5; i++)
cout>>"array[">>i>>"]=">>array>>endl;
cout>>"Pointer değişken aracılığıyla dizi üzerinde dolaşılması">>endl;
for (int i = 0;i < 5; i++)
cout>>"*(pInt + ">>i>>")=">>*(pInt + i)>>endl;
getch();
return 0;
}
array[0]=0
array[1]=1
array[2]=2
array[3]=3
array[4]=4
Pointer değişken aracılığıyla dizi üzerinde dolaşılması
*(pInt + 0)=0
*(pInt + 1)=1
*(pInt + 2)=2
*(pInt + 3)=3
*(pInt + 4)=4 Çok Boyutlu Diziler <Resim><Resim> Bu bölümde birden çok boyutu olan dizileri ele alacağız. Çok boyutlu diziler en basit haliyle iki boyutlu diziler olarak karşımıza çıkar. İki boyutlu dizileri; bir boyutlu dizilerden oluşan bir boyutlu diziler olarak da ifade edilebilir. char strArray[30][255]; Yukarıdaki komut satırında herbiri 255 byte uzunluğunda 30 stringden oluşan strArray isiminde bir dizinin tanımı yapılmıştır Yukarıdaki ifade aşağıdaki tanımlamalar ile eşdeğerdir. typedef char myString[255]. NewType;
newType strArray[30]; Çok boyutlu dizilerde her bir boyut dizi elamnalrına erişlimde ekstra yük getirecektir. Yani aynı boyutlarda çok boyutlu bir dizini elemanlarına erişmek tek boyutlu diziye göre daha yavaş olacaktır.
Çok boyutlu dizlerini fonksiyonlara parametre olarak geçilmelerinde dizini her bşr boyutunun beliritilmesi gerekmektidir. Sadece en soldaki indisi belirtmeme şansına sahibiz.
double dizi[12][13][14]; Şeklinde tanımlanmış bir diziyi parametre olarak alacak fonksiyonun prototipi aşagıdaki gibi olacaktır. bool myFuntion(dizi[12][13][14]); veya bool myFuntion(dizi[][13][14]);
|
|
|
|
|
Logged
|
| | |