Haberler:

Forum kuralları güncellendi LÜTFEN  okuyunuz:  https://bit.ly/2IjR3ME

Ana Menü

fonksiyon çağırma

Başlatan mr.engineer, 09 Haziran 2021, 15:23:26

mr.engineer

Merhaba, C'de kafama takılan bir şey var.

Elimde bir test.h isimli başlık dosyası var ve burada bir fonksiyon bildirimi yaptım. Fonksiyonun tanımının da başka bir kaynak dosyada olduğunu farz edin.
abc.c isimli bir kaynak dosyanın içinde, test.h başlık dosyasını eklemeden oradaki bir fonksiyona çağrı yapıyorum yani abc.c uzantılı dosyada bu fonksiyonun bildirimi yok.
(Hatta test.h dosyası olmasa da durum aynı oluyor.) Bu fonksiyonu bilerek çağırsam sorun olmayacak ama yanlışlıkla çağırsam bunu nasıl tespit edebilirim? abc.c dosyasında default olarak int foo(); bildirimi yapılacak ve fonksiyonun başka bir yerde tanımı da olduğundan hata vermeyecek. Ama bu fonksiyonu yanlışlıkla çağırdığım için projede bir hataya yol açacak.
Bu durumda bazı derleyiciler uyarı bazıları hata veriyor galiba.
Bu durumu engellemenin yolu nedir? Fonksiyonun başına static yazmadan nasıl çözülür?


   
//test.h
...........
...........
void func();
int foo();
.............
.............

//abc.c                             
#include <stdio.h>           
#include <stdlib.h>
......................
.....................
foo();                 //yanlışlıkla çağırdım
.......................
.................... 

volkanunal

Merhabalar, anladığım kadarıyla ve netleştirmek için şunları yazabilirim.

(Hatta test.h dosyası olmasa da durum aynı oluyor.) -> Bu ifadeniz yanlış, aynı olmuyor. Burada bir örtülü tanım derleyici tarafından yapılıyor ve C89/C90 da derleyici tarafından tanımlanan fonksiyon int değere geri dönen ve parametre almayan bir imzaya sahip şekilde derleyici tarafından tanımlanıyor. (C99 kaldırıldı diye biliyorum emin değilim.)

Kodun derleniyor olması onun doğru çalışacağı anlamına gelmiyor, burada ki business sizin tarafınızdan sağlanmalı. Modern derleyici hata da verebilir ya da sadece bir uyarı verebilir. Her zaman fonksiyon prototiplerini vermek gerekiyor bence. Yani sizin çağırdığınız o "foo" fonskiyonu sizin yazdığınız "foo" fonksiyonu değil, derleyici tarafından int'e geri dönen şekilde yazılmış bir fonksiyonu.

Eğer farklı unit içerisindeyse ilgili yer extern bildirimini kullanabilirsiniz.
Primum nil nocere

volkanunal

#2
Alıntı yapılan: volkanunal - 09 Haziran 2021, 16:24:17Merhabalar, anladığım kadarıyla ve netleştirmek için şunları yazabilirim.

(Hatta test.h dosyası olmasa da durum aynı oluyor.) -> Bu ifadeniz yanlış, aynı olmuyor. Burada bir örtülü tanım derleyici tarafından yapılıyor ve C89/C90 da derleyici tarafından tanımlanan fonksiyon int değere geri dönen ve parametre almayan bir imzaya sahip şekilde(parametre konusundan tam emin değilim standartlara bakmak lazım, aklımda kalan bu şekilde) derleyici tarafından tanımlanıyor. (C99 kaldırıldı diye biliyorum emin değilim.)

Kodun derleniyor olması onun doğru çalışacağı anlamına gelmiyor, burada ki business sizin tarafınızdan sağlanmalı. Modern derleyici hata da verebilir ya da sadece bir uyarı verebilir. Her zaman fonksiyon prototiplerini vermek gerekiyor bence. Yani sizin çağırdığınız o "foo" fonskiyonu sizin yazdığınız "foo" fonksiyonu değil, derleyici tarafından int'e geri dönen şekilde yazılmış bir fonksiyonu.

Eğer farklı unit içerisindeyse ilgili yer extern bildirimini kullanabilirsiniz.

Yukarda düzenleme yapamadım, fonksiyonun parametre konusundan emin değilim.
Primum nil nocere

Tagli

#3
Normalde daha önce deklare edilmemiş bir fonksiyonu çağıramazsın. Header dosyaları bu işe yarıyor zaten. abc.c içinde fonksiyonu çağırmadan önce yukarıda bir yerde int foo(void); tanımı olmalı.

Ancak,

Anladığım kadarıyla istisnai bir durum var. Bazı C derleyicileri önceden deklare edilmemiş fonksiyonlar gördüklerinde fonksiyonun "int foo()" formatında olduğunu varsayıp derleme yapabiliyorlar. Eğer bu formattaki (signature) bir fonksiyon başka bir yerde gerçekten tanımlı ise linker'da da hataya sebep olmadan bağlanabilir. Bu durum C standatlarındaki tarihi bir kalıntı galiba. Güncel versiyonlarda ve güncel derleyicilerde belki kapatılmıştır, araştırmak lazım.

StackOverflow'da konuyla ilgili bir soru buldum:
https://stackoverflow.com/questions/9182763/implicit-function-declarations-in-c

Burada bahsettiğine göre normalde C99'dan itibaren bu durum standartta engellenmiş. Ancak derleyiciler bazen hata yerine sadece uyarı verebiliyor. Aynı kaynakta, bunu durumu engellemek için bir derleme bayrağından bahsedilmiş (ben denemedim):
-Werror=implicit-function-declaration

Konuyla uzaktan alakalı olduğu için bahsetme ihtiyacı duyduğum bir konu daha var: Eğer foo.c dosyasında bir int bar(int) gibi bir fonksiyon varsa, bunu static olarak tanımlarsan, bu fonksiyon bağlayıcıya (linker) görünmez olur. Bu dışarı ile paylaşılmayacak ve sadece o dosya içinde kullanılacak fonksiyonların aynı ismi alabilmesine olanak verir. Ancak paylaşılan fonksiyonlarda isim çakışması olacaktır. C'de bunun çözümü yok. C++'ta bu durum namespace'ler ile çözülüyor.

Son olarak, fonksiyonları "int foo()" şeklinde değil "int foo(void)" şeklinde tanımlamayı alışkanlık haline getirmek lazım. Boş parantez C'de farklı bir anlama geliyor. Sanırım o fonksiyonun herhangi bir parametre alabileceği şeklinde yorumlanıyor. Ayrıntıları bilmiyorum ama yine de kulağa tehlikeli geliyor. C++'ta böyle bir durum yok, o yüzden C++'ta kod yazarken ben de boş parantezleri tercih ediyorum.
Gökçe Tağlıoğlu

mr.engineer

Alıntı yapılan: volkanunal - 09 Haziran 2021, 16:24:17Merhabalar, anladığım kadarıyla ve netleştirmek için şunları yazabilirim.

(Hatta test.h dosyası olmasa da durum aynı oluyor.) -> Bu ifadeniz yanlış, aynı olmuyor. Burada bir örtülü tanım derleyici tarafından yapılıyor ve C89/C90 da derleyici tarafından tanımlanan fonksiyon int değere geri dönen ve parametre almayan bir imzaya sahip şekilde derleyici tarafından tanımlanıyor. (C99 kaldırıldı diye biliyorum emin değilim.)


Ben de aynısını söyledim zaten. Derleyicinin default olarak int foo(); şeklinde bildirim yapması test.h dosyasının olup olmamasına bağlı değil demek istedim. Verdiğim örnekte test.h dosyası olmasına rağmen abc.c dosyasında include edilmiyor.
Ben zaten foo fonksiyonunu çağırmak istemiyorum istesem zaten dahil ederim, yanlışlıkla bugün böyle bir hata yaptım nasıl engel olurum diye düşündüm.

Dediğin gibi C99'da kaldırılmış ama gcc desteklemiyormuş. Yani sadece uyarı veriyor   

orkun@orkun-vm:~/Desktop$ gcc -o sample -Wall -c -std=c99 test.c
test.c: In function 'main':
test.c:9:2: warning: implicit declaration of function 'foo' [-Wimplicit-function-declaration]
    9 |  foo();
      |  ^~~

mr.engineer

Alıntı yapılan: Tagli - 09 Haziran 2021, 16:37:29Anladığım kadarıyla istisnai bir durum var. Bazı C derleyicileri önceden deklare edilmemiş fonksiyonlar gördüklerinde fonksiyonun "int foo()" formatında olduğunu varsayıp derleme yapabiliyorlar.

Hocam bu kısım derleyiciden değil C standartlarından kaynaklı diye biliyorum.
Son söylediğiniz parametre almayan fonksiyonlara dediğiniz gibi içine void yazmayınca parametre verebiliyorsunuz. Aşağıdaki kod visual studioda uyarısız çalışıyor. Bunun bir zararı var mı bilmiyorum ama Xc8 compiler'da (MPLab) bu durum error veriyor diye hatırlıyorum. Tabi standartlara göre de farklılık olabilir orasını bilmiyorum

#include <stdio.h>

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

int main()
{
	foo(4);
}

Tagli

Alıntı yapılan: mr.engineer - 09 Haziran 2021, 20:43:16Dediğin gibi C99'da kaldırılmış ama gcc desteklemiyormuş. Yani sadece uyarı veriyor
Bunu muhtemelen, C99 yeni çıktığı zaman eskiden yazılmış programlar patlamasın diye sadece uyarı verecek şekilde yapmışlar.

İlk mesajımda bahsettiğim ayarı etkinleştirerek uyarı yerine hata vermesini sağlayabilirsin sanırım. Bence derleyicinin hata vermesi daha güvenli olur.
Gökçe Tağlıoğlu