Birlikler, aynı bellek anını paylaşan üyelere sahiptir ve yapılar gibi türetilmiş veri tipidir. Bir programda farklı durumlar için bazı değişkenler birbiri ile ilgili değildir fakat diğer değişkenler birbiri ile ilgili olabilir. Bundan dolayı birlikler kullanılmayan değişkenlerin boşu boşuna hafıza kullanımını önlemek için aynı hafıza alanını paylaşır. Birlik üyeleri herhangi bir tip olabilir. Union'da saklanacak byte miktarı, en az birliğin büyük hafıza alanı isteyen üyesini alacak şekilde olmalıdır. Çoğu durumda union'lar iki veya daha fazla veri tipi içerir. Kullanımda, sadece bir üye ve böylece bir veri tipi referans olarak kullanılabilir.


Union/Birlik Bildirimi

Birliklerin bildirimi de yapılarda olduğu gibidir. Yalnızca struct anahtar sözcüğü yerine union anahtar sözcüğü kullanılır.

union [birlik_ismi] {
    <tür> <birlik_elemanı>; 
    <tür> <birlik_elemanı>; 
    <tür> <birlik_elemanı>; 
    ...
}

Yukarıdaki genel biçimde:

union :                 Bildirim için gerekli bir anahtar sözcüktür.
birlik_ismi :         BirliÄŸi anlatan isimlendirme kurallarına uygun herhangi bir isim olabilir.
birlik_elemanı :   Birlik elemanıdır; isimlendirme kurallarına uygun herhangi bir isim olabilir.


Örnek:
union number {
int x;
double y;
};

Union/Birlik Değişkenlerinin Tanımlanması

Tıpkı yapılarda olduÄŸu gibi birliklerde de yer ayırma iÅŸlemi bildirim ile deÄŸil tanımlanma iÅŸlemi ile yapılmaktadır. Birlik bildirimlerinin yapı bildirimlerinde tek farkı struct anahtar sözcüğü yerine union anahtar sözcüğü kullanılmasıdır. 

Genel biçimi:

union <birlik_ismi> <birlik_deÄŸiÅŸkeninin_ismi>;

Örneğin;
union number {
int x;
double y;
}a,b;
veya

union number a,b;

Örnekler

1) union ve struct arasındaki fark
#include <stdio.h>
union unionJob
{
//defining a union
char name[32];
float salary;
int workerNo;
} uJob;

struct structJob
{
char name[32];
float salary;
int workerNo;
} sJob;

int main()
{
printf("size of union = %d bytes", sizeof(uJob));
printf("\nsize of structure = %d bytes", sizeof(sJob));
return 0;
}
Ekran Çıktısı:

size of union = 32 bytes
size of structure = 40 bytes

2)
#include <stdio.h>
#include <string.h>

union Data {
int i;
float f;
char str[20];
};

int main( ) {

union Data data;

printf( "Memory size occupied by data : %d\n", sizeof(data));

return 0;
}

Ekran Çıktısı:

Memory size occupied by data : 20

3)
#include <stdio.h>
#include <string.h>

union Data {
int i;
float f;
char str[20];
};

int main( ) {

union Data data;

data.i = 10;
data.f = 220.5;
strcpy( data.str, "C Programming");

printf( "data.i : %d\n", data.i);
printf( "data.f : %f\n", data.f);
printf( "data.str : %s\n", data.str);

return 0;
}

Ekran Çıktısı:

data.i : 1917853763
data.f : 4122360580327794900000000000000.000000
data.str : C Programming

4)

#include <stdio.h>

// Declaration of union is same as structures
union test {
int x, y;
};

int main()
{
// A union variable t
union test t;

t.x = 2; // t.y also gets value 2
printf("After making x = 2:\n x = %d, y = %d\n\n",
t.x, t.y);

t.y = 10; // t.x is also updated to 10
printf("After making y = 10:\n x = %d, y = %d\n\n",
t.x, t.y);
return 0;
}
Ekran Çıktısı:

After making x = 2:
 x = 2, y = 2

After making y = 10:
 x = 10, y = 10

5)
#include <stdio.h>

union test {
int x;
char y;
};

int main()
{
union test p1;
p1.x = 65;

// p2 is a pointer to union p1
union test* p2 = &p1;

// Accessing union members using pointer
printf("%d %c", p2->x, p2->y);
return 0;
}
Ekran Çıktısı:

65 A


C programlama ile ilgili olan diğer konulara aşağıdaki linkten ulaşabilirsiniz:


Kaynaklar:
A'dan Z'ye C programlama
Deitel C/C++
https://www.tutorialspoint.com/cprogramming/c_unions.htm
https://www.programiz.com/c-programming/c-unions
https://www.geeksforgeeks.org/union-c/


 

Bu yazımızda, C programlamanın en önemli elemanlarında bir olan yapılar (struct) konusuna deÄŸineceÄŸiz. 



Yapı (Struct) Nedir ?

Yapılar -bazen topluluklar diye anılır- tek bir isim altında onunla ilgili deÄŸiÅŸkenlerin olduÄŸu koleksiyonlardır. Yapılar, sadece aynı veri türünde öğeleri içeren dizilerin aksine farklı veri türünde deÄŸiÅŸkenler içerebilir. Yapılar, diziler gibi belleÄŸe sürekli biçimde yerleÅŸen nesnelerdir. Dizilerde olduÄŸu gibi baÅŸlangıç adresleri içerilerek fonksiyonlara kolaylıkla aktarılabilirler. 

Yapıların Bildirimi

Yapılar türetilmiş veri türleridir. Yapılar diğer türdeki nesneleri kullanarak inşa edilirler. Yapı bildiriminin genel biçimi:

struct [yapı_ismi] {
    <tür> <yapı_elemanı>;
    <tür> <yapı_elemanı>;
    <tür> <yapı_elemanı>;
    ...
};


Yukarıdaki genel biçimde:

struct :             Bildirim için gerekli anahtar sözcüktür.
yapı_ismi :      Yapıyı anlatan isimlendirme kurallarına uygun herhangi bir isim olabilir. / yapı                                         etiketidir.
yapı_elemanı : Yapıyı oluÅŸturan deÄŸiÅŸken isimleridir; isimlendirme kurallarına uygun herhangi bir isim                           olabilir.

!! Bildirimin küme parantezinden sonra noktalı virgül ile sonlandırıldığına dikkat ediniz.!!

Örneğin, düzlemde bir nokta x ve y bileşenlerinden oluştuğuna göre, bu aynı elemanlar yerine bir yapı biçiminde de bildirebiliriz:

struct NOKTA {
    int x;
    int y;
};
...


Benzer biçimde, tarih bilgileri de hiçbiri int türünden üç ayrı değişken yerine, yapı kullanılarak mantıksal bir bütünlük içinde ifade edilebilir:

struct DATE {
    int day;
    int month;
    int year;
};

Yukarıdaki bildirimde DATE yapının ismi; day, month ve year ise int türünden yapı elemanlarıdır.

Yapı bildirimiyle derleyici yalnızca yapılar hakkında bilgi edinir; bellekte onlar için herhangi bir yer ayrılmaz. Yapı bildirimlerini bir çeşit şablon tanımlaması gibi düşünebilirsiniz. Tıpkı fonksiyon prototiplerinde olduğu gibi yapı bildirimleri de yalnızca derleyiciyi bilgilendirmek amacıyla kullanılmaktadır.

Yapı Değişkenlerinin Tanımlanması

Bellekte yer ayırma işlemi yapı değişkenlerinin tanımlanmasıyla oluşur. Yapı değişkenlerinin tanımlanması aşağıdaki iki biçimde yapılır:

struct <yapı_ismi> <yapı_değişkeninin_ismi>;

veya

struct [yapı_ismi] {
    ...
} [deÄŸiÅŸken_listesi];

Yağı deÄŸiÅŸkenlerinin yapı bildiriminde sonra tanımlanması zorunludur. 

Örneğin:

struct NOKTA {
    int x;
    int y;
};
...

struct NOKTA a;

veya

struct NOKTA {
    int x;
    int y;
}a;
...


benzer biçimde DATE yapısı türünden bir d değişkeni:

struct DATE {
    int day;
    int month;
    int year;
};
...
struct DATE d;

veya

struct DATE {
    int day;
    int month;
    int year;
}d;
...

Yapı Değişkenlerine İlk Değerin Verilmesi

Yapı deÄŸiÅŸkenlerine dizilerde olduÄŸu gibi küme parantezleri içerisinde ilk deÄŸer verilir. 

Örneğin:

struct date {
    int day;
    int month;
    int year;
};
...
struct date d = {1,4,1995};


Derleyici ilk değerleri yapı elemanlarına sırasıyla yerleştirir. Yani yukarıdaki örnekte yapı elemanlarının alacağı değerler şöyledir:

d.day => 1
d.month => 4
d.year => 1995


Yapı Üyelerine Erişim

Yapı üyelerine erişmek için iki operatör kullanılır: Yapı üyesi operatörü (.) (aynı
zamanda nokta operatörü olarak da bilinir) ve yapı işaretçisi operatörü ( ->) (aynı zamanda ok
operatörü olarak da adlandırılır). Yapı üyesi operatörü yapının deÄŸiÅŸken ismiyle yapı üyesine eriÅŸir. 

Örneğin:

printf("%d",d.day);

Yapı işaretçisi operatörü -aralarında herhangi bir boşluk olmayan eksi (-) işareti ve büyüktür (>) işaretinden oluşan - yapıda bir işaretçi üzerinden bir yapı üyesine erişir.

struct data *x;
printf("%d", x-> year);

Örnek:

#include <stdio.h>

struct card {
char *face;
char *suit;
};

int main( )
{
struct card aCard;
struct card *cardPtr;

aCard.face = "Ace";
aCard.suit = "Spades";

cardPtr = &aCard;

printf( "%s%s%s\n%s%s%s\n%s%s%s\n", aCard.face," of ", aCard.suit,
cardPtr->face," of ",cardPtr->suit,
(*cardPtr).face," of ",(*cardPtr).suit);
return 0;
}
Ekran Çıktısı:

Ace of Spades
Ace of Spades
Ace of Spades

İç İçe Yapılar

Bir yapının içende başka bir yapı nesnesi tanımlanabilir. İç içe yapıların tanımlanması C'de iki biçimde yapılmaktadır:

1) İçerideki yapının bildirimini daha yukarıda yaparak. Bu durumda derleyici doğal akış yönünde ilerlerken bildirilen yapıyı tanıyabilir.

struct date{
    int day, month, year;
};
...
struct person {
    char name[30];
    struct date bday;
};

2) İçerideli yapının dışındaki yapının içerisinde bildirilmesiyle. Bu durumda değişken tanımlanması da yapılmalıdır:

struct person {
    char name[30];
    struct date{
        int day, month, year;
    }bday;
};

Yapıların Fonksiyonlara Parametre Olarak Geçirilmesi

C'de parametrelerin kopyalanarak fonksiyonlara geçirildiÄŸini hatırlayalım. Yapı deÄŸiÅŸkenleri de fonksiyonlara kopyalanarak parametre olarak geçirilebilirler. 

Örnek:

#include <stdio.h>

struct date{
int day,month,year;
};

void dispDate(struct date x){
printf("Year: %d\n",x.year);
printf("Month: %d\n",x.month);
printf("Dat: %d\n",x.day);
}

int main() {
// Write C code here
struct date n;

n.day = 4;
n.month = 5;
n.year = 1995;
dispDate(n);

}
Ekran Çıktısı:

Year: 1995
Month: 5
Dat: 4




C programlama ile ilgili olan diğer konulara aşağıdaki linkten ulaşabilirsiniz:




C'de Stringler #C7


 Bu yazının konusu C programlama string kullanımıdır. C'ye yeni baÅŸlayanların zorlandığı konulardan biridir. Bu nedenle stringleri ayrı bir bölümde incelemenin daha iyi olacağını düşündük. 



String Nedir?

C programlamada "iki tırnak içerisindeki ifadelere" string ifadeleri ya da kısaca stringler denir. Örneğin;

"İstanbul"
"sayi = %d\n"
"Lütfen bir sayı giriniz:"
...

ifadeleri birer stringtir.

C'de stringler aslında karakter gösteren birer adrestir. C derleyicileri, derleme aÅŸamasında bir stringle karşılaÅŸtığında, önce onu belleÄŸin güvenli bir bölgesine yerleÅŸtirir,  sonuna NULL karakteri ekler ve daha sonra string yerine yerleÅŸtirildiÄŸi yerin baÅŸlangıç adresini koyar. Bu durumda string ifadeleri aslında stringlerin bellekteki baÅŸlangıç yerini gösteren karakter türünden bir adrestir. ÖrneÄŸin;

char *p;
...
p= "Deneme";

gibi bir kodun derlemesi sırasında, derleyici önce "Deneme" stringini belleğin güvenli bir bölgesine yerleştirir, daha sonra yerleştirdiği yerin başlangıç adresini string ifadesi ile değiştirir.

String ifadeleri karakter türünden göstericilere atanmalıdır. String ifadeleri dizi isimlerine atanamaz. Aşağıdaki örneği inceleyiniz;

char s[20];
...
s = "İstanbul";

Bu ifade s bir nesne olmadığı için geçersizdir. Ancak bu durumu dizilere iki tırnak içerisinde ilk değer verme işlemi karıştırmayınız..!

char s[20] = "İstanbul";

Çünkü dizilere ilk değer verme işleminde derleyici önce diziyi belirtilen uzunlukta açar, daha sonra iki tırnak içerisindeki ifadeleri -NULL karakter dahil olmak üzere- dizi elemanlarına sırasıyla yerleştirir. Yani dizilere ilk değer verme işleminde derleyici, string ifadelerinde olduğu gibi bir adres yerleştirmez.


Stringlerin Ömürleri

  • C'de nesneler ömür bakımından 4 gruba ayrılır. Bunlar;
  • Otomatik Ömür (Automatic Storage Duration): Bildirildikleri blokta, bildirildikleri yerde ömürleri baÅŸlayıp bildirildikleri bloÄŸun sonunda ömürleri sonlanan nesnelerdir. ÖrneÄŸin bir fonksiyon içinde tanımlanan yerel deÄŸiÅŸkenler bu tip bir ömre sahiptirler.
  • Statik Ömür (Static Storage Duration): Programın çalışmaya baÅŸlaması ile bellekte yer edinip programın sonlanmasıyla bellekten silinen nesnelerdir.
  • Thread Ömrü (Thread Storage Duration): Ã–mrü bir Thread’in çalışmaya baÅŸlamasından sonlanana kadar olan ve o Thread’e özel olan nesnelerdir. Bu ömür C11 standartları ile dile eklenmiÅŸtir. Bu ömre sahip olan nesnelerin incelenmesi bu yazının kapsamı dışındadır.
  • Dinamik Ömür (Dynamic/Allocated Storage Duration): Ä°steÄŸe göre bellekten yer verilebilen ve bellekten silinebilen nesnelerdir. Bu ömre sahip olan nesnelerin incelenmesi bu yazının kapsamı dışındadır.

Stringler statik ömürlü nesnelerdir. Tıpkı global değişkenler gibi programın yüklenmesiyle yaratılır, programın icrası bittiğinde silinirler. Dolayısıyla stringler çalışabilen kodu büyütürler. Birçok sistemde statik verilerin toplam uzunluğunda belli bir sınırlama söz konusudur.

Stringlerin BirleÅŸtirilmesi 

Stringleri aşağıdaki gibi parçalayamayız:

char *p;
...
p = "Bu gün hava
            çok güzel";

Ancak string ifadeleri büyüdükçe bunu tek bir satırda yazmak hem sıkıntı yaratmakta hem de okunabilirliÄŸi bozmaktadır. Uzun stringlerin parçalanmasına olanak vermek amacıyla derleyiciler yan yana yazılan string ifadelerini birleÅŸtirirler. 

Örneğin;

p = "Bu gün hava"
        " çok güzel"; 

geçerli bir ifadedir. Bu durumda iki string ifadesi birleştirilecek ve aşağıdaki biçime getirilecektir;

p = "Bu gün hava çok güzel";

İki string arasında hiçbir operatör bulunmadığına dikkat ediniz:

p = "Ankara," "İstanbul";

ifadesi ile 

p = "Ankara, İstanbul";

ifadesi eÅŸdeÄŸerdir.

Birleştirmenin yanı sıra tek bir ters bölü ile satır sonlandırılarak sonraki satıra geçiş sağlanabilir. Örneğin;

p = "Bu gün \"
    "hava çok güzel";

ifadesi ile 

p = "Bu gün hava çok güzel";

ifadesi eşdeğerdir. Ters bölü işaretinden sonra stringin aşağıdaki satırı başından itibaren devam edildiğine dikkat ediniz.

Ancak ters bölü karakteri ile sonraki satırın başından devam etme standart olarak her C derleyicisinde geçerli olmayabilir.

Stringlerde Ters Bölü Karakterlerinin Kullanılması

Stirngler içerisinde ters bölü karakter sabitleri de kullanılabilir. Derleyiciler string içerisinde bir ters bölü karakteri gördüklerinde, onu yanındaki karakter ile birlikte tek bir karakter  olarak ele alır. ÖrneÄŸin;

p = "Adı\nSoyadı"; // Adı Soyadı

benzer biçimde:

p = "Adı\tSoyadı"; // Adı    Soyadı

\n ile tek bir karakter, \t ile tab karakter.

Stringlerle Göstericilere İlk Değer Verilmesi

Stringler kullanılarak göstericilere doğrudan ilk değer verilebilir. Örneğin:

char *p = "Istanbul";
char *err = "Bellek yetersiz";
char *s = "Devam etmek için bir tusa basiniz";
...

String ifadeleri aslında karakteri gösteren birer adres olduğuna göre ilk değer verilen göstericilerin de karakter türünden göstericiler olması gerekir. İki tırnak içerisinde dizilere ilk değer vermeyle göstericilere ilk değer verme arasındaki ayrıma tekrar dikkatinizi çekmek istiyoruz.

char *p = "Deneme";
char s[10] = "Deneme";

Göstericilere ilk değer verildiğinde derleyici bunu bir string ifadesi olarak ele almaktadır. Yani string belleğe yerleştirildikten sonra başlangıç adresi göstericiye atanır. Oysa dizilerde önce dizi açılır, daha sonra karakterler tek tek dizi elemanlarına yerleştirilir. Dizilere ilk değer verirken kullandığımız iki tırnak ifadeleri adres belirtmezler.


Örnek

Aşağıdaki örnekte stringin, uzunluğu, kelime sayısı, cümle sayısı, sayı sayısı, girilen harfin indisi, girilen kelimeden sonraki kısmını bulan program vardır:

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


int uzunluk(const char *);
int kelime( const char*, int);
int cumle(const char*, int);
int sayi(const char*, int);
int indis(const char*, char);
const char* my_strchr(const char*,const char);

int main() {
const char* c = "Bu gun hava cok guzel ama yaz mevsimi icin biraz serin.\
 Bu sene bir turlu yaz gelemedi. Gelse ne olacak ki? Salgin yuzunden kimse bir\
 yere gidemiyor. Kalabaliklardan uzak durmak zorunda olmak berbat bir sey. Bu gun gunlerden Persembe.\
 Bu gun pazara gittim. Ben pazardan 3 kg elma, 2 kg portakal aldim. Saat 9 da geldim. Ben 1999 yilinda dogdum.";
const char* r = "Merhaba";
const char s = 'z';
int t = 0;
int uzk = uzunluk(c);
int klm = kelime(c, uzk);
int cml = cumle(c, uzk);
int syi = sayi(c, uzk);
int x = indis(c, s);
const char* q = my_strchr(c, s);

printf("Girilen kelimeden sonraki kisim = %s\n", q);
printf("Uzunluk = %d\n", uzk);
printf("Kelime sayisi = %d\n", klm);
printf("Cumle sayisi = %d\n", cml);
printf("Sayi sayisi = %d\n", syi);
printf("Harf indisi= %d\n", x);
return 0;
}

// stringin uzunuÄŸu bulunur
int uzunluk(const char *c) {
int uzunluk = 0;
for (int i = 0; c[i]!= '\0'; i++) {
uzunluk = uzunluk + 1;
}
//printf("Uzunluk = %d\n", uzunluk);
return uzunluk;
}

// kelime sayısı bulunur
int kelime(const char* c, int uzk) {
int kelime = 0;
for (int i = 0; i<uzk; i++)
{
if (c[i] == ' ') {
kelime = kelime + 1;
}
if (c[i] == '.' || c[i] == '?') {
if (c[i + 1] != ' ') {
kelime = kelime + 1;
}
}
}
//printf("Kelime sayisi = %d\n", kelime);
return kelime;
}

// Cümle sayısı
int cumle(const char* c, int uzk) {
int cumle = 0;
for (int i = 0; c[i] != '\0'; i++)
{
if (c[i] == '.' || c[i] == '?') {
cumle = cumle + 1;
}
}
//printf("Cumle sayisi = %d\n", cumle);
return cumle;
}

// cümlede geçen sayıların kaç tane olduğunun bulunması
int sayi(const char* c, int uzk) {
int sayi = 0;
for (int i = 0; i < uzk; i++) {
if (c[i] == '1' || c[i] == '2' || c[i] == '3' ||c[i] == '4' ||
c[i] == '5' || c[i] == '6' || c[i] == '7' || c[i] == '8' ||
c[i] == '9') {
if (c[i - 1] == '1' || c[i - 1] == '2' || c[i - 1] == '3' || c[i - 1] == '4' ||
c[i - 1] == '5' || c[i - 1] == '6' || c[i - 1] == '7' || c[i - 1] == '8' ||
c[i - 1] == '9')
sayi = sayi - 1;
sayi = sayi + 1;
}
}
return sayi;
}
// gönderilen kelimenin indisini bulur
int indis(const char* c, char s) {
int sayac = 0;
int u = strlen(c);

for (int i = 0; i < u; i++)
{
if (c[i] == s) {
sayac = i;
break;
}
}
return sayac;
}
// strhcr aynı işlevi yapar
const char* my_strchr(const char* c, const char s)
{
//const char* t = NULL;
if (c != NULL)
{
do
{
if (*c == s)
{
return c;// t = c;
//break;
}
} while (*c++);
}
return NULL;
}

Örnekler

Buradaki örnekler string fonksiyonları ile ilgilidir;

1)  strchr - Program strchr() fonksiyonu ile bir karakter dizisi içinde 's' karakterini arar. Karakterin bulunduÄŸu yerden itibaren dizi içeriÄŸini ekrana yazar.

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

int main( void )
{
    const char cdizi[] = "Bilgisayar";
    char *ret;

    ret = strchr(cdizi, 's');

    printf("Bulunan karakterden itibaren dizi içeriği: %s", ret);

    return 0;
}

Çıktı:

Bulunan karakterden itibaren dizi içeriği: sayar

2) strcmp - Program strcmp() fonksiyonu ile iki karakter dizisini karşılaÅŸtırarak elde ettiÄŸi sonucu ekrana yazar.

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

int main( void )
{
    const char *cp1 = "Bilgisayar";
    const char *cp2 = "Bilgileri";
    int ret;

    ret = strcmp(cp1, cp2);

    if(ret<0) printf("cp1 karakter dizisi cp2 karakter dizisinden küçüktür!");
    else if(ret>0) printf("cp2 karakter dizisi cp1 karakter dizisinden küçüktür!");
    else printf("cp1 karakter dizisi cp2 karakter dizisine eÅŸittir!");

    return 0;
}

Çıktı:

cp2 karakter dizisi cp1 karakter dizisinden küçüktür!

3) strlen - Program strlen() fonksiyonu ile bir karakter dizisinin uzunluÄŸunu elde ederek, elde ettiÄŸi deÄŸeri ekrana yazar.

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

int main( void )
{
    const char *cp = "Bilgisayar";
    int ret;

    ret = strlen(cp);

    printf("%s karakter dizisinin uzunluÄŸu: %d\n", cp, ret);

    return 0;
}

Çıktı:

Bilgisayar karakter dizisinin uzunluÄŸu: 10

4) strcpy - Program strcpy() fonksiyonu ile bir karakter dizisinin içeriÄŸini diÄŸer bir karakter dizisinin üzerine kopyaladıktan sonra elde edilen yeni karakter dizisini ekrana yazar.

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

int main( void )
{
    char cdizi1[] = "Bilgisayar";
    const char cdizi2[] = "Programlama";

    strcpy(cdizi1, cdizi2);

    printf("cdizi1 bellek içeriği: %s\n", cdizi1);

    return 0;
}

Çıktı:

cdizi1 bellek içeriği: Programlama



C programlama ile ilgili olan diğer konulara aşağıdaki linkten ulaşabilirsiniz:


Kaynaklar:

A'dan Z'ye C programlama
https://www.bilgigunlugum.net/prog/cprog/c_stdkut/string
https://www.mustafayemural.com/cpp-my000047/