C pointerlara giriş

Merhaba.

Bu yazı C’de pointerları çalışırken aldığım notlardır.

Bu pointerların vikipediadaki tanımı:

Programlama dillerinde bellek adreslerini saklayan değişkenlere verilen genel isim. Bir programlama dilinde herhangi bir değişkeni tanımladığınızda hafızada ona bir yer ayrılır. İşaretçiler bu hafıza alanlarının adreslerini tutarlar

Bu konuyu anlamamda yardımcı olan şeylerin başında değişkenlerin tanımı geliyor. İşte birbaşka tanım

Değişkenler, bir bilgisayar programında değerleri saklamak için kullanılan, bilgisayar bellek konumlarına verdiğiniz adlardır.

Hepinizin zaten bildiği gibi bir bir programlama dilinde değişken tanımladığımızda değeri memory’e konulur.İsmi ilede Bu değişkenin değerini memory’den alırız.

Basitçe aşağıdaki gibi yazacağımız bir program ile memory’deki yerimizi görelim.

#include <stdio.h>

int main() {

short age = 20;
printf("Address of age variable %p \n", &age);
return 0;
}

Çıktı tabiki her çalıştırdığımızda farklı olucaktır.

Örnek çıktı:

Address of age variable 0x7ffe5886c856

Bu örnekte 0x7ffe5886c856 değeri bizim ram bellek üzerinde short tipindeki age değişkenimiz için işletim sisteminin ayırdığı alan.

Pointer

Pointer, başka bir değişkenin adresini, yani bellek konumunu(az önce ekrana yazdırdık) tutarlar.

Pointerlar değişkenler gibi tanımlanırlar.Sadece başlarında *(asteriks) işareti bulunur.

Hemen bir örnek yapalım.

short *age_p;    /* pointer to an short */
printf("Address of age_p variable %p \n", &age_p);

Daha önce üzerinde çalıştığımız programa yukarıdaki satırları ekliyorum.

Address of age variable 0x7ffe1b87255e
Address of age_p variable 0x7ffe1b872560

Gördüğünüz gibi age_b memory’de farklı bir yerde.Aralarındaki fark değişken direk veriyi tutuyor.Pointer ise asıl verinin durduğu değişkenin adresini tutuyor.

Şu ana kadar elimizde short tipinde bir değişken ve onun adresini tuması için tanımladığımız bir pointer var. Aşağıdaki blokta yapacağımız tanımlama ile artık memory’de age_p’nin içerisinde age değişkeninin adresi duruyor. Başına * işareti koayarak değerini yazarsanı veya değiştirirseniz. Aslında age değişkeninin değerini değiştireceksiniz.

age_p = &age;
printf("Value of age_p pointer %d \n", *age_p);

Valıe of age_p pointer 20

Programın son halinde ise memory aşağıdaki gibi olucaktır.

Programın son halinde ram bellek temsili.

Gördüğünüz gibi aslında pointer olarak tanımladığımız değişken asıl değeri tutan değişkenin adresini tutuyor. * işareti ilede eriştiğimizde bu alana gidip buradaki değer ile işlem yapıyor.

Basitçe neden pointler kullanıyoruz.

— Aklımıza gelen ilk madde bellek tasarrufu.Anlaşılır olması açısından bu örneği short gibi küçük bir veri tipi ile yaptık. Peki ya daha büyük veri tipleri ile çalışsaydık.Bunları fonksiyonlara parametre olarak geçtiğimiz zaman compiler bu veriyi memory üzerinde kopyalayacaktır. Buda büyük objelerde bellekteki verimi düşürecektir. Bunu yerine eğer referansını geçerseniz fonksiyonun içerisindeki işlem doğrudan memory’deki yerinde yapılacaktır.Memory’deki tekrarları engellememizi sağlar. Ayrıca bknz( https://metinagaoglu.com/call-by-value-call-by-reference )

İterasyon sırasında hızı artırabilirler: örneğin bir arrayi gezmek isterseniz, indis değerini yinelemek için sadece bir for döngüsü yazarsınız, ancak gerçekte bellek adresini artırmış olursunuz. Dolayısıyla, performansa duyarlı bir kodlama yapıyorsanız, önce indisi artırmak ve ardından bellek adresini güncellemek yerine, pointer ile doğrudan bir sonraki bellek konumuna gidip performans iyileştirmesine erişebilirsiniz.

Yapılan değişiklikler her yerde aynıdır: bağımsız thread’ler ile çalışan bir programınız olduğunu varsayalım.Her biri için aynı verilerin kopyasını oluşturmak yerine, onlara orijinal verilerin adresini sağlarsınız ve böylece değerde herhangi bir değişiklik yapılırsa, her thread en son güncellemeleri alır.

Şimdilik bu kadar.Pointerlar ile ilgili aritmetiği vb. gibi konulardada özet yazılar yazmayı planlıyorum.

Kaynaklar

https://www.tutorialspoint.com/cprogramming/c_pointers.htm

https://www.quora.com/Why-we-use-POINTERS-in-programming-whats-the-practical-use

Call by value & call by reference

Selamlar

Bu konu hakkında yazılmış birçok makale var.Bende kendim daha iyi anlamak için kendi bloguma karalama tarzında bir yazı yazacağım.

Yazdığımız programlama dillerinde hepimiz fonksiyonlar/metodlar tanımlıyoruz. Bunlara parametre geçiyoruz bazen dönüş değeri olarak , bazen ise verdiğimiz parametreler üzerinde değişiklik yapıp sonucunu alıyoruz.

Programlama dillerinde, fonksiyonlar iki şekilde çağrılabilir: Call by Value ve Call by Reference.

Genelde fonksiyon method tanımlamalarınıza extra bildirmediğiniz sürece “reference by value” yaklamşımı kullanılır. Bizde ilk önce bunu bir örnek ile göreceğiz.

Call by value

Buraya kadar tamam. Normal şartlarda bu nasıl gerçekleşiyor basit bir örnek yapalım.İlk örneğimizde 2 değişkeni birbiriyle değiştiren klasik bir swap fonksiyonu yazcağız.Örneğimizi inceleyelim.

#include <stdio.h>


void swap(int a,int b) {
    int temp;
    temp = a;
    a = b;
    b = temp;
}

int main() {

    int a = 50 , b = 20;
    printf("a: %d \t b: %d \n",a,b);

    /* calling a function to swap the values */
    swap(a, b);

    printf("a: %d \t b: %d \n",a,b);

    return 0;
}

Yukarıdaki kodu incelersek standart bir swap fonksiyonumuz var. Ancak bunu çağırsak bile çıktı değişmiyor. a ve b değişkenlerimiz değerlerini koruyorlar.

metinagaoglu@linux-mint:~/Desktop/C/refs$ ./ref
a: 50 b: 20
a: 50 b: 20

Reference by value’da bir değişkenin değerini bufonksiyonun parametresine kopyalar. Bu nedenle, ana işlevin parametresinde yapılan değişiklikler bağımsız değişkeni etkilemez.Saddece fonkssiyonun içerisinde geçerliğidir.Aynı değerden memory’de 2 tane vardır. Parametre olarak geçilenler direk değerleri kopyalanmıştır.

Yaptığınız değişiklikler kopyalanan değerler üzerinde yapılır.Foksiyon dışındakiler etkilenmez.Fonksiyo çalıştıkran sonra kopya silinir ve memory’de sizin geçtikleriniz aynen duruyordur. Bu yüzden çıktılarımız aynı.

Call by Reference

Call by reference ta ise fonksiyona parametre olarak referencelarını geçiyoruz.Yani memory’de nerde olduklarını. Fonksiyonun içerisinde ise bu değerler ile memory’deki yerini bildiğimiz değişkendeki değerlerle gene istediğimiz işlemi yapabiliyoruz. Diğer yaklaşımdan farklı değerler kopyalanmadığı için yaptıklarınız her değişiklik değeri değiştiriyor.

Az önce yaptığımız swap fonksiyonunu şimdide call by reference ile yazalım.

#include <stdio.h>

void swap(int *a,int *b) {
int temp;
temp = *a;
*a = *b;
*b = temp;
}

int main() {

int a = 50 , b = 20;
printf("a: %d \t b: %d \n",a,b);

/* calling a function to swap the values */
swap(&a, &b);

printf("a: %d \t b: %d \n",a,b);


return 0;
}

Bu seferki durumda ise gördüğünüz gibi main içerisinde swap fonksiyonmuza & işareti ile referansını geçtik. İçeride ise parametre olarak geçilen değerin referansı ile memorydeki adresine ulaştık ve o değerler üzerinde işlem yaptık.

metinagaoglu@linux-mint:~/Desktop/C/refs$ ./ref
a: 50 b: 20
a: 20 b: 50

Tabiki biz bu yazıda gene kolayına kaçtık ve 2 integer değişkeni kullandık.Ancak örnekleri bununla sınırl tutmayın.Elimizde bir array veya linkedlist vs türünde büyük bir değer varsa ve bunu üzerinde işlem yapıcaksak şeklindede düşünebilirsiniz.

Ne olduğunu kabaca anladığımıza göre biraz avantaj ve dezavantajlarından bahsedelim.

Call by value avantajları

  1. Parametre olarak geçilen ana değişkenler değişmez.
  2. Takibatı ve debug yapması daha kolaydır.Ana değişkendeki değerleri değiştirmediği için fonksiyon içinde daha rahat geçici işlem vs yapılabilir.

Dezavantajları

  1. Hafıza açısından verimli değildir.Aynı değişken için oluşturulmuş iki adet kopya vardır..

Call by reference avantajları

  1. Fonksiyon argümanın değerini değiştirebilir.
  2. Bellek alanından tasarruf etmenize yardımcı olur. Tek bir değeri tutmak için yinelenen veriler oluşturmaz.
  3. Bu yöntemde işlem yapılan parametrenin bir kopyası yoktur. Bu nedenle çok hızlı işlenir.

Dezavantajları

  1. Referans alan bir fonksiyonunu parametresinin boş olmadığından emin olması gerekir.
  2. Çok iş parçacıklı programlarla çalışırken tehlikelidir.
  3. Takibatı ve debug’ı bi nebze daha zordur.

Göz atmanızı tavsiye ettiğim kaynaklar:

https://stackoverflow.com/questions/40185665/performance-cost-of-passing-by-value-vs-by-reference-or-by-pointer

https://www.geeksforgeeks.org/difference-between-call-by-value-and-call-by-reference/

https://www.guru99.com/call-by-value-vs-call-by-reference.html

C programlama dilinde string fonksiyonları

Herkese merhaba , önceki yazımda c ile char veri tipini görmüştük.Bu yazıda ise string manipülasyonu yapan birkaç fonksiyondan bahsedeceğiz.

strcat()

İlk fonksiyonumuz strcat(contcatenates).Strcat iki char tipindeki değişkeni birleştirmeye yarar.2 tane parametre alır.Hedef ve kaynak.

char *strcat(char *destination, const char *source)

Strcat () fonksiyonu, hedef string’i ve kaynak stringini birleştirir ve sonuç hedef değişkeninde saklanır.

Hemen örnekle görelim.

#include <stdio.h>
#include "string.h"

int main() {
    char str1[50] = "My name is", str2[] = "metin";

    strcat(str1,str2);

    puts(str1);
    puts(str2);
    return 0;
}

Sonuç

My name ismetin
metin

Dikkat etmemiz gereken nokta arkadaşlar birleştirilen string 1. parametrede kalmaktadır.

2. nokta ise strcat() , destionation string yan 1. parametrenin boyutu source stringini saklayacak kadar büyük olmalıdır. Değilse, segmentasyon hatası hatası alırsınız.

C strcmp()

Diğer göreceğiniz fonksiyon string compare, parametre olarak verilen 2 string karşılaştırır.Eğer birbirleri ile aynı ise 0 döndürür.

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

Strcmp () işlevi iki char değişken alır ve geriye integer döndürür.

İki stringin ilk karakteri eşitse, iki stringin de sonraki karakteri karşılaştırılır. Bu işem iki string değerin karakterleri birbirleri ile farklı olana veya boş bir ‘\ 0’ karakterine ulaşılana kadar devam eder.

Hemen örneğimizi yapalım.

#include <stdio.h>
#include "string.h"

int main() {
char var1[] = "eth0";
char var2[] = "eth0";
char var3[] = "wan";

//comparing var1 and var2
if( strcmp(var1,var2) == 0 ) {
printf("These variables are same \n");
}

//comparing var1 and var3
if( strcmp(var1,var3) != 0 ) {
printf("These variables are not same \n");
}

}

Sonuç

These variables are same
These variables are not same

C strcpy()

String copy fonksiyonu olan strcpy() fonksiyonuna göz atalım.Fonksiyon strcat gibi önce char tipnide destionation sonrada source parametresi alıyor.

char* strcpy(char* destination, const char* source);

Strcpy () foksiyonu, source parametresi ile gelen string’i (boş karakter dahil) destination değişkenine kopyalar.

#include <stdio.h>
#include "string.h"

int main() {

char str1[20] = "PHP";
char str2[15];

strcpy(str2,str1);
puts(str2);
return 0;
}

Çıktı

PHP

Gördüğümüz gibi başta boş olan str2 değişkeni copy komutundan sonra ekrana kopyalandığı değeri veriyor.

C strlen()

Son göreceğimiz fonksiyon ise string length fonksiyonu.bu fonksiyon parametre olarak verilen string değerin uzunluğunu unsigned size_t olarak geri döner.

#include <stdio.h>
#include "string.h"

int main() {

char var1[20]="C programming";
char var2[20]={'P','r','o','g','r','a','m','\0'};

int len = strlen(var1);

// using the %zu format specifier to print size_t
printf("Length of string var1 = %zu \n",strlen(var1));
printf("Length of string var2 = %zu \n",strlen(var2));

return 0;
}

Çıktı

Length of string var1 = 13
Length of string var2 = 13

Strlen () fonksiyonunun uzunluğu hesaplarken \0 null karakterini saymadığını unutmayın.Bu kısmını kullanıcdan soyutlar.

Not: bu yazıda ele aldığımız fonksiyonlar <string.h> içerisinde tanımlıdır.Kullandığımız kodlarda bunu include ettiğimize dikakt edin.

Teşekkürler…

C programlama dilinde stringler

Selam , bu yazıda C programlama dilindeki char veri türünden bahsedeğiz.Aslında yapısı itibariyle üst seviyedeki programlama dillerinden farklıdır.Özellikle farklı dilden geçenlerin zorlandığı bir konu olduğunu düşünüyorum.Lafı çok uzatmadan başlıklara inelim.

Aslında C programlama dilindeki stringler sıfır karakter \0 ile sonlandırılmış bir karakter dizisidir. Örneğin:

char c[] = "c string";

Derleyici, çift tırnak işaretleri içine alınmış bir dizi karakterle karşılaştığında, varsayılan olarak sonuna bir null karakteri \ 0 ekler.

Bir string nasıl tanımlanır?

C’de string değişkenelri aşağıdaki şekilde tanımlayabilirsiniz.

    char c[] = "abcd";

    char c[50] = "abcd";

    char c[] = {'a', 'b', 'c', 'd', '\0'};

    char c[5] = {'a', 'b', 'c', 'd', '\0'};

Stringlere sonradan değer atama

Diziler ve diziler C’dek tanımlandıktan sonra atama operatörünü desteklemezler. Örneğin

char name[100];
name = "Metin";  // Error! array type is not assignable.
string_example1.c:16:17: warning: initializer-string for array of chars is too lon

Kullanıcıdan string okuma

Kullanıcıdan string okumak için scanf () fonksiyonunu kullanabilirsiniz.

Scanf() fonskyinonuda dikakt etmeniz gereken boşlukla (boşluk, satırsonu, sekme vb.) karşılaşana kadar karakter dizisini okur.

Örnek 1: scanf() ile string okuma

#include <stdio.h>
int main()
{
    char name[20];
    printf("Enter name: ");
    scanf("%s", name);
    printf("Your name is %s.", name);
    return 0;
}

Output

Enter name: Metin Ağaoğlu
Your name is Metin.

Metin Ağaoğlu yukarıdaki programa girilmiş olsa da, name değişkeninde sadece “Metin” saklandı. Metin’den sonra boşluk ile karşılaştığı için okumayı durdurdu..

Bir string satırı nasıl okunur?

Bir string satırını okumak için fgets() fonksiyonunu kullanabilirsiniz. Sonrasında görüntülemek için puts () fonksiyonunu çağırabilirsiniz.

Örnek 2: fgets() ve puts()

#include <stdio.h>
int main()
{
    char name[30];
    printf("Enter name: ");
    fgets(name, sizeof(name), stdin);
    printf("Name: ");
    puts(name);
    return 0;
}

Burada, kullanıcıdan bir dizge okumak için fgets () işlevini kullandık.

fgets (isim, sizeof (isim), stdlin); // dizeyi oku

Sizeof (isim) değeri 30 olur. Dolayısıyla, girdi olarak isim dizgisinin boyutu olan en fazla 30 karakter alabiliriz.

Dizeyi yazdırmak için puts (name); kullandık.

String değişkenlerini fonksiyonlara parametre olarak geçme

C’de stringler, dizilere benzer şekilde bir fonksiyona parametre olarak aktarılabilir. Hemen bir örnek yapıp bunu görüntüleyelim.

Stringi fonksiyona parametre geçirme

#include <stdio.h>
void displayString(char str[]);

int main()
{
    char str[50];
    printf("Enter string: ");
    fgets(str, sizeof(str), stdin);             
    displayString(str);     // Passing string to a function.    
    return 0;
}
void displayString(char str[])
{
    printf("String Output: ");
    puts(str);
}

Sonraki yazıda ise C’deki string leri fonksiyonlar ile nasıl müdahele edip üzerinde işlem yaparız bunu ele alacağız.

Teşekkürler…

Kaynak: https://www.programiz.com/c-programming/c-strings