C++ extern kullanımı

Arkadaslar extern kullanimini arastirdim ve yazdigi sey “bir degiskenin baska bir modulde tanimlandigini belirtmek icin kullaniliyor” yaziyordu, ne demek istedigini anladim ama neden kullanmaliyiz ne gerek var veya sadece belirtmeye mi yariyor?? :thinking:

X değişkeni Y modülünde var gibi mi?

Evet belirtmeye yariyormuş ama ne gerek var??

Aynı isim olması (sanmıyorum bu değil) veya oradaki değişkenlere müdahale edebiliyor olmamız.Veya bunda X var,bundaki X’i al gibi şeyler kullanmaz mısın?

extern kullanılması gereken bir örnek üzerinden extern'i anlatmaya çalışayım.

Şimdi diyelim bir tane header dosyamız var. Bu dosyamızda da global alanda görünecek bir adet fonksiyon tanımlamış olalım.

dosya.h

void foo();

Bu header dosyamızı bir kütüphane olarak dosya.c isminde bir dosyanın içine aktaralım ve foo fonksiyonunu kullanalım.

dosya.c

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

void foo()
{
    printf("hello");
}

İki tane benzer program oluşturacağız. Bunların birincisi bir C programı olacak, diğeri de bir C++ programı olacak. Ve C++ programında extern'in nasıl kullanıldığını göreceğiz.

Ana C programımızın ismi main.c olsun.

main.c

#include "dosya.h"

int main()
{
    foo();
    return 0;
}

Bizim bu main.c'yi derlemek için linkleme yapmamız gerekir. Yani doğrudan main.c'yi derlemeye kalkışırsak foo için tanımsız bir başvuru hatası alırız. Bu yüzden c uzantılı dosyalarımızdan obj dosyaları oluşturacağız.

gcc -c main.c && gcc -c dosya.c

Dizinimizde şimdi main.o ve dosya.o isminde iki adet obj dosyası var. Ve bu dosyaları birbirlerine linkleyerek derlemek için şöyle bir komut yazıyoruz.

gcc main.o dosya.o -o dosyam

Artık oluşan dosyam dosyasını çalıştırırsak, ekrana hello yazısı yazdırılacaktır.

Diyebilirsiniz ki bunun C++'daki extern ile ne alakası var. Şimdi izninizle ona gelmek istiyorum. Az önce bahsettiğim gibi aynı programın bir de C++ versiyonunu yapacağız. Dosyamızın ismi bu sefer main.cpp olacak.

Önce kodları hiç değiştirmeden yazalım bakalım.

main.cpp

#include "dosya.h"

int main()
{
    foo();
    return 0;
}

Şimdi bu dosyanın obj dosyasını oluşturalım.

g++ -c main.cpp

main.o dosyamız oluşacaktır.

Şimdi bu dosya.o ile main.o dosyamızı linkleyerek dosyam'ı oluşturmaya çalışalım.

g++ main.o dosya.o -o dosyam

Bu durumda foo'nun tanımsız olduğuna yönelik bir hata alacağız. İşte böyle bir hatayı önlemek için extern anahtar sözcüğünü kullanmamız gerekiyor.

Dolayısıyla main.cpp dosyamızda şöyle bir değişiklik yapıyoruz.

main.cpp

extern "C" {
#include "dosya.h"
}

int main()
{
    foo();
    return 0;
}

Dosyayı bu şekilde kaydettikten sonra tekrar bir obj dosyası oluşturalım.

g++ -c main.cpp

Şimdi de bu iki dosyayı birbirine linklenmiş bir şekilde derleyelim.

g++ main.o dosya.o -o dosyam

Artık g++ ile derlenen dosyam, tıpkı gcc ile derlenen dosyam gibi aynı sonucu vererek çalışacaktır.

Burada extern anahtar sözcüğünün kullanılmasının sebebini açıklamaya çalışayım.

main.cpp dosyasında extern'i eklemeden önce oluşturulan main.o dosyasının içinde neler olduğuna bakalım. Bunun için önce main.cpp'nin içeriğini eski haline getiriyoruz ve obj dosyası oluşturuyoruz. Sonra da aşağıdaki komutu çalıştırıyoruz.

nm main.o 

Şuna benzer bir çıktı almamız gerekiyor:

                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main
                 U _Z3foov

Burada gördüğünz gibi foo fonksiyonunun ismi biraz farklı. Bu foov ifadesindeki v, void olduğu için var ve bu ifade ile aslında linklemeye çalıştığımız fonksiyonun ismi birbirlerinden farklı.

dosya.o'nun içine bakalım.

nm dosya.o

Şöyle bir çıktı almamız gerekiyor:

0000000000000000 T foo
                 U _GLOBAL_OFFSET_TABLE_
                 U puts

Biz extern ifadesini kullanarak, foo'nun isminin C dosyasındaki gibi kalmasını sağlamış oluyoruz.

Bir de extern edilmiş main.cpp dosyamızın içerisine bakalım. Bunun için tabi önce yeni bir obj dosyası oluşturmamız gerekiyor, çünkü dosyanın içeriğini değiştirmiştik.

nm main.o

Bu kez alacağımız çıktı şöyle olacaktır.

                 U foo
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main

Umarım anlatabilmişimdir.

5 Beğeni

Hocam simdi bi sorum var yukarda dosya.c yi nasil dosya.h olarak cagirdiniz

dosya.c'yi dosya.h olarak çağırmadım. dosya.h diye bir header dosyası oluşturdum. Sonra dosya.hdosya.c dosyasına aktardım. Sonra dosya.c dosyasından bir obj dosyası oluşturdum. main.c dosyasına dosya.h'ı aktardım ve main.c dosyasından da bir obj dosyası oluşturdum. Sonra bu iki obj dosyasını linkleyerek derledim. C++ için de benzer bir yol izlendi. Tek fark C++'da fonksiyonu tanımlı hale getirmek için extern kullanmamız oldu.

Kafam karisti gibi ama saolun anlamaya calisirim xD
( @dildeolupbiten hocam si hangi kaynaktan ogreniyorsunuz c++ ı bir kac kaynak buldum ama merak ettim)

Hakikaten “modul” yaziyorsa okumayi birak—dogru terim compilation unit.

Degiskenin linkage’ini external yapiyor, define edilmeden declare edilmesini sagliyor. Fonksiyonlardaki int hede(int); deklarasyonuyla ayni is yani. (Fonksiyonlarin default linkage’i external.)

Ayni zamanda extern "C" gibi bir kullanimi var, o da ABI gibi diger linkage parametrelerini degistiriyor. Yukarida @dildeolupbiten ornegini vermis.

3 Beğeni

Örnekler üzerinden daha rahat anlarsınız diye düşünüyorum.

Bir tane header dosyamız var:

header.c

int topla(int x, int y);

Bu header dosyasını linklenecek.c isminde bir dosyanın içine aktaracağız.

linklenecek.c

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

int topla(int x, int y)
{
    printf("x + y = %d\n", x + y);
    return 0;
}

Bir tane de ana isminde bir C++ dosyamız var. İçinde de şunlar yazıyor:

ana.cpp

extern "C" {
#include "header.h"
}

int main()
{
    topla(2, 3);
    return 0;
}

Şimdi siz bu haliyle ana.cpp dosyasını program haline getirmeye çalışırsanız, topla fonksiyonu için tanımsız başvuru hatası alırsınız. Çünkü header dosyasında topla fonksiyonunun ne tip değer döndürdüğünü ve hangi tipte kaç tane parametre aldığını tanımladık. Ama fonksiyonun nasıl bir işlem yapacağını tanımlamadık. Bu tanımlamayı linklenecek.c ismindeki dosyamızda yapacağız. Dolayısıyla ana.cpp dosyamızdaki topla fonksiyonu ile linklenecek.c dosyamızdaki topla fonksiyonunun linklenmesi gerekiyor. Bunun için de hem ana.cpp hem de linklenecek.c dosyalarından obj dosyaları oluşturuyoruz.

g++ -c ana.cpp && gcc -c linklenecek.c

Sonra da bu obj dosyalarını birlikte derliyoruz.

g++ ana.o linklenecek.o -o dosyam

Linklemeye ihtiyaç duymadan yapamaz mıydık? Elbette yapardık, o zaman şöyle yapmamız gerekirdi:

header.h dosyamız.

int topla(int x, int y){
    return x + y;
}

ana.cpp dosyamız.

#include "header.h"
#include <iostream>

using namespace std;

int main()
{
    std::cout << "x + y = " << topla(2, 3) << "\n";
    return 0;
}

Böylece doğrudan ana.cpp dosyamızı alıştığımız şekilde derlerdik.

1 Beğeni

İnanmayacaksın ama google sağ olsun benim takip ettiğim herhangi bir kaynak yok. Boşuna Hz. Google demiyorum.

1 Beğeni

Ya hocam biraz kizabilirsiniz ama son ornekteki externin amacini anlamadim orda extern demeseydik de zaten topla ya gorev yuklemedigimizden hata almicak miydik zaten :sweat_smile: :

Son örnek derken? Hangi örnekten bahsediyorsunuz? Son mesajımda extern kullandığım örnekten mi?

1 Beğeni

Bu ornekte @dildeolupbiten

extern “C”
C++ içinde C kodu çalıştırabilmemize yarıyor sanırım.
https://isocpp.org/wiki/faq/mixing-c-and-cpp

Dediğiniz doğru. Daha spesifik olarak C++'ta kullacağınız bir fonksiyon adının C bağlantısına sahip olmasını sağlar.

Hayir, export edilenlerin C linkage’i ile export edilmesini, import edilenlerin de C linkage’ina sahip olmalarinin beklenmesini sagliyor.

Veya dedigine yaklasmak istersek: C++ icinden C kodunun cagrilabilmesine, ve tersine yariyor.

@aib’in dediği doğru aslında. Yani çalıştırılmasını sağlıyor ifadesi hatalı. Ben de doğru diyorsunuz diyerek hata yapmışım.

Aslında bu ifadeyi kullanarak anlatmak istediğiniz şey ile, bu ifadenin anlamı arasında tam bir örtüşme yok. Yine de anlatmak istediğinizi anlıyorum.

Toparlamaya çalışayım, yanlışım varsa da düzeltin lütfen.

extern "C", bir bağlantı belirtimidir. C++ dosyanız ile bir C dosyasını linkleyerek derlemeye çalıştığınızda, extern "C" bağlantı belirtimini kullanarak, C dosyasında yer alan fonksiyonun adını, header dosyasının içindeki aynı ada sahip fonksiyonla bağlantılı hale getiriyor ve derleyicinin bu ismi karıştırmamasını sağlamış oluyoruz.

2 Beğeni

Kaynak dosyalari linklenerek derlenmiyor, obje dosyalarina derleniyorlar.

Daha sonra bu obje dosyalari linkleniyorlar.

C dosyasindan kast edilenin ne oldugundan emin degilim. C++ kaynak kodu veya extern “C” ile baglantisini da anlayabilmis degilim, icinde include mu ediyoruz dosyayi?

extern "C", icine aldigi declaration’larin obje dosyalarinda C linkage’i ile bulunmasini sagliyor. Name mangling cogu C++ linkage’inda olup cogu C linkage’inda olmayan bir sey.

Pardon ben obje dosyalarından bahsetmeyi unutmuşum.

Paragrafı aşağıdaki gibi değiştirirsek doğru mu ifade etmiş oluruz?

extern "C", bir bağlantı belirtimidir. C++ obje dosyası ile bir C obje dosyasını linkleyerek derlemeye çalıştığınızda, extern "C" bağlantı belirtimini kullanarak, C objesinde yer alan fonksiyonun adını, header dosyasının içindeki aynı ada sahip fonksiyonla bağlantılı hale getiriyor ve derleyicinin bu ismi karıştırmamasını sağlamış oluyoruz.