BETTER LATE THAN NEVER
Completed counter: 19/61
-
Tiobe indeksine gore en populer dil
C
dilidir, onu sirasiylaPython
,Java
,C++
veC#
takip etmektedir. -
C dilini niteleyen genel kriterler soyle isimlendirilebilir:
Turkce Ingilizce Buyurgan Imperative Prosedurel Procedural Mulkiyeti Yok Non-proprietary Orta seviyeli Middle-level Statik tur kavramina sahip Static typing Genel amacli General-purpose Verimli Efficient Yapay Artificial Standart Standart Tasinabilir Portable Ifade gucu yuksek Expressive -
Imperative vs Declarative: Imperatif diller hangi sonucu elde edecegini degil nasil yapildigini anlatmaya yoneliktir. Temel yapi taslari degiskenlerdir. Deklaratif dillerde nasili degil nereye ulascagini soyleyip sonuc aliriz, mesela veri tabanindan ismi Ahmet olan kisilerin sorgulanmasinda, bu sorgunun nasil yapildigi onemli degilken, Imperative dillerde butun bu surecin de anlatilmasi gerekir. C programlama dili imperative'dir.
-
C Programlama dili prosedurel'dir. Buna paradigma denir nesne yonelimli programlama, fonksiyonel programlama gibi.
-
Python
gibi diller hem nesne yonelimli hem fonksiyonel paradigmayi desteklerken, bu konuda en yetkin dilC++
'dir.C++
cogu paradigmayi destekler. -
Prosedurel programlama ana programi kucuk parcalara ayirma bu parcalari programlama ve bu kucuk parcalarin islenmesi olarak yorumlanabilir:
Functional decomposition
. -
cppreference.com
C dilinde calisirken kullanilacak en kiymetli dokuman ve referans kaynagidir. -
C dili
c89
,c99
gibi standartlara sahiptir. Dilin kurallarini belgeler. Bu dokumanISO
dokumanidir. -
C dilinin mulkiyet hakki herhangi bir kurum ya da kurulusta degildir.
-
C dili orta seviyeli bir dil oldugu icin hem insan hem makine diline yakindir.
-
C icin
portable assembly
denir, assemly kadar islemciye yakin ancak assembly gibi islemciye bagli da degildir. C makine diline yeterince yakin ve insana da uzak degildir. -
C dilini assembly koduna ceviren programlara derleyici -
compiler
denir. -
Derleyiciler oyle gelismislerdir ki kodu assembly'de degil C'de yazip derleyici ile assembly'e cevirmek daha verimlidir:
optimizing compilers
. -
Bi dilde yazilan dili baska dile cevirmeye
translator
denir. Eger yuksek seviyeli dilden dusuk seviyeli dile donusum yapiliyorsa bunacompile
-derleme
yapana dacompiler
-derleyici
denir. C'den assembly'e donusumu yapan programa bu yuzden compiler - derleyici denir. -
GCC
GNU C Compiler demektir. Zaman icinde genisleyip bir cok farkli dili desteklemistir, bundan sonra adi GNU Compiler Collection olarak yeniden anlamlandirilmistir. -
C dili genel amacli bir dildir belirli bir domain'e ozgu tasarlanmamistir. Oyun da yazilabilir, simulasyon da yapilabilir. Cogunlukla verimin kritik oldugu yerlerde ve sistem programlamada kullanilir.
-
Insanin konusmasina yakin programlama dillerine dogal programlama dilleri denir, bu sekilde yazilmamis olanlara ise yapay dil denir. C yapay bir programlama dilidir. Insana anlatilir gibi yazilmaz kendi yapay dilini kullanir.
+
,-
,*
,while
gibi kendine ait ifadelere sahiptir. -
C++
ismiC
dilinin increment++
operatorunden gelir. C'nin guclu ozelliklerini merkeze alip, nesne yonelimli programlama gibi C'nin desteklemedigi diger bir cok programlama paradigmalarini eklemeler yaparak tasarlanmistir. -
Backword compability
eski kodlarin degismis kurallara ragmen guncel derleyicilerde derlenebiliyor olmasidir. C dilinde bu destek cok yuksektir.
- Nested Looplarda loopun kapali parantezinin sonuna yorum olarak hangi loopa ait oldugu yazilabilir.
- Name-Lookup: ilgili degiskenin belirli aralikta definition’inin taranmasi.
- Fonksiyonun tanimi linkeri ilgilendiren, derlemede linker asamasinda hata verdiren bir durumdur.
- Fonksiyon bildirimi(fonksiyon declaration) fonksiyondan once gelebilir, derleyicinin parameter vs hatasini kontrol eder. Fonksiyon definition derleyici tarafindan bilinmez, linker bilir onu.
- Derleyici declaration gorunce tur donusumunu yapiyor.
- Cagirdigimiz her fonksiyon icin derleyici ya definition yada declaration gormesi gerekir.
- Declarationlar include komutu ile eklenen h dosyasinda yapilir. Bazen de direk definitionlar yapilir.
- Declarationda isim vermek zorunlu degil ama definition’da zorunlu.
- C++ da (void) ve () ayni iken, C’de () bu parameter degiskeninin oldugunu fakat bilinmedigini soyluyor. Bu yuzden () yerine (void) kullanmaliyiz.
- Function redeclaration C’de mumkundur hata vermez.
- DLL Dynamic Link Library.
- C’de static tanimlanan fonksiyonlar sadece o .c de cagirilacagi icin .h’a declaration yazilmaz
- Diyezle baslayan satirlarin hepsi onislemci komutudur, derleyiciye dogrudan verilmez
- Conditional compiling onislemci komutlari ile saglanir.
- #Include: ismi verilen dosya icindeki bildirimlerin .c dosyasina yapisitirilmasi
- Include<> : default directoryde arar- ide ya da compiler tarafindan belirlenen yer- include directorysi – hazir baslik dosyalari standard baslik dosyalari vs
- Include” ” : kaynak dosyanin bulundugu dizinde arar, bulamazsa standard include directory arar
- Include baslik dosyasi icinde karsilastigi tum on islemci komutlarini calistirir, geri kalan c kodlarini direk kopyalar ve yazildigi yere yapistirir.
-
#define ile tanitilan isimlere macro denir. En temel isim bu. Object-like macro functional macro symbolic constant manifest constant gibi alt isimleri de var.
-
Soyle bir define olabilir #define forever for (;;)
-
Stdbool.h bool true false’i vs icerir.
-
Embedded systemde macrolar adresleri tutmak icin de kullanir.
-
Fonksiyonel makroda makro isminden sonra hemen parantez tokeninin gelmesi gerekiyor.
-
Islem onceligi durumundan kacmak icin fonksiyon makrolarinda degiskeni () icinde yazmaktayiz.
#define ISLEAP(y) ((y) % 4 == 0 && ((y) % 100 || (y) % 400 == 0))
-
Kodu kucuk ve sik cagirilan fonksiyonlar cagirildiginda fonksiyon yazmak maliyeti daha fazladir. Bu durumda makrolar daha anlamlidir(fonksiyonel makrolar)
-
Fonksiyonel makronun icine baska bir makro yazilabilir.
-
Fonksiyonel makro kodun fonksiyona gidip return etmesinden cok daha hizli calisir.
-
Makrolarin icine fonksiyonlar da yazilabilir, makro sadece yer degistirme olarak gorulmelidir.
-
Makrolar statement iceriyorsa sonuna ; koymak gerekir.
-
Makrolari kullanirsak fonksiyon pointeri gibi bir durum kalmiyor.
-
Makrolarin ciktisi olarak tanimsiz ifadeler olusabilir - kodlama hatasina musait.
-
#a onu makroda kullanilirsa a'yi cift tirnak icine alir. Haliyle bu asagidaki iki komut ayni anlama gelir:
#define iprint(x) pritnf("%d\n", x) #define iprint(x) printf(#x)
-
Kosullu derleme makrolari:
#undef #if #else #elif #endif #ifdef #ifndef
-
Farkli donanimlar icin bazi komutlar derleyiciye verilmeli bazi komutlar ve bildirimler verilmemeli bu yuzden bu makrolarla hangi bildirimlerin derleyiciye verilecegini secmemizi saglar.
-
Farkli donanimin yaninda farkli os farkli dil versiyon ve lokalizasyon da bu makrolarin kullanim sebepleri arasinda gorulebilir.
-
Bu ifadelere preprocessor expressions deniyor.
-
On islemcinin gormeyecegi ifadeler silik sekilde yazilarak gosterilir.
-
Bu #if logic operasyonlari da icerebilir. Derleyici ile ayni mantik calismakta. Ancak burada degisken kullanilamaz. Degisken derleyicinin oldugu yerde kullanilabilir
-
Makro programa dahil olmadigi icin program tarafindan degistirilemez
-
defined()
bir makronun tanimli olup olmadigina bakar -
Yapi bildirimi gibi bazi bildirimlerin ikinci kez gorulmesi syntax hatasidir.
-
Multiple Inclusion Guard: Bildirimin tekrarlanmasini engellemek istiyoruz, cunku derleyici birden fazla gorurse bu bildirimi syntax hatasina sebep oluyor. Bunu engellemek icin kullanilan bir metod.
-
Bir baslik dosyasinin birden fazla kez dahil edilmesini engelleyen idiomatic yapidir.
#ifndef
,#define
ve#endif
kullanilarak yapilir.#ifndef NUTILITY_H #define NUTILITY_H ... typedef int Word; ... #endif
-
#pragma once
da bu isi yapmak icin gelistirilmistir ancak butun derleyiciler tarafindan desteklenmemektedir, standart degildir -
#ifndef
yerine#if !defined(NUTILY_H)
ayni seydir. -
#undef
bir makronun tanimini ortadan kaldirir, makroyu tanimsiz hale getirir.
-
Makroyu yeniden tanimlamak icin once onu
#undef
etmek gerekmektedir. -
predefined symbolic constant(ontanimli sembolik sabitler): bir define komutu ile tanimlanmamis olmalarini karsin onislemci tarafindan tanimli kabul edilen, dil tarafindan onceden belirlenmis onislemci isimleri. __ ile baslarlar.
__LINE__
,__FILE__
,__DATE__
,__TIME__
bunlar, bulunduklari yerin degerlerini alirlar, ornegin__LINE__
makrosununu kullanildigi yerde line makrosunun oldugu satir numarasi icersine yazilir. -
bunun disinda
__cplusplus
gibi yapilar da kullanilabilir, ornegin:#ifndef __cplusplus #error this program only builded by C++ Compiler #endif
-
#pragma standart bir on islemci komutu ama hangi anlami yuklenecegi derleyiciler tarafindan belirlenmistir.
-
#pragma warning(disable: 4244)
: 4244 numarali warningin verilmesini engeller. Derleyiciye bagli bir durumdur. -
Makronun icinde ; olabilir, fonksiyonel makro parantez icinde yazilmis olabilir, bu parantez oncelik problemine cozum olarak koyulmus olsa dahi burada da dikkat edilmelidir.
-
Fonksiyonel makrolarda if while gibi conditional statementler yazilabilir.
-
Switch deyiminin govdesinde n tane case deyimi olabilir, 0 da olabilir.
-
Switch icindeki degere esit olan Case'e gore programin akisi oraya yonlenir ve oradan devam eder.
-
case label'lari ile go to label'lari arasinda lojik olarak hicbir fark yoktur.
-
case labellari, break deyimi ile sonlanmalidir. ayrica bir default labeli da yazilmalidir.
-
case ifadesi bir sabit ifadesi ve tamsayi olmak zorundadir.
-
else if merdivenleri sadece tam sayi degeri esitligiyle yazilmaz(x == 7), bu yuzden else-if in scopu daha genistir.
-
return degeri enum olan fonksiyonlarda switch case kullanilmasi daha derli toplu gozutmesini saglar.
-
switch case'de jump table'lar kullanildigi icin else if merdivenine gore optimizasyon sansi daha yuksektir.
-
switch case yapilarinda genelde case ifadeleri ya makro ya da enum secilir.
-
olusma sikligi daha yuksek olan case'leri yukariya yazmakta fayda var.
-
case'lerin icinde uzun uzun islem yapmak guzel bir kod aliskanligi degildir, bunun icin bu statementlar, bir fonksiyonda toplanip fonksiyon cagirilabilir.
-
bazen case'lerden sonra asagidaki satirin da calismasi gerekir, bu durumda
case 1: foo();//fallthrough
diye comment olarak belirtilmelidir. -
fallthrough ornegi:
int day_of_year(int d, int m, int y) { int sum = d; switch(m - 1){ case 11: sum += 30; //fallthrough case 10: sum += 31; //fallthrough case 9: sum += 30; //fallthrough case 8: sum += 31; //fallthrough case 7: sum += 31; //fallthrough case 6: sum += 30; //fallthrough case 5: sum += 31; //fallthrough case 4: sum += 30; //fallthrough case 3: sum += 31; //fallthrough case 2: isleap(y) ? 29 : 28; //fallthrough case 1: sum += 31; //fallthrough } return sum; }
-
goto statement, iki farkli sekilde yapilabilir: long jump ve local jump(near jump)
-
long jump baska bir fonksiyondaki frame stackine giden goto statementtir.
-
local jump ayni fonksiyon icerisindeki bir frame stackine gidilmesidir.
-
C'de sadece local jump mumkundur. Sadece fonksiyon icerisinde kullaniliyor.
-
Programin akisini gotolar ile yapmak programin test edilmesini zorlastiriyor, bu yuzden insanlar kullanmamaya yatkin. Ancak bazen tek cozum o oluyor.
-
goto bir labeldir(etiket). Labellar
labelname:
seklinde tanimlanir. Ve her label muhakkak bir statement icermelidir. -
goto labellari function scope denen ozel bir tanim araligina sahiptir. Fonksiyon icindeki her alanda kullanilabilir.
-
goto syntaxi:
goto label; label: //statement;
-
goto kontrol deyimi ile fonksiyonda daha yukaridaki bir deyime programin akisi yonlendirilmemelidir.
-
cok ic ice tasarlanan dongulerde butun donguden cikmak icin goto kullanilabilir. goto kullanilmaz ise flag set edilip bu flagin degerine gore breakler calistirilir, bu durum goto kullanmaktan cok daha komplikedir.
for ...{ //statement while...{ //statement for ...{ //statement if ...{ //statement goto out; } } } } out: //statement
-
Type Conversion bir ifadenin dogrudan degil farkli bir turde ifade edilerek o turde kullanilmasina verilen addir.
-
implicit type conversion: implicit ortulu demek ustu kapali bir sekilde tur donusumu yapilmasidir. Implicit donusum icin bir talimat verilmiyor, derleyici dilin kurallari geregi tur donusumunu otomati yapiyor.Mesela bir int val ile double val ile toplama islemi yaparsak int val bu toplama islemnine double value olarak girer. Bunu derleyici otomatik olarak yapar.
-
explicit type conversion: burada acikca derleyiciye bir operator tarafindan turu donusturmesi gerektigi soyleniyor, bu operatorun adi type-cast operatorudur.
-
implicit type conversion, usual arithmetic conversion'da operatorlerin kullanilmasi sonucu otomatik olarak yapilan ortulu donusumlerdir. Operatorler = +->< vs
-
bir diger implicit type conversion, atama esnasinda yapilan donusumlerdir. Atama veya kopyalama yapilirken tur otomatik olarak degistirilir.
int ival = 3.4
degerinin 3 olarak alinmasi gibi.- Fonksiyon cagrisindaki parametre gonderilen parametreden farkliysa yine otomatik olarak degistirilmesi gibi.
return ival;
statementinda da otomatik olarak degistirilir.
-
Genel olarak yaklasim veri kaybinin engellemeye yoneliktir.
-
Degiskenlerin rank siralamasi asagida verilmistir:
1. Grup: long double double float 2. Grup: long long / unsigned long long long / unsigned long int /unsigned int ----integral promotion bound---- 3. Grup: short char/signed char/ unsigned char _Bool
-
1. Grup: Bu uc operanddan birinin oldugu bir islemde diger operandin rank'i bu operanda donusturulur ve islem bu turde yapilir.
-
2. Grup: Bu uclude:
- Rankler ayni ve signedness farkli ise isaretsiz olan ture donusulerek islem yapilir.
- Rank farkli yuksek olan rank isaretsiz dusuk olan isaretli ise islem isaretsiz yuksek rank'te yapilir.
- Rankler ve isaretler farkli ve buyuk olan rank isaretli kucuk rank isaretsiz ise, isaretsiz kucuk rank buyuk ranke sigiyorsa buyuk rankteki turde yapilir, eger tutamiyorsa isaretsiz olanin bir buyuk rankinde yapilir.
-
3. Grup: integral promotion, islemdeki bir ya da iki degiskenin int alti olmasi durumunda(integral promotion bound) bu degisklerin int'e donusturulup islemin burada yapilmasidir.
-
Int to double implicit type-cast ornegi asagida verilmistir:(Usual Aritmetic Conversion)
int x = 12 int y =5 double dval = 1. * x / y /* 1. ile carpilmasi x'i double yapti x ile carpilmasi y'yi de double yapti. 1. ile carpilmadan double turune esitlenseydi sonuc 2.0000 olacakti.
-
int x = -1;
veunsigned int y = 1
icinx>y
yazar isek x implicit typecast ile 4294967296 degerine donusur. -
Toplama Carpma gibi islemlerde:
- isaretli turlerde tasma tanimsiz davranis.
- isaretli turlerde tasma olmaz - moduler aritmetik devreye girer.
-
Truncation, buyuk bir rankteki degerin kucuk bir ture donusturulurken buyuk bitlerinin kaybedilmesi olayidir.
-
Gercek bir sayidan tamsayiya donusum yapildiginda onlalikli kisim gider, geri kalan kisim tamsayiya sigarsa sorun yoktur, sigmaz ise tanimsiz davranis olur.
-
Buyuk turden kucuk ture atama yapilmamalidir, yapilir ise de tur donusturme operatoru ile yapilmasi gerekir.
-
Explicit expression = (target type)expr;
int x = 10; int y = 3; double dval = (double)x / y;
-
Pointer adres anlamina gelen bir sozcuk.
-
Pointer'lar ikiye ayrilir, Object Pointers ve Function Pointers.
-
Nesnenin bellekte nerede oldugu bilgisi, nesnenin adresidir.
-
Degiskenin turu ne ise adresi de * tur seklinde gosteriliyor. Haliyle nesne turu kadar adres turu vardir.
int *int double *double
-
int *ptr
ptr is a pointer to int... -
Tokenlar arasindaki bosluk karakterlerinin bir etkisi olmadigi icin asterix atomunun nereye bitisik oldugu cok onemli degil.
*
sadece bir sonraki degiskeni niteler:int *pointer, non-pointer
gibi. Burada ikinci degiskenint
turundedir.
-
pointer turlerinin hepsi 4 byte buyuklugundedir, char'dalong long da ayni buyukluktedir.
-
&
address of
operatorudur, (adres operatoru) -
*
dereferencing / indirection
operatorudur, (icerik operatoru) -
[ ] index / subscript
operatoru -
->
member selection op.
(arrow operator) ok operatoru. -
Ornek kullanim:
int x = 10; int y = 30; int *p = &x; p = &y;
-
adres degiskeninin icindeki deger
printf("&x = %p\n", &x)
ya daprintf("ptr = %p\n", ptr)
seklinde yazdirilabilir. -
array decay, array to pointer conversion: Bir dizinin ismi bir ifade icinde kullanildiginda derleyici otomatik olarak o ismi dizinin ilk elemaninin adresine donusturuyor. Asagidaki iki satir bu ozellikten dolayi ayni anlama gelmektedir.
int *ptr = &a[0]; int *ptr = a;
-
Bu durum sizeof operatorunde gecerli degildir. Iki degisken farkli iki sonuc uretir.
-
Bir nesnenin adresi degistirilemez. Nesne bellekte bir yerden bir yere tasinamaz.
-
Derefencing-Indirection Operatoru(*): Icerik operatoru: Adresi verilen degiskenin degerini verir.
-
Icerik operatorunun operandi bir adres olmali.
-
Bir kodun karmasik gorunmesi icin yapilmasina obfuscation denir, anlami deg ismeden anlasilmaz hale getirilmeye yarar.
-
Icerik operandi dizinin ilk elemanini gosterir.Asagidaki kodda a[0] 100 degerini alir.
int a[] = {10,20,30,40}; *a = 999;
-
pointee pointerin gosterdigi nesnedir.
int *ptr ptr pointer *ptr pointee
-
C de pointerlar call by reference cagri modelinde kullaniliyor. Bu temel fonksiyonudur.
-
scanf call by reference bir fonksiyon cunku aldigi argumanin degerini degistiriyor.
-
Call by Reference = kendisini cagiran fonksiyona kendisine verilen degeri geri iletmeye calisan fonksiyonlar.
-
Bunu yapmanin pointersiz yolu return etmektir. Buna geri donus mekanizmasi denir. Bazi durumlarda pointer kullanmamak daha kolay daha yalin ve pratiktir.
-
Ancak komplike senaryolarda call by reference kullanmak daha avantajlidir.
-
Bir senaryo: Geri donus degeri fonksiyonun basari degerini donerken pointer ile gecilen arguman da hesaplanan degeri tutuyor olabilir bu durumda yine hesaplanan degeri call by reference ile almak faydalidir.
-
Call by reference ile calismak daha az maliyetlidir. Cunku call by value'da return edilen degeri tutan bir degisken ve asil degisken varken diger durumda sadece bir degisken vardir. Kopyalama yapilmaz. Iste bu kopyalama maliyeti call by referenceyi cazip kilmaktadir.
-
Degiskenin - Yapinin boyutu ne kadar buyuk olursa olsun, pointerin boyutu degismemektedir. Bu sekilde cikti veren fonksiyonlarin yanina
//out
yazilir,void foo(T *ptr)//out
; -
Call by Reference ile alinan adreslerdeki degerler sadece input olarak kullanilacaksa fonksiyon declarasyonunda onlarin basina
const
anahtar sozcugu kullanilir. Bu sekilde bu degisken sadece salt okuma amacli kullanilir.void add_two_num(const int* pleft, const int* pright, int* presult); void func(int *ptr); //output-param void foo (const int *ptr); //input-param
-
Input parametresi kucuk boyutlu ise(20 byte sinir olabilir) call by value ile alabilir, eger buyuk bir input ise call by reference ile alinmasi tercih edilmelidir.
-
Dizilerin dogrudan call by value olarak fonksiyona gonderilmeleri mumkun degildir. Donus degeri de dizi olamaz. C dilinde Bir fonksiyonun
- Parametresi dizi olamaz
- Geri donus degeri bir dizi olamaz.
-
Pointer Aritmetigi: C'de bir adres ile bir tamsayi toplanabilir adresten tamsayi cikartilabilir. Tamsayidan adres cikartilasi syntax hatasidir.
-
Bu islemlerin sonucu adrestir, adres ile tamsayi isleme girdiginde cevap adrestir.
-
Dizinin bir elemaninin adresini 1 ile toplarsak bir sonraki elemanin adresini elde ederiz. Bu pointer aritmetigi sayesinde ptr'nin 1 artamasi icin sizeof(int) ile toplamak yerine 1 ile toplayabiliyoruz.
-
Haliyle
a[10]
dizisi icina+i
ile&a[i]
i'nin artan degerleri icin ayni sekilde dizi icinde ilerler.a[b] = *(a+b)
veb[a] = *(a+b)
-
Pointer degiskenin bir artmasi gosterdigi dizi elemanindan bir sonrakisini gostermesi anlamina geliyor.
for (int i = 0; i < 10; ++i){ printf("%d %d %d\n", *ptr, a[i], *(a+i)); ++ptr; }
-
++cnt
cnt degerinin degeri 1 artar iken++ptr
ptr gostericisinin dizinin bir sonraki elemaninin adresini gostermesi demektir. -
Pointer aritmetiginde iki adres toplanamaz. Syntax hatasidir.
-
Iki adres birbirinden cikartilirsa isaretli bir tamsayi elde edilir
(a+5) - (a+3)
2
tamsayi degerine esittir. Ancak ayni dizi elemanlarini tutan adreslerde kullanmak mantiklidir. -
a dizisinin i elemanina erisirken olan:
a[i]
a adresi + i tamsayisi isei[a]
da i tamsayisi + a adresi oldugu icin ayni anlama gelir. -
p adres olmak uzere
*p
ilep[0]
arasinda hicbir farklilik yoktur.p[-3]
ile*p[p-3]
de aynidir. -
Dizinin olmayan bir elemanina erismeye calismak syntax hatasi degil ancak tanimsiz bir davranistir.
-
C dilinde pointer degisken ya
valid
ya dainvalid
state
'dedir. invalid pointeri sadece atama yapmak icin kullanilmalidir. -
Cop degerdeki(ilk deger atanmamis) pointerlar gecersiz pointerlardir. Bu pointera ilk deger adresi atamak disinda baska kullanilamaz.Bu pointer'lara
wild pointer
denir. -
Bir nesnenin adresi olmayan bir pointer'da gecersiz pointerdir. 5 elemanli bir dizinin son elemani
a[4]
iken,a[5]
gecerlidir ancak kullanmak istersek derefence edersek tanimsizdir. -
Bir adres hayati devam eden bir nesnenin adresi ise gecerlidir, ayrica dizinin bittigi yerden sonraki adres gecerlidir ancak icerik operatorunun operandi olarak kullanildiginda tanimsizdir bu durum daha detayli incelenecektir.
-
Bir pointerin tuttugu nesnenin hayati bittiginde o pointerin da gecerliligi ortadan kalkar.
-
C'de diziler buyuyen varliklar degildir kac elemani varsa o kadar kalacaktir, haliyle diziyi tasan pointer bu anlamda kullanilamaz.
-
Dizinin buyuklugunu tutmak icin belki kullanilabilir, ancak derefence edilemez.
-
int* p = NULL
p gecerli ancak hicbir nesneyi gostermiyor, Null pointer. -
Const anahtar sozcugu bir degiskenin taniminda kullanildiginda o degiskenin degerinin degismeyecegini adeta bir sabit gibi kullanilacagini gosteriyor. Degistirilmeye calisirsa syntax hatasi olur.
-
const variable
bir oksimorondur. Degismeyecek olan degisken demektir. Okuma amacli kullanilacak olan degiskenler ve diziler icin kullanilir. -
int const
ileconst int
ayni anlama gelmektedir. -
Degeri degismeyecek degiskenleri
const
ile tanimlayinca hem okunabilirlik artiyor hem de derleyici tarafinda optimizasyon saglaniyor. -
Sembolik sabit makrolari,
define
ileconst
arasinda farklar vardir:- const degiskenleri scopu vardir.
- nesne olduklari icin adresleri pointer degiskenlerde tutulabilir.
- Omur kategorisi degisebilir. Sembolik sabit sadece bir sabit degeridir.
- Case dizi indisi gibi yerlerde ancak makro sabitler kullanilabilir.
-
Dizinin sadece 5 indisli elemanina deger verip digerlerini sifir ondegeri ile tanimlamak:
int a[100] = {[5] = 459}
-
const nesnelerin ilk degeri sabit olmak zorunda degildir runtime'da belirlenir
const int y = func();
-
static omurlu degiskenlere ilk deger veren ifadenin sabit bir ifade olmasi gerekir.
-
const bir degiskeni bir sekilde degistirsek dahi bu tanimsiz bir davranis olur.
-
int * const ptr = &x
demek: ptr'nin degerinin hic degismeyecegini ve hep x'i gosterecegi anlamina gelir.ptr = &y
syntax hatasidir.*ptr
degeri degisebilir yani x'in degeri degisebilir ancak bu ptr hep o adresi tutacaktir. Boyle pointerlara const to pointer denir. top level const ve right const' da denir. -
const int * ptr = &x
ve ayni anlama gelenint const * ptr = &x
ptr yoluyla ptr'nin gosterdigi degiskenin sabit olacagi anlamina gelir.ptr
x
'i salt okuma amaci ile gosteriyor.*ptr = y
hata verecektir ancak ptr baska bir degeri gosterebilir,ptr = &y
ifadesi mumkundur. Bu sekilde tanimlanmis pointerlara pointer to const denir. low level const ve left const' da denir. -
Kisacasi const hangi ifadeden once gelirse const olan odur.
-
const int* const ptr
sabit olan bir degerin adresini surekli tutacak olan bir pointerdir. -
Cogunlukla low level const kullanilir. Salt okunacak olan sabit degeri tutan bir pointer.
-
const dogrulugu: const ile tanimlanmasi gereken butun degiskenler const anahtar sozcugu ile tanimlanmis olmalidir.
-
access fonksiyonlarinda erisilen degiskenin const olarak tanimlanmamasi cokca yapilan hatalardandir.
-
void func(int * p)
ben bu fonksiyonda p'nin tuttugu degiskeni set edecegim demek ikenvoid func(const int *p)
ben bu fonksiyonda p'nin tuttugu degiskeni sadece okuyacagim demektir. -
const int*
degerininint*
degerine typecast edilmesi yanlis ikenint*
degerininconst int*
degerine donusturulmesi hat degildir.int x = 10; const int* ptr = &x; //burada bir hata yok.
const int x = 10; int* ptr = &x; //burada bir hata olur. const olan x'i gosterdiginin const olmadigini soyleyen ptr ile gostermeye calistik.
-
Dizi ustunde islem yapan fonksiyonlar dizinin adresini ve boyutunu isterler. Bu cagriyi yapacak fonksiyon bu iki degeri geciyor olmalidir.
void printArray(const int* p, size_t size)
. -
Dizinin belirli kismi yazdirilmak istenirse mesela 5. elemandan itibaren 3 elemena yazilmak istenen a dizisi icin
printArray(a+5, 3)
notasyonu kullanilir.a+5
yerine&a[5]
yazilabilir. -
Bir ornek kod: ortalama alma fonksiyonu aldigi pointeri yine bir baska fonksiyon olan dizi toplama fonksiyonuna vermekte:
int sum_array(const int* p, int size) { int sum = 0; while(size--){ sum += *p; ++p; } return sum; } double get_mean(const int* p, int size) { return(double)sum_array(p, size)/size; } int main() { int a[SIZE]; randomize(); set_array_random(a, SIZE); print_array(a, SIZE); printf("mean value of array = %f\n", get_mean(a, SIZE)); }
-
Birden fazla geri donuse ihtiyaci olan fonksiyonun bunu yapmasi icin ilk metod call by reference'dir.
void get_array_max_min(const int* array, int size, int* max, int*min)
seklinde max ve min return edilmek yerine doldurulur. -
Adres alan fonksiyonlar bu adreslerdeki degiskenleri baska fonksiyonlara verebilirler.
-
Bir fonksiyona bir arrayin iki elemaninin pointer uzerinden gecmek icin iki notasyon vardir.
swap(&p[k], &p[k+1]); swap(p+k, p+k+1);
-
Pointer degiskeni fonksiyona parametre olarak vermenin bir yolu
int *p
iken diger yolint p[]
dir. -
void sort(int p[], int size)
'deki konvensiyon p eger bir dizinin ilk elemani ise tercih edilir, bu bir zorunluluk degildir ancak her ikisi de kullanilabilmektedir.p[]
yaygin degildir. Unutulmamalidir ki bir fonksiyon bir diziyi parametre alamaz.p[]
ilk elemandir. -
*p++
p'nin gosterdigi nesneye eris, ardindan p'yi bir arttir. Array islemlerinde cokca kullanilir. En yaygin idiomlardan biridir.#define SIZE 100 void copy_array(int *pdest, const int *psource, int n) { while(n--) *pdest++ = *psource++; }
-
Bir ornek kod: a dizisinin A indisli elemanindan baslayarak b dizisinin B indisli elemanindan baslayana yere N tane eleman kopyalayan fonksiyon:
void copy_partial_array(int *pdest, const int *psource, int n) { while(n--) *pdest++ = *psource++; } int main() { int a[]; int b[]; copy_partial_array(b+B, a+A, N); //1.method copy_partial_array(&b[B], &a[A], N); //2.method }
-
++ ve -- operatorunun operandi bir dizi olamaz. Dizinin ismi burada kullanilamaz.
-
Bir ornek
++*p++
sagdan sola oncelik seviyesi ile okursak(++(*(p++)))
demek yani dongude ise her elemanin degerini 1 artirarak ilerler. -
px == py
icin adreslerin ya ayni nesnenin adresi olmasi gerekiyor ya ayni dizinin bittigi yerin adresi olmasi gerekiyor ya da null pointer olmasi gerekiyor. -
a dizinin bittigi yerin adresi
pe = a+SIZE
olarak tanimlayip, diziyi donerken karsilastirma olarak kullanilabilirwhile(ps != pe)
. Buna range denir,[p1 p2)
gibi bizim durumda[ps,pe)
. -
px == py
x'i ve y'yi gosteren iki farkli adresi karsilastirirken*px == *py
x'in ve y'nin tuttugu adreslerin icindeki degerleri karsilastirir. -
Bir turu temsil eden alternatif bir turu - tur ismini olusturmak mumkundur. Buna alias takma isim denir.
-
typedef bir ture esisim olacak yeni bir tur ismi bildirimidir
int
yerinetamsayi
gibi bir isim vermek gibidir. Derleyici artik tamsayi'yi taniyacaktir.typedef int Word; // artik Word int yerine gececektir. Word x = 5; // bu sekilde tanimlama yapilabilir. Word func(int...); // return degeri Word yani int olan bir fonksiyon
-
typedef int* IPTR
int* turu yerinie IPTR denebilir.IPTR p = &x
. Scope'u belirlemek icin makrolar yerine kullanilir. Ayrica birden fazla pointeri tek satirda tanimlamak yine typedef ile mumkundur.define IPTR int* typedef int* Iptr; int main() { IPTR p1, p2; //int *p1, p2; Iptr p1, p2; //int *p1, *p2; }
-
typedef tanimlama yol haritasi
- hangi ture es isim verecekseniz o turden bir degisken tanimlayin.
int x;
- tanimlamanin basina typedef anahtar sozcugunu yerlestirin.
typedef int x;
- degiskene verdiginiz ismi o degiskenin turune vereceginiz es isimle degistiriniz.
typedef int Myint;
- hangi ture es isim verecekseniz o turden bir degisken tanimlayin.
-
Dizi icin
typedef int INTA[10];
10 elemanli bir dizinin typedef'idir.INTA10 a, b, c;
icinint a[10], b[10], c[10];
-
typedef bildiriminin avantajlari
- Karmasik bildirimleri okumasi daha kolay bir hale getirir.
- Ara degisken basliklari yaratarak ana tur isimlerini kullanmak yerine bu ara degiskenlerden turetmeler yapilir.
typedef double Dollar
diyip ardindan dolarla ilgili degiskenlerin bu degikenden turetilerek kullanilmasi gibiDollar sum_account, expenditure
gibi. - Ilerde Dollar turunun tuttugu dolar degerlerinin turunu degistirmek istersek sadece typedef bildirimini degistirerek yapabiliriz.
-
typedef bildirimiyle olusturulan isim baska bir typedef turu olusturuluken kullanilabilir
typedef int Word
icintypedef Word* Wordptr
-
standart kutuphanelerde
int32_t
uint16_t
gibi typedefler vardir.
-
Bir typedef ornegi:
typedef int Bool
boylece derleyiciBool
turunde calisabilecek. -
Standart kutuphane typedef bildirimlerini okunabilirlik ve tasinabilirlik amacli kullaniyor.
-
size_t
sizeof operatorunun urettigi degerin turudur.typedef unsigned int size_t
seklinde tanimlanabilir. Ancak bu tanim derleyiciye birakilmistir.unsigned int
olmak zorunda degildir. Gercek tur yerine typedef kullanildiginda bu gibi durumlarda derleyiciye hareket alani birakiliyor.size_t
'nin buyuklugu derleyicideki int degerinin buyuklugune gore degisebiliyor.Ya da onulong
turune atayabiliriz. -
Bununla beraber,
ptrdiff_t
,time_t
,clock_t
,fpos_t
veldiv_t
gibi standart typedefler de vardir. -
strlen
,malloc
,memcpy
gibi fonksiyonlarsize_t
parametresini kullanan bazi standart C fonksiyonlarinda tasinabilirlik amaciyla bu method kullanilmistir. -
standart kutuphanenin
size_t
kullandigi yerde(parametre turu ya da geri donus degeri turu olarak) biz desize_t
ile o degiskeni almak zorundayiz ki tasinabilirlik sorunumuz olmasin. -
Standart kutuphane
size_t
tur es isminin istenildigi durumlar:- Bir dizi boyutu isteyen fonksiyon parametrelerinin turu olarak.
- Yazi uzunlugu turu olarak.
- sizeof degeri isteyen parametreler.
- tane(adet) turu.
-
stddef.h
kutuphanesi bir cok makro ve typedef iceren cok buyuk olmayan bir kutuphanedir. -
size_t
degerinin printf'de yazdirmak icin%zu
set edilmistir:printf("sizeof(int) = %zu\n", sizeof(int))
. -
Functions Returning Pointers, Adres donduren fonksiyonlar C'de en cok kullanilan yapilardan biridir. Fonksiyonun geri donus mekanizmasi ile bir nesneyi teslim etmesi demektir.
-
int *func(void)
seklinde tanimlanir.int *xptr = func()
seklinde kullanilabilir.return &x
gibi donebilir. -
Bu sekilde pointerin hangi adresi tutacagini dogrudan vermek yerine fonksiyonun ciktisina gore belirleyebiliyoruz.
-
Otomatik omurlu degisken, bu kodun yurutulmesi surecinde hayatta olan ve kod blogu bitince sifirlanan kodlardir. Otomatik omurlu degiskenin adresini fonksiyon disina return eden pointer, invalid pointer olur.
int* foobar(void) { int sum = 0; for(int i = 0; i<10; ++i){ sum +=i } return ∑ } int main() { int* ptr = foobar(); // foobar yerel bir degisken donmektedir. Haliyle bu degiskenin omru bittigi icin undefined behaviour. }
-
Ancak sum degiskeni
static int sum = 0
olarak tanimlansa idi bir undefined behaviour olmazdi, cunku static omurlusum
degiskeni program calistigi surece var olacakti. Haliyle global degiskenler, static anahtar sozcugu ile tanimlanan yerel degiskenler ve string literaller"string literal"
-
Adres donduren bir fonksiyon asla ve asla otomatik omurlu bir nesnenin adresini dondurmemelidir.
-
Bir fonksiyon static omurlu nesne adresi donduruyorsa fonksiyona yapilan her cagri ayni adresi donuyor olmali.
-
Fonksiyon kendisini cagiran koddan bir nesnenin adresini alir adresini aldigi nesnenin adresini dondurur.
-
Bir kod ornegi: Bir dizinin en buyuk elemaninin adresini donduren fonksiyon:
#define SIZE 20 int* array_max(const int* pa, size_t size) { int* pmax = (int *) pa;// const cast - const'tan int'te atama yapilamayacagi icin. for (size_t k = 1; k < size; k++) { if(pa[k] > *pmax) pmax = (int *)&pa[k]; } return pmax;// bu adres pa dizisinden cekildigi icin otomatik omurlu degildir, dizinin icindeki global adreslerden biridir. } int* array_min(const int* pa, size_t size) { int* pmin = (int *) pa;// const cast - const'tan int'te atama yapilamayacagi icin. for (size_t k = 1; k < size; k++) { if(pa[k] < *pmin) pmin = (int *)&pa[k]; } return pmin;// bu adres pa dizisinden cekildigi icin otomatik omurlu degildir, dizinin icindeki global adreslerden biridir. } int main() { int a[SIZE]; randomize(); set_random_array(a, SIZE); print_array(a, SIZE); int *pmax = array_max(a, SIZE); int *pmin = array_min(a, SIZE); printf("max = %d\n", *pmax); printf("indis of max = %d\n", pmax-a); //pmax - a pmax elemaninin bulundugu indisi verir *pmax = -1;// en buyuk elemani -1'e esitledik, boylece test edebilecegiz kodumuzun dogru calistigini print_array(a, SIZE); print_array(a, pmax - a + 1); //dizinin basindan en buyuk elemani kadar ve o eleman dahil yazdirir. print_array(pmax, SIZE - (pmax - a)); //dizinin en buyuk elemanindan sonuna kadar yazdirir. if(pmax > pmin){// max'in adresi min'den buyuk mu max dizide daha once mi geliyor. print_array(pmax, pmin - pmax + 1);// max onceyse maxtan mine kadar yazdir } else{ print_array(pmin, pmax - pmin + 1);// min onceyse minden maxa kadar yazdir. } print_array(pmax < pmin ? pmax : pmin, abs(pmax - pmin));// yukardaki islemin aynisini yapan tek satir kod blogu swap(array_min(a, SIZE), array_max(a, SIZE));// dizinin max ve min degerlerinin yerlerini degistirir. }
-
Otomatik omurlu bir nesnenin adresiyle asla donulmemelidir.
-
NULL
bir object-type makrodur. Bir anahtar sozcuk degildir. Identifier vs degildir. NULL standart bir makrodur.(stdio.h
,stdlib.h
,stddef.h
,stddef.h
,time.h
) -
NULL pointer herhangi turden bir pointer degiskene atanabilen, ilk deger olarak verilebilen bir sabit ifadesidir.
-
NULL pointer semantik(mantiken-syntax degil) olarak hic bir nesneyi gostermeyen bir pointer'dir.
-
Degeri NULL olan bir pointer degiskeni dereference etmek undefined behaviour'dur.
-
Bir pointerin adresinin null olup olmadigi
if(p == NULL)
gibi yazarak sinanabilir. -
C'de Pointer olmayan degiskenlere
NULL
atamasi yapilamaz, sadece pointer degiskenlerdeNULL
makrosu kullanilabilir. -
C'de lojik ifade beklenen yerlerde(lojik operatorler, dongu karsilastirma operatorleri vs..) adres ifadeleri de kullanilabilir.
if(ptr != NULL)
veif (ptr)
while(ptr)
ptr ? x : y
-
Aritmetik turden static omurlu degiskenlere ilk deger verilmediginde bunlar hayata 0 degeri ile baslar ve de Aritmetik turden static omurlu degiskenlerin ilk deger verilmediginde bunlar da hayata
NULL
pointer degeri ile baslar. -
Bir pointer degiskene tam sayi sabiti olarak 0 atanirsa bu gecerlidir, ve bu durumda derleyici 0 tam sayi sabitini
NULL
pointer'a donustururptr = 0
. Ancakptr = NULL
tercih edilir. -
int a[5]
icin a'nin turuint[5]
, doubleda[20]
nin turudouble[20]
'dir.int[5] = {1,2,4}
icin nasilint[3]
veint[4]
0 oluyorsa. Pointer arrayde'deint *p[20] = {&x, &y}
gibi bir tanimda geri kalan 18 elemanNULL
pointer olur. -
int *p[20]
20 taneint*
turunden pointer iceren bir pointer arrayidir. -
Bazi geri donus degeri pointer olan fonksiyonlarin basarili olduklarinda donus degeri nesne adresi iken hata durumunda
NULL
doner. Mesela fopen() fonksiyonu basarili olursa *FILE donerken basarisiz olursaNULL
doner. Bu basarisiz olan pointer return eden fonksiyonlarNULL
donmesi durumu standart kutuphanelerde ve bizim yazacagimiz kutuphanelerde de cokca kullanilir. -
Arama fonksiyonlari(search - find fonskiyonlari.) da arama sonucu olarak bulunan nesnenin adresini donmektedir, yani pointer doner. Eger aranilan deger bulunamazsa
NULL
pointer doner. -
Bir kod ornegi: Bir int dizide bir deger arayan search_in_array isimli bir fonksiyon tanimlayin
int* search_in_array(const int* p, size_t size, const int val) { for (size_t i; i < size; i++) { if (*p == val) return (int*)p; p++; } return NULL; }
-
Bizden nesne adresi eden fonksiyona null pointer gecmemeliyiz
void func(int* p)
Ancak bazi fonksiyonlar pointer parametresine null parametresini alabilir, bu fonksiyonlar null pointer icin de iceride opsiyonu olan fonksiyonlardir. Mesela fflush fonksiyonu null pointeri alirsa ayri bir is yapar null pointer gecmezsek ayri bir is yapar. -
NULL pointeri ayrica flag gibi de kullanilabilir, bir pointer basta NULL tanimlanarak mesela if'te nesne adresi ataniyorsa buradan if'e girip girmedigi kontrol edilebilir.
-
Dinamik omurlu nesne adresi tutan bir pointerin, nesnesinin omru bittiginde
NULL
pointera atanir. -
C dilinde yazilar char dizilerde tutuldugu icin
strlen
,strspn
,strpbrk
gibi fonksiyonlar char pointerconst char*
ya dachar *
parametresi alir. -
C'de yazilar null terminated olarak tanimlanir. Haliyle stringin yani char dizisinin son elemani NULL oldugu icin diger arraylerde oldugu gibi boyutunun fonksiyona verilmesine gerek yoktur. for loopu
\0
(null karakter) ile karsilasinca duracak sekilde doner.void print_str(const char* p){ for(int i = 0; p[i] != '\0`; ++i){ printf("%c", p[i]); } }
-
putchar
aldigi karakteri standart output device'a basar. -
sgets
fonksiyonu icindeki stringe standart inputtan alinan yaziyi yazar. -
NULL karakter
'/0'
0 sayisina tekabul etmektedir. Bir stringin sonunda olan bu karaktere karar diziyi donmek icin(while *p != '\0')
gibi yapilar kullanilabilir. -
Bir fonksiyon aldigi adresteki diziye veya bu dizideki yaziya yazacagi, buradaki yaziyi yazip degistirecek ise. yani
const
ile verilmemis bir pointer ile cagirilan bir fonksiyonun diziyi tasma riski vardir. -
Burada cagiran fonksiyonun bu durumu takip ediyor olmasi gerekir, bu sorumlulugu fonksiyonu cagiran kod bloguna birakir.(std. kutuphaneler boyledir.)
-
Diger secenek ise diziyi verirken boyutunu da fonksiyona vermektir.
-
strlen:
size_t strlen(const char *p)
strlen cagrisina verdigimiz dizi size_t turunden bir degiskende tutulmalidir. -
Bir kod ornegi strlen: Alinan stringi tersten yazdiran fonksiyon,
revprint
void revprint(const char* p){ for (int i = (int)(strlen(p) -1); i >= 0; --i){ putchar(p[i]); } }
-
strchr
dizideki aranacak karakteri ilk buldugunda bu elemanin adresini dondururchar* strchr(const char *p, int ch)
. -
strrchr
dizideki aranacak karakteri son buldugunda bu elemanin adresini dondururchar* strrchr(const char *p, int ch)
. -
str structindaki p pointerinda tutulan karakterin indisini
p - str
isleminin ciktisi olan tamsayi verir. -
Elimizde bir yaziyi gosteren bir pointer degisken var, bu pointer degiskenin degerini yazinin sonundaki null karakter ile degistirmemiz gerektigini dusunelim. Bunu yapmaya yonelik bir cok farkli kodlar vardir.
while(*p != '\0') ++p;
while(*p) ++p;
while(*p++) ; --p;
p += strlen(p);
p = strchr(p, '\0');
-
NULL pointer
ilenull character
karistirilmamalidir. -
NULL
bir makrodur ve adres sabitidir, pointerlara atanmalidir. Pointeri degeri hicbir nesnenin adresi olmayan bir adrese ceker. -
null character
= '/0' bir tamsayi sabitidir, ya int ya da char turden bir degiskene atanabilir. Bu bir degiskendir, dizinin sonunda olmasi dizinin sonlandirilmasi anlamindadir. -
Bir C idiomu
NULL
poiter ilenull
karakteri ayni ifade icinde kullaniyor, eger ptr yaziyi gosteriyor ve ptr'nin gosterdigi yazi bos degil ise ifadesi:if (ptr != NULL && *ptr != '\0')
buradashort circuit
davranisindan faydalanilir. Bu sekildeNULL
pointeri derefence etmemis oluyoruz. Bu ifadeyleif(ptr && *ptr)
ayni ifadedir. -
Bir diziye bir yazi
struct name = "name"
seklinde yapamayiz onun yerine strcpy ile yapilir. -
Standart kutuphanede fonksiyonlar her zaman hedef adres kaynak adresten once alinir.
-
strcpy
boyut almadigi icin kullanirken dikkat etmek gerekir tasma olup olmadigi developerin sorumlulugundadir. -
while(*pdest++ = *psource++)
null karaktere kadar kopyalamayi yapar. -
strcpy fonksiyonunun kesisen bloklar(overlapped) uzerinde calismasi tanismizdir.
strcpy(str+2, str)
ile ali alali yapmaya calismak gibi durumlar burada kast edilmektedir. Bunun icinmemmove
gibi bir fonksiyon kullanilabilir. -
strcat
bir yazinin sonunda bir yazi eklemek icin kullanilir.
-
strcmp
buyuk olan degere sahip olani buyuk doner.lexicographical
compare denir buna. Burada gelen ilk karsilikli ogenin hangisi buyukse o buyuk olur. Boyuta karsilikli ogelerin hepsinin ayni oldugu durumda bakilir. Sozlukteki gibi karsilastirilir. -
strcmp
esitlik durumunda 0 dondugu icinreturn
veif
statementlerinde genellikle!strcmp...
seklinde kullanilir. -
C'de asla stringler esitligi dogrudan kontrol edilemez. Diger dillerde vardir.
s1 == s2
dedigimizdes1
ves2
dizisinin adresleri karsilastirilir ve dogal olarak her zaman false doner. -
stricmp
stringlerin karsilastirmasinicase insensitive
olarak yapar. -
strpbrk
yazinin icinde bir karakter grubundan birinin ilk gectigi yeri bulmakta kullanilir. Mesela bir yazida sesli harflerden birini aramak icin kullanilabilir.strchr
'nin coklusu gibi dusunulebilir.breitling
stringindeaeiou
harflerinden birini aramak ornek olabilir.char name[9] = {b,r,e,i,t,l,i,n,g}; char letters[5] = {a,e,i,o,u}; char* p = strpbrk(name, letters); //returns adress of letter if found, else returns NULL pointer.
-
Literal constant ile ayni anlama gelmektedir, ikisi de sabit demektir. cift tirnak icinde yazilmis yazilari tanimlamakta kullanilmaktadir:
"string literal"
. -
String literalleri derleyicin bakis acisinda elemanlari char turden olan sonu null karakter olan bir dizidir, derleyici bu ifadeyi gordugunde bir dizi yaratip onu bu string literali ile doldurur "murat" = char arr[5] = {r,a,d,o,\0} demektir.
"rado"
ise bu dizinin adresini tutan bir pointer gibi tanimlanir. Teorik olarak*"rado"
bu dizinin ilk elemani olanr
karakterine erismek icin kullanilabilir. -
String literal'leri statik omurludur, yani string literalini bir kez kullanilsa dahi program boyunca bellekteki yerini koruyor. Bu durum bazen avantaj veya dezavantaj olabilir.
-
String literallerin turu dogal olarak
char
dir. Ancakconst
olmamasina ragmen, bir string literalini degistirme girisimi tanimsiz bir davranistir. Yani string literal'leri read only kullanima aciktir. -
Bu cokca yapilan hatadan dolayi kacinmak icin bir yol vardir string literal'i
const
olarak tanimlamaktir:const char* p = "breitling"
. Boylece bu string literal'deki bir degeri degistirme girisiminde bulunulmak mumkun olmaz ve tanimsiz davranistan kacinilir. Zaten Cpp'da bu durum zorunludur. -
String literalin gecildigi fonksiyonun prototipinin
char *
degilconst char *
olmasi gerekir. -
Ozdes string literal'ler farkli iki char pointerina atandiginda bu iki literalin ayni adrese sahip olup olmayacagi net degildir, bu ozdes string literal'leri tek bir dizi olarak da farkli iki dizi olarak da tutulabilir. Burada
unspecified behaviour
sozkonusudur. Haliyle bir string literali if statement'ta vs kullanilamaz:if(chr == "str_literal")
. Bu karsilatirmayi yapmanin gecerli yolustrcmp
'yi kullanmaktir. -
String literaller ile
strcmp
,strcpy
gibi fonksiyonlarin beraber kullanilmasi cok sik karsilasilan bir durumdur.strcpy(name, "movado")
seklinde bir cok kez karsimiza cikar. -
String literalinde harfleri kullandigimiz gibi onlarin hex karsiligi olan ve daha fazlasini da kullaniliriz:
"\x42"
gibi"A\x42C"
string literaliuABC
yazisina esittir. -
"\\"
string literali\
'yi ifade etmeyi saglarken\"
string literali"
'yi ifade eder.
-
C'de string literal const degildir ancak Cpp'da const'tur. Her iki dilde de const gibi ele alinir. Static omurlu degiskenlerdir.
-
printf
fonksiyonu variyadik bir fonksiyondur:int printf(const char*,...);
prototipi standart bir eleman sayisini aramaz. -
puts
ise sadece aldigi adresteki stringi sonuna kadar yazdirip newline'a gecen bir fonksiyondur, variyadik degildir. -
sizeof
bir operator ve keyword'dur ve compile time operatoru'dur. Bir constant expression verir.strlen
ise bir fonksiyondur ve geri donus degeri programin calisma zamaninda ortaya cikar. Bu iki ifadenin tek benzerligi ikisinin desize_t
turunden cikti vermesidir. -
sizeof
vestrlen
icin karsilastirma ornegi:const char* p = "zenith" printf("[1] %zu\n", sizeof p); // prints size of char pointer = 4 printf("[2] %zu\n", sizeof *p);// prints size of char pointers point val's size "z" it is char = 1 printf("[3] %zu\n", sizeof "zenith"); // prints size of val's size for 7(6+null) time = 7 printf("[4] %zu\n", strlen(p)); // lenght of string = 6 printf("[5] %zu\n", strlen("zenith")); // lenght of string = 6 printf("[6] %zu\n", strlen("")); // lenght of string = 0 printf("[7] %zu\n", sizeof("")); // lenght of string plus null string = 1 printf("[8] %zu\n", sizeof ++p); // size of char pointer = 4 printf("[9] %zu\n", strlen(p)); // lenght of string = 6 there no dif on p pointer pointed adres for[8] because sizeof op has no effect on its parameter.
-
31'de kaldim pointer arrays
-
36nin ilk 5 dksini izlemedim. pointer arrays.
-
Pointer to Pointer, gosterici gosteren gostericiler demektir.
-
Pointerin kendi adresinin tutuldugu pointerlardir.
-
Bir ifade bir
T
turunden bir nesnenin adresini gosteriyorsa turuT*
dir. Bu durumdaT*
'i tutan pointerin turu deT**
olacaktir. -
**p
ile gosterilir, bunu*(*p)
gibi dusunebiliriz.int x = 23; int* p = &x; int** q = &p; //int * turundeki p pointerinin adresini tutan int** turunden degisken. &p'nin turu int**dir.
-
****ptr'ye kadar kullanilabilir.
-
swap ornegi, swap yapan fonksiyonun int* turunden degiskenlerin adreslerini aldigi duruma ornek olacaktir.
void pswap(int** ptr1, int** ptr2)// takes address of pointer { int* ptemp = *ptr1; *ptr1 = *ptr2; *ptr2 = ptemp; } int main { int x = 10; int y = 20; int* p1 = &x; int* p2 = &y; pswqp(&p1,&p2); // x ve y'nin degerleri degismez onlari gosteren gostericiler degisir. pswap(p1,p2); //x ve y'nin degerleri degisir. }
-
Daha karmasik bir senaryo incelemek gerekirse: Bir dizinin hem en buyuk elemanin hem de en kucuk elemanin adresinin hesaplayan fonksiyon.
void get_min_max(const int* pa, size_t size, int** ptr_min, int** ptr_max) { *ptr_min = *ptr_max = (int *)pa; for (size_t i = 1; i < size; ++i){ if(pa[i] > **ptr_max) *ptr_max = (int *)pa[i]; else if(pa[i] < **ptr_min) *ptr_min = (int *)pa[i]; } } int main() { int a[SIZE]; // randomize(); set_array_random(a, SIZE); print_array(a, SIZE); int* pmin, *pmax; get_min_max(a,SIZE, &pmin, &pmax); swap(pmin,pmax);// changed vaules of variables }
-
Pointer to Pointer boyutu pointer ile aynidir.
-
Pointer to Pointer'a da NULL ilk atama yapilabilir.
-
Bu adresler bir dizi de olabilirdi:
void foo(int** pa, size_t size); int a = 12, b = 23, c = 34, d = 56; int main() { int* pa[] = {&a, &b, &c, &d};//pointer array. foo(pa, asize(pa)); }
-
Derleyici acisindan
void func(int *p, int size)
ilevoid func(int p[], int size)
arasinda fark yoktur. Ikiside bir pointeri gosterir. Dogal olarakvoid func(int **p, int size)
ilevoid func(int *p[], int size)
aynidir. Ancak*p[]
dizinin ilk elemanini gosteren pointerlar icin tercih edilir genelikle. -
**p
pointeri dereference yapip tekrar deference etmekdouble derefencing
veyadouble indirection
denir. -
*ptr ile *ptr[5] farkli anlamlara geliyor *ptr; ptr'nin gosterdigi gostericinin besinci elemanini dereference eder.
-
int *const p = &x
durumumda p'nin degeri degistirilirse sentaks hatasi verilir. const pointer to int. -
int const *p = &x
durumunda p'yi gosteren pointerin degeri degistirilirse sentaks hatasi verir. pointer to const int. -
*
ne'den once gelirse const olan odur. -
int** const ptr = &q
icinptr = &q
hata verir. -
int* const *ptr = &q
icin*ptr = &q
hata verir. -
int const **ptr = &q
icin**ptr = &q
hata verir. -
Dogal olarak const anahtar sozcugunun ve *'larin yerlesimi tamamen lojik tasarima baglidir.
-
const anahatr sozcugu birden fazla da kullanilabilir, mesela:
int const * const * const ptr
yukaridaki uc durumun da degistirilemez oldugu anlamina gelen gecerli bir ifadedir. -
Salt okuma amacli kullanimlarda fonksiyon prototiplerinde yazmayi unutmamak gerekir.
-
void pointers.
char *strncpy(char *pdest, const char *psource, size_t n)
buradaki aradakin
n karakterin uzerinden islem yapilmasini saglar. strcpy'den farki budur. Ayni durumstrcat, strncat
vestrcmp, strncmp
icin de gecerlidir. -
strncpy sona null karakteri koymasi icin n parametresinin yazinin boyutundan buyuk olmasi gerekiyor. Eger yazi daha kucukse null karakterin manuel olarak son elemana set edilmesi gerekmektedir.
-
strncpy(dest, source, 3)[3] = '\0';
kopyalamayi ilk 3 elemanda yapip 3 indisli elemana null karakteri ekler. -
Yazidan kucuk elemani kopyalamada otomatik olarak NULL karakterinin eklenmemesi soyle kullanilabilir: 012uga6789 yaratimi ornegi:
char source[100] = "tugay"; char dest[100] = "012345678"; //012uga6789 printf("(%s)\n", dest); strncpy(dest + 3, source + 1, 3); printf("(%s)\n", dest);// burada uga'yi 345 yerine koydu.
-
void
de bir turdur ancak bazi kisitlamalari vardir:- Bir nesnenin turu
void
olamaz. - Dizinin elemanlarinin turu de
void
olamaz dogal olarak.
- Bir nesnenin turu
-
Bir ifadenin turu
void
olabilir. Bu durum iki farkli sekilde karsimiza cikar:- Geri donus degeri olmaya fonkisyonun donus degeri yerine yazilir, bu fonksiyonlara
void function
denir. - Bir ifade void turune cast edilebilir. Tur donusturme operatorunun (
typecast
) hedef turuvoid
olabilir:(void) (x + 5)
gibi. Bu durum geri donus degeri olmasina karsin bu degerin istenmedigi - discard edildigi durumlarda kullanilir. Bu durumda bu geri deger donmemeyi bilerek yaptigimizi gostermek icin bu gosterimi kullaniriz. Fonksiyonu cagirdim ama geri donus degerini kullanmak istemiyorsam -(void)getchar();
gibi buradagetchar
normaldeint
donecektir ancak bu sekilde onu kullanmayacagimizi gostermis olduk.
- Geri donus degeri olmaya fonkisyonun donus degeri yerine yazilir, bu fonksiyonlara
-
int func(void);
func fonksiyonu parametre almiyor demek ikenint func()
fonksiyonun parametreleri hakkinda bilgi verilmedigi anlamina gelir. -
void*
turu bir pointer/adres turudur.void
ile karistirilmamalidir. -
void* vptr
icinvptr
herhangi turden bir nesnenin adresini tutabilir. Asagidaki atamalar gecerlidir:int x = 10; double dval = 2.31; char str[] = "oguz"; void* vptr = &x; vptr = &dval; vptr = &str;
-
void pointer'da dereferencing islemi gecersizdir
*vptr = 20;
veyavptr[2]
gibi. -
vptr + 5
gibi pointer aritmetigi de calismaz. -
void pointerla yapilabilecek islemler:
- ilk deger veya atama yoluyla ilk deger atanabilir:
vptr = &x;
Tam sayi gercek sayi atanamaz. - NULL pointer degeri atanabilir
vptr = NULL;
veyavptr = 0;
gibi. vptr == str
seklinde adres kontrollerinde kullanilabilir.
- ilk deger veya atama yoluyla ilk deger atanabilir:
-
T
herhangi bir tur olmak uzere: C dilindeT*
turundenvoid*
turune ve void turundenT*
turune otomatik donusumu vardir. Bu donusumler otomatik(implicit) oldugu haldeint* iptr = (void*)vptr
gibi explicit olarak da yapilabilir. -
Generic programlama
Turden bagimsiz programlama demektir.void*
burada cogunlukla kullanilir. -
Ture bagli olmayan fonksiyonlar yaratmak onlarla calismak icin generic programlamadan yararlanilir.
-
Ornek vermek gerekirse: herhangi turden iki degiskenin takas edilebilmesi icin bir fonksiyon yazma:
void gswap(void * vp1, void * vp2, size_t n) //generic swap { char *p1 = vp1; //char because it has 1 byte magnitude char *p2 = vp2; //char because it has 1 byte magnitude We will swap byte one byte right now. while(n--) { char temp = *p1; *p1++ = *p2; *p2++ = *temp; } } int x = 56443; int y = 34566; double d1 = 32.553; double d2 = 33.123; gswap(&x, &y, sizeof(int)); //sizeof used for determining type of variables from their sizes. gswap(&d1, &d2, sizeof(double));
-
Fonksiyonlarin icine gonderilen adresin, bir diziye mi ait oldugu veya ait ise o dizinin boyutu hakkinda fikir vermesinin olasiligi yoktur.
-
a(int) dizisinin ilk 4 elemani ile b(int) dizisinin son 4 elemanini takas etmek icin soyle bir fonksiyon cagirilabilir:
gswap(a, b + SIZE - 4, 4* sizeof(int));
-
string.h
'daki fonksiyonlar sadece stringler uzerinde calismaz, bellek bloklari uzerinde islemler yaparlar. -
memset
,memcpy
,memmove
,memchr
vememcmp
bu fonksiyonlardir. -
memset
bellek blogunu bir tamsayi ile doldurur. -
void *memset(void *vp, int val, size_t size);
icinvp
bellek blogunun baslangic adresi,val
doldurulacak deger, size ne kadar bellek alaninin(kac tane byte) doldurulacaginin boyutunu verir. -
Bellekte size ile calisirken genel pratik
eleman sayisi * sizeof(elemanin_turu)
olmali, 10 elemanli birint
dizi icin10 * sizeof(int)
olmali. -
void*
ile const void* icin diger turlerdeki durum gecerlidir. Salt okuma icin kullanilacak degiskenler fonksiyonaconst void*
ile alinir. -
memcpy
generic bir kopyalama islemini yapan fonksiyondur. Bir bellek blogunu bir yerden baska bir yere kopyalamak icinmemcpy
fonksiyonunu cagirabiliriz. -
void *memcpy(void *vpdest, void *vpsource, size_t n);
n tane byte kopyalar. -
Turden bagimsiz atama operatoru gibi dusunulebilir.
-
Elimizde iki bellek blogu varsa biri okuma biri yazma icinse, ve okuma icin olan yazma icin olanla cakisiyorsa buna
overlapped blocks
denir. -
memcpy
fonksiyonustrcpy
gibioverlapped block
larda tanimsiz davranis olusturabilir, boyle bir durumda kullanilmamalidir. -
Boyle durumlari belirtmek icin fonksiyon overlapped olmayan degiskenler almasi gerektigini belirtmek gerekir.
restrict
anahtar sozcugu ile tanimlanir,restrict
degiskenler overlapped etmeyen bellek bloklarini almak zorundadirlar. -
Bir dizinin elemanlarini 10 tane yana kaydirmak
overlapped block
durumuna ornek olabilir. -
overlapped block
olan durumlarda kullanilmasi gereken fonksiyonmemmove
dur. -
memchr
bir bellek blogunda belirli bir tamsayi degerine sahip bir byte arar. -
void *memchr (void * ptr, int value, size_t n);
prototipine sahiptir, value degerini ptr adresinden itibare n byte icinde arar. -
memcmp
turden bagimsiz iki bellek blogunu karsilatiran ve karsilastirma sonucunu donen fonksiyodur. -
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
prototipine sahiptir. -
Karsilatirmalar isaretsiz olarak yapilir.
-
void pointer
temelde generic fonksiyonlar tasariminda kullanilir, generic fonksiyon turden bagimsiz fonksiyon demektir. -
Turden bagimsiz olarak bir diziyi reverse edecek fonksiyon buna ornek olabilir:
void * greverse(void* vpa, size_t* size, size_t sz)
vpa
ilk elemanin adresi vesize
dizinin boyutu ikensz
bir ogenin boyutunu tutar. -
Buradaki ornekte dikkat edilmesi gereken onemli bir nuans vardir:
gswap
fonksiyonuna elemanlar boyutlarini belli edecek sekilde verilmelidir, bu sekilde farkli boyutlarda calisabilmesi mumkun olacaktir:void * greverse(void* vpa, size_t* size, size_t sz) { char* p = (char*)vpa; for(size_t i = 0; i < size / 2; ++i) { gswap(p + i * sz, p + (size - 1 - i) * sz, sz); //step size is i * sz for this swap operation.
-
memcmp
ile turden bagimsiz siralama fonksiyonu yazmak mumkun degildir, cunku bizim bellek karsilastirma fonkisyonumuz, byte byte calisir. Sadece esitligi kontrol edebiliriz bu sebeple buyuk kucuk karsilastirip siralayamayiz. -
Turden bagimsiz dizilerde siralama icin standart kutuphanedeki
qsort
fonksiyonu kullanilabilir. -
void **
turu generic bir pointer turu degildir.void **
turu sadecevoid *
turden bir nesne adresi anlamina gelen ifadenin turunu tutabilir. -
Callback mekanizmasi: Bir fonksiyona baska bir fonksiyonu arguman olarak gondermektir: Function Pointers
-
C
,Cpp
vejava
gibi dillerde bir fonksiyona arguman olarak bir fonksiyon gonderilebilir. -
Soyle somutlastirilabilir: a fonksiyonu b fonksiyonunu cagiriyor ve b'ye c fonksiyonunu arguman olarak veriyor.
-
Call-back: I sent you a function and you will call it back olarak da aciklanabilir.
-
Bu callback yapisi adres semantigiyle yani fonksiyon pointerlari gerceklenir.
-
Bir fonksiyon adresinin turu geri donus degerinin ve parametrelerinin turu ile birlikte belirlenir.
-
Dogal olarak
int foo(in t)
,int bar(double)
veint baz(void)
fonksiyonlarinin adreslerinin turleri farklidir. -
int toupper(int)
veint isalpha(int)
ayni turden fonksiyon adres turune sahiptir. -
int foo(int)
turunden bir fonksiyonun adresinin turu:int(*)(int)
seklinde gosteririlir. Bu notasyondonus turu(*(*)(argumanlarin turleri)
seklinde gosterilir. -
Geri donus degeri
int
olan iki stringi karsilastiranstrcmp
fonksiyonun adresinin turuint(*)(const char* const char*)
olur. -
Fonksiyonun adresini elde etmenin ilk yolu
&
islevini kullanmaktir:&foo
,&strlen
gibi. -
Diger yol ise
function to pointer
conversion'dur. Bu donusumarray decay/array to pointer
conversion'a benze yani dizinin ilk elemanin adres olarak kullanilmasi metodu&a[0] = a
esitligidir. -
function to pointer
conversion'u bir fonksiyonun isminin bir ifade icinde kullanildiginda derleyici fonksiyon ismini fonksiyonun adresine donusturur. -
int foo(double);
icin: asagidaki ikinci ifadefoo
de birincisi gibi&foo
adrese donusturulerek kullanilir.int foo(double); int main() { int (*fptr)(double);//foo's function pointer type. &foo foo fptr = &foo; //valid fptr = foo; //valid }
-
int (*fptr)(double)
gostericisi ileint func(int)
fonksiyonun adresini tutmaya calismak:fptr = &func
C'de yanlis C++'da sentaks hatasidir. -
Bir fonksiyon pointeri, global yerel yada statik/otomatik omurlu olabilir.
-
Fonksiyonlarin parametreleri ve geri donus degerleri funtion pointer olabilir.
-
Sistemde butun nesne gostericilerin boyutu nasil ayniysa, butun fonksiyon gostericilerinin de boyutu birbiriyle aynidir. Ancak nesne gostericisi ile fonksiyon gostericisinin boyutu ayni olmak zorunda degildir.
-
Fonksiyon cagirma operatoru
function call operator = ()
fonksiyonun ismiyle kullanildigi gibi ile de kullanilabilir.func()
olarak cagirilan fonksiyon(&func)()
yazildiginda da cagirilir. Oyleysefunc
fonksiyonunun adresini bir fonksiyon pointerina atayip onla da cagirabilirdik.void func(void); int main () { void(*fp)(void) = &func; //fp points func now. fp();//calls the func }
-
Fonksiyon cagrisinin fonksiyon gostericisi uzerinden yapilmasi cok sik kullanilan bir metoddur.
-
Fonksiyon pointeri en son hangi fonksiyonun adresini almissa, fonksiyon pointeri ile o fonksiyon cagirilir.
-
Haliyle fonksiyon pointer'i ismine bakarak hangi fonksiyonun cagirilacagi runtime'da belli olur.
-
void func(int (*)(int))
:func
fonksiyonun parametresi geri donus degeri ve aldigi argumanint
olan bir fonksiyon gostericisidir:int square(int a) { return a * a; } int multiply_2(int a) { return a * 2; } void func(int (*fp)(int)) { int result = fp(20);// equals to `square(20)` or `multiply_2(20)` funcs return vals. } int main() { func(&square);//pass square func as an function argument func(square); //pass square func as an function argument too func(multiply_2) // pass multiply_2 func as an function argument }
-
Yukaridaki ornekte
func
fonksiyonunun hangi ciktiyi uretecegi tamamen onun cagirildigi yerde kendisine verilen argumana baglidir,square
argumanini alir ise 400multiply_2
argumanini alir ise 40 doner, haliyle algoritmayifunc
fonksiyonu degil onu cagiranlar belirler. Fonksiyon hangi algoritmayi implemente edecegine karar veremez, sadece ona verileni implemente eder. Bu mekanizma siklikla kullanilir. -
Fonksiyon bildirimleri cogunlukla
typedef
bildirimleri ile fonksiyon adresi turlerine verilen es isimlerle kullanilir. Bunun sebebi aksi turlu cok karmasik bildirimlerin gerekmesi. Gorsel karmasiklik artar hata yapma riski yukselir.typedef int (*FPTR)(const char*,const char*); void *bar(int (*fpx)(const char*,const char*),int (* fpy)(const char*,const char*)); //without typedef void bar(FPTR fpx, FPTR fpy); //with typedef
-
Sonuc olarak genellikle function pointer'lar typedef(es isim) ile kullanilmalidir.
-
Fonksiyonlar function pointer ile cagirilirken
*
kullanilmasi sart degildir*fp() = fp()
ancak*fp()
ile cagirmakfp
isminin fonksiyon ismi degil adres ismi oldugunu vurgulamak icin kullanildigini gosterir. -
Yukaridaki duruma uygun sekilde bir function pointer ile fonksiyonu cagirirken operatorlerin oncelik tablosu dikkate alinmalidir. Sozgelimi fonksiyon cagirma operatoru
()
icerik operatorunden*
oncelikli oldugu icin icerik operatoru ile cagirilan fonksiyon*fp()
seklinde degil(*fp)()
seklinde cagirilmalidir. -
Fonksiyon pointerlarinin en sik kullanildigi yerlerden birisi generic fonksiyonlardir - turden bagimsiz fonksiyonlar
-
qsort
bu fonksiyonlara ornek ve en sik kullanilanlardan biridir. Turden bagimsiz olarak bir diziyi siralar. -
qsort
'un tanimi soyledir:void qsort(void *vpa, // adress of array that will be sorted size_t size, // size of array size_t sz, // size of element type of array int(*fp)(const void *, const void *) //used to compare two of elements of array - has same convention with strcmp )
-
Dorduncu parametre olan fonksiyonu biz yaziyoruz, yani
qsort
generic ancak kullandigi compare foksiyonu customdir. -
Haliyle dizinin turune gore bir compare fonksiyonu yazilir bu fonksiyon
qsort
'a verilmelidir. Fonksiyonun prototipi void * eleman alsa da iceride ilgili typecast islemi yapilmalidir
- Fonksiyonu cagiran fonksiyonlara
client
denir. - Callback mekanizmasinda cagiran fonksiyon soyle dusunulebilir: Ben parametreler ve bu parametreleri kullanacak fonksiyonun alirim ve generic olarak her tur parametre ve onlari kullacak olan fonksiyonun adresini alabilirim, islemi bana verilen fonksiyona yaptiririm ve onun bana verdigi donus degerini dogrudan ya da dolayli olarak cikti ederim.
- Function pointer array, mantiksal iliskiye sahip olan dizileri gruplandirmak ve onlarin arasinda dolasmak icin kullanilan bir yapidir.
- Ayni nesne ustunde farkli fonksiyonlar ile operasyon yapmak icin kullanilmaktadir.
int (*a)(int)
turunden elemanlari olan bir fonksiyon arrayiint (*a[10])(int)
ile tanimlanir.- Bunu typedef ile yapmak icin
typedef int(*FPTEST)(int)
tur es ismi ile adlandirilmis fonksiyon pointer'danFPTEST[10]
diyerek ayni ture sahip birden fazla fonksiyonun adresini bir dizide tutabiliriz.FPTEST a[] = {&isupper, &islower,...}
gibi. Bu diziden 5 indisli fonkisyonaa[5]('A')
sekli nde erisilebilir. - Kutuphanelerde cok sik kullanilan bir Callback kullanim ornegi: Bir fonksiyon default davranis olarak bir fonksiyonu cagiriyor. Ornegin
terminate
fonksiyonu default olarak abort islevini cagirir. Ancak kutuphane ismiset_terminate
olan bir islev verir.set_terminate
islevinin yaptigi fonksiyonu degistirmek icin kullanilabilir, boylece kutuphanedeki default davranisi degistirmek mumkun olur. - Bir diger kullanim ornegi: Cagirildigi zaman baska fonksiyonlari cagiran bir
foo
fonksiyonu olsun.foo
fonksiyonunun hangi fonksiyonlari cagiracagini ismifoo_register
olan baska bir fonksiyon belirlemektedir.foo_register
aldigi fonksiyonlar queue gibi yazar ve onlara erisilecektir.foo_register
fonksiyonuna kaydelilenler onunla ayni adres bloguna erisenfoo
fonksiyonu tarafindan cagirilir. - Multi-dimensional arrays'de kaldim 1.28
-
Ilk 17 dakikayi izlemedim.
-
User defined types, C'de en sik kullanilan araclardan birisidir.
-
C'nin nesne yonelimli paradigmaya yaklastigi konudur.
-
Kendi turlerimizi kendimizin olusturmasi icin kullanilan yapilardir.
-
Veri alip okudugumuz bilgisayar portunu bir tur olarak tanimlayabiliriz, ya da ogrenci turunden bir nesne tanimlayabiliriz.
-
Bunun icin yapilar
structures
, birliklerunion
ve integer type'ienumarations
vardir. -
Structure su sekilde tanimlanir.
struct Data { // Data is structure tag int mx; //members... double md; //Structure must have at least one member int a, b, c; int * ptr; //every type possible char str[100]; }; // end of definition int main() { struct Data mydata;//Legal Data mydata;// Data could not used without struct - it is wrong }
-
sizeof
operatoru struct'in icindeki elemanlarin boyutlarinin toplamini yani struct'in boyutunu verir.sizeof(struct Data)
seklinde yazilir. -
Bu yapi bildirimleri - structures, cogunlukla header dosyalarinin icinde tutulur.
-
Bildirimler yer tutmaz ancak onlardan instance yaratildiginda elemanlarin boyutu kadar yer tutar.
-
Static omurlu degiskenleri ilk deger vermeden yaratirsak 0 ile dolu olacak sekilde ilk deger alir. Pointer'lar ile null pointer olarak otomatik olarak tamamlanir.
-
Fonskiyonlara da arguman olarak struct'lar verilebilir. Fonksiyonun donus degeri struct olabilir:
struct Data func(struct Data x)
-
Bu turden bir nesneyi gosterecek bir pointer degisken de yine tanimlanabilir:
struct Data *ptr = &mydata;
-
C'de struct bildirimlerinde ilk deger vermek mumkun degildir. Ancak struct'in instance'inin bildiriminde degerler verilebilir.
-
Yapilarin elemanlarina erismek icin iki tane operator vardir, bunlarin ismi member selection operation:
.
dot ve->
arrow operatorudur. -
Bu iki operator de operator oncelik seviyesinde birinci siradadir.
.
operatoru->
operatorunden daha onceliklidir. -
Iki operator de yapinin elemanina erismek icin kullanilir,
.
operatoru yapinin kendisini ister->
operatoru ise adresini ister. -
.
operatorunun sol operandi bir yapi turunden nesne olmak zorundadir. Saginda ise bu yapinin elemanini yazmak gerekir. Yukarida tanimlanan mydata structure'i icinmydata.mx
gibi kullanilabilir.mydata.mx
artik herhangi bir integer degiskenle aynidir. Adresi isemydata
struct'inin icindedir. -
Bir yapinin icinde baska bir yapi tanimlanir.
struct Date { int x,y,z; }; struct Student { int no; char name[40]; struct Date bdate; // bdate is another struct hold birtday of students }
-
Bir yapi nesnesi sadece 4 operatorun operandi olabilir:
.
,->
,sizeof
,&
. -
sizeof
icinsizeof(struct Data)
ilesizeof(mydata)
ayni ciktiyi verir. -
struct Student
icin soyle bir nesne yaratilip atama yapilabilir:struct Student Cey = {122, "cey", {1,1,1999}}
-
Struct turunden baska bir ture donusum mumkun degildir. Yapi turleriyle calisirken otomatik tur donusumu mumkun degildir. Atama operatorunun sagi ve solu ayni turde olmalidir. Farkli iki struct birbirine atanamaz ve toplanamaz. Tur degistirme operatoru vs kullanmak da mumkun degildir.
struct A { int x; }; struct B { int y; }; int main() { struct A a1; struct B b1; int ival = 10; a1 = 10; //syntax error a1 = b1; //syntax error b1 = (struct B)10; // syntax error }
-
Ayni turden iki struct birbirine atanabilir, bu durumda atanan structin icindeki butun degerler tek tek atandigi structin elemanlarina kopyalanir. Ancak buyuk structurlarda cok maliyetli bir istir. Sonuc olarak bu kullanmasi mumkun bir operasyondur ancak buyuk structurlarda kullanilmaz.
-
Bunun yerine struct'in elemanlarini secip karsi tarafta da ayni elemanlara atamasi yapilir.
struct Data { int x, y, z; double dval; }; int main() { struct Data a = {10, 20, 30, 4.5}; struct Data b; b = a; //legal but not used generally for big structures. b.x = a.x; b.y = a.y; b.dval = a.dval; memcpy(&b, &a, sizeof(struct Data)); //equals to b = a; }
-
const struct
larin elemanlarina atama yapmaya calismak yine syntax hatasidir. -
Bildigimiz gibi dizileri dogrudan birbirine atayamayiz ancak struct'in elemani olan diziler birbirine atanabilir, bu sekilde dolayli yoldan bu islemi gerceklestirmek mumkun olur.
-
C'de butun yapilar public'tir C++'daki gibi private elemanlar yaratilmasi soz konusu degildir.
-
Struct'in icinde dizi varsa ilk deger atanirken yukardaki student structure'indaki date arrayi gibi kume parantezi kullanilir ancak kullanilmasa dahi hata vermez sirayla o elemanlara atama yapilir:
struct Student Cey = {122, "cey", {1,1,1999}}
ilestruct Student Cey = {122, "cey", 1,1,1999}
ayni anlama gelir. -
Ancak kume parantezlerini kullanmak ozellikle cok boyutlu diziler iceren struct'lara ilk deger verirken fazlasiyla onemlidir, kullanilmalidir.
-
Asagida ilk deger tanimlanmasi yapilan bir struct verilmistir.
struct Data { int x, y; int a[20]; const char* p; char str[40]; }; int main() { struct Data mydata = { .x = 10, .y = 40, .a = { [10] = 5, [13] = 9}, //designated initializer syntax .p = "movado", .str = "zenith" //static storage }
-
Ismi olmayan turden degiskenlerin tanimlanmasi da yine mumkundur. Ancak orada elemanlarin direk struct'in sonunda belirtilmesi gerekir.
struct { int a, b, c; }x1, x2 = {1,2,4};// x2 has initial value but x1 has not.
-
Pointer to Structure Objects, Struct adresini tutan pointer turu demektir:
struct Data { int a, b, c; }; int main() { struct Data* p; // pointer to structure struct Data mydata; struct Data a[10]; struct Data *p = &mydata; // same usage p = a; // same usage }
-
Yapi ne kadar buyuk olursa olsun pointer'in boyutu integer pointer boyutu kadardir.
46 Pointer to Struct & Yapi Turleri ve Typedef Bildirimleri & Yapi Nesneleri Uzerinde Islem Yapan Fonksiyonlar
-
Struct'in
a
isimliint
elemani olsun ve buna pointeri olanptr
uzerinden erismeye calisalim. Bu durumda yazmamiz gereken ifade(*ptr).a
'dir. -
Ancak Struct pointer'larina erisirken yukardaki yapidan ziyade
->
ok operatoru kullanilir. -
Ok operatorunun sol operandi bir yapi nesnesi adresi olmali.
-
Ok operatorunun sag operandi sol operandi olan yapi nesnesinin elemanlarindan biri olmali.
-
Gunun sonunda
ptr->b
ile(*ptr).b
ayni anlama gelir. -
Bir structure adresiyle bir fonksiyona arguman olarak gecilebilir.
struct Data { //Data name is called as structure tag. int x, y; double dval; char buffer[16]; }; void func(struct Data* ptr) { ... } int main() { struct Data mydata = {1, 3, 4.5 , "breitling" }; func(&mydata); }
-
C'de struct tag'leri yeni instance yaratilirken tek basina kullanilamaz:
Data mydata;
hata verirkenstruct Data mydata
olmasi gerekendir. -
Bu sekilde her zaman
struct
anahtar sozcugunden kurtularak kullanmak icintypedef
bildirimleri kullanilmaktadir. -
typedef
bir turun yerine es isim kullanilmasi idi, bu bildirimletype alias
yaratmak. -
Structlarda typedef kullanmanin avantaji Struct on ekinden kurtularak yazimi kolaylastirmak.
-
Structlara es isim verildigi gibi struct pointerlarina da verilebilir:
typedef struct Data* DataPtr
. -
Typedef bildirimi su sekilde yapilabilir:
typedef struct Data { int x, y; double dval; char buffer[16]; } Data; int main() { //Both equation has same meaning. Data mydata = {3, 7, 6.7}; struct Data mydata = {3, 7, 6.7}; }
-
Struct'lar genel olarak kutuphaneler yazilirken kullanilir. Kutuphane fonksiyonlari bu struct'larin turunden parametreler alan ve geri donduren fonksiyonlar olmaktadir.
-
Bir fonksiyonun parametresi struct turunden olabilir, bu durumda bu fonksiyonu cagirirken yapi turunden bir nesne verilmelidir. Bu cagri bir cesit
call by value
cagrisidir. Dogrudan structure verilir.Call by value
bir kopyalamaya neden oldugu icin maliyet fazladir. -
Haliyle bu tarz bir kullanim sadece kucuk struct'larda kullanilabilir.
-
Struct'in boyutu buyukse fonksiyona nesneyi degil nesnenin adresini veririz, fonksiyon ciktilarini adresini aldigi nesneye yazarak doner.
-
Bu tarz fonksiyonlara
accessor
/getter
/get function
isimleri verilir. -
Bu fonksiyona cagri nesnenin kendisiyle degil adresiyle olacaktir. Bu cagri bildigimiz uzere
call by reference
olarak adlandirilir. -
Fonksiyonlar adresini aldiklari structure'lari iki sekilde kullanabilir, bunlardan birincisi sadece islem yapip o struct'i doldurmak icin olan yontem
out
, digeri ise hem once doldurulmus bu struct'i okumak ve/veya degistirmek icin kullanilan yontemin-out
-
Bununla beraber fonksiyon icinde bir struct yaratilip o geri donus parametresi olarak da girilebilir.
struct Watch create_random_watch(void)
fonksiyonu gibi. -
Bu tarz fonksiyonlar da ancak kucuk yapilarla calisirken kullanilabilir.
-
Fonksiyonun geri donus degeri struct pointer turunden bir degisken olabilir.
struct Watch* foo(void)
seklinde fonksiyon tanimlanir. -
Adres donduren bir fonksiyon otomatik omurlu bir nesnenin adresini donderemez, haliyle struct'lar icin de bu durum gecerlidir.
-
Boyle durumlarda fonksiyon icinde memory allocation ile bir bellek alani yaratilir, bu bellek alanini gosteren pointer donderilir.
struct Watch* create_watch(void) { struct Watch* pd = (struct Watch *) malloc(sizeof(Employee)); pd->id = 122233; pd->dimension = 41; return pd; }
-
Fonksiyonlar cagirilirken aldigi parametre degiskeni ve geri donus degeri struct turunden bir adres olabilir bu durumda ayni struct'in adresini geri dondurur.
-
Klasik struct yapisindaki durumda yazilimci calistigi struct'in butun elemanlarini ve alabilcegi degerleri bilmeli, burada her struct icin ogrenme yuku vardir. Gomulu sistemlerde genelde bu yapilar kullanilir.
-
C'de Nesne yonelimli programlamaya en yakin yapilar struct'lardir. Bazi C kutuphane ve modulleri oop yaklasimiyla olusturulmustur.
-
Struct'taki elemanlara dogrudan erismek ve degistirmek yerine o struct'a fonksiyonlar araciligiyla erismek kullanilan bir baska yontemdir, yazilimci asla dogrudan struct uzerinde calismaz.
-
Client kodun butun elemanlara erisip yanlis degerler girmesini vs engeller. Hizmet veren kodlar disinda hicbir kod blogu bu struct'lara dogrudan erisemez. Bu degerlere dogrudan erisen fonksiyonlar haliyle az oldugu icin burada yapilar degistiginde az bir kod degistirme eforu olur.
-
Bu sistem OOP'deki public private yapilara benzetilmistir.
-
C'de iki tarz kutuphane vardir, bunlar C tarzi kutuphane ve OOP tarzi kutuphane olarak adlandirilabilir.
-
C tarzi kutuphaneler yapilarin butun elemanlari hakkinda bilgi sahibi olmayi gerektiren ve dogrudan erisip uzerinde islemler yapildigi kutuphanelerdir.
-
OOP tarzi kutuphaneler ise client kodun fonksiyonlar uzerinden nesne uzerine erismeye calistigi dogrudan erisimi kapandigi kodlardir. Nesnenin elemanlariyla dogrudan etkilesime sadece kutuphane fonksiyonlari uzerinden yapilabilir. C++'deki private erisim gibi bir duruma benzetilmistir.
-
time_t
epoc
tan gecen sureyi ifade eder,Epoc
sistem tarafindan belirlenmis orjin timepointidir, meselaunix
sistemler icin 1.1.1970 noktasidir. Genelliklelong long
turunden degiskendir. -
struct
tm
kutuphane struct'i bununla ilgili bir cok elemana sahiptir. -
locale
belirli kutuphanelerde belirli kurallar setine denir. Para birimi euro olsun saat zamani gmt +2 olsun gibi. Buna uygun fonksiyonlaralocale-dependent
, uygun olmayanlara iselocale-independent
denir. -
locale.h
fonksiyonundakisetlocale
bu localizasyon degisikliklerinin yapilmasi gerekmektedir. -
local bu sekilde degistirdikten sonra local-dependent fonksiyonlar o lokaldeki konfigurasyona gore calismaya baslayacaktir.
-
strftime
fonksiyonu zaman bildirimini secilmis olan locale gore formatlar. -
clock_t
bir sureyi takip ediyor, n tane clock_t bir saniyeye bedel,sistemden sisteme degisen bir degerdir. 1/n = CLOCK_PER_SEC denklemine gore ilerlenir. -
clock()
fonksiyonu parametre almaz. Cagirildigi ana kadar main'in baslangicindan ne kadar zaman gecmis bunuclock_t
tipinde olcak sekilde doner. -
Yapilan islemlerin ne kadar suruldugunu(saniye cinsinden) hesaplamak icin start ve end degerleri yaratilip fark alinir.
int main() int a[SIZE]; clock_t start = clock(); sort_array(a,SIZE); clock_t end = clock(); printf("%ld clock tick\n", end - start); printf("%f clock tick\n", (double)(end - start) / CLOCKS_PER_SEC);
-
CLOCK_PER_SEC
yerine standart olmayan ama yaygin bir diger degerCLK_TCK
olarak tutulur. -
sleep
standart bir C fonksiyonudur, icinde ms cinsinden parametre alir. -
mktime
bulunan zamandan belirli bir sure onceki timepoint'i return eden bir fonksiyondur, girdi olarak girilen zaman time_t turunden olmalidir. Cikti da boyledir. -
OOP tarzi kutuphanelerde yapinin elemanlari client kodun erisemeyecegi veya degistiremeyecegi tasarimdadir.
-
Bunu yapmanin birinci yolu degistirlmesi istenmeyen elemani void pointer ile gostermektir. Bu durumda client kod bu yapinin elemanlari hakkinda bilgi sahibi olamaz erisemez.
struct Data { void* vp; }; int main() { struct Data mydata; //mydata.vp // even reach it could not be changed. }
-
Ikinci yol, struct'in elemanlarinin gosterildigi ancak public private olanlarin
_
notasyonu ile ayrildigi senaryodur:x
public ikenx_
private demektir. Client kodu yazan bunu bilerek hareket eder. -
Elemanlara erismek icin elemanlari cagiran fonksiyonlar kullanilir, bu en yaygin yoldur. Nesnenin adresi cagirma fonksiyonuna gonderilir, temel ilke budur.
-
1.48de kaldim.