C referans ile geçme


204

C bir değişkenin referans olarak geçmesini desteklemiyorsa, bu neden işe yarar?

#include <stdio.h>

void f(int *j) {
  (*j)++;
}

int main() {
  int i = 20;
  int *p = &i;
  f(p);
  printf("i = %d\n", i);

  return 0;
}

Çıktı:

$ gcc -std=c99 test.c
$ a.exe
i = 21 

22
Bu kodun neresinde referans veriyorsunuz ?
Atul

15
C'nin referans olarak geçmediğine dikkat edilmelidir, sadece işaretçiler kullanılarak taklit edilebilir.
Bazı programcı ahbap

6
Doğru ifade "C, dolaylı olarak başvuru yoluyla bir değişken geçirmeyi desteklemez " şeklindedir - &işlevi çağırmadan önce açıkça (ile ) bir başvuru oluşturmanız ve işlevde (ile *) açıkça dereference etmeniz gerekir .
Chris Dodd

2
Kod çıkışınız tam olarak çağrıldığında buna eşittir f(&i);, yalnızca C'de bulunmayan referansla bir geçiş uygulamasıdır. Referans olarak C geçişi
EsmaeelE

@Someprogrammerdude İşaretçiyi geçmek referans yoluyla geçer. Bu, "anlayışlı" C programcılarının gurur duyduğu gerçeklerden biri gibi görünüyor. Sanki bir tekme attılar gibi. "Ah C'nin referansı olduğunu düşünebilirsiniz ama hayır aslında harharhar'dan geçen bir bellek adresinin değeri". Referans olarak geçmek, sadece değişkenin kendisinden ziyade bir değişkenin nerede saklandığı bellek adresini geçmek anlamına gelir. C'nin izin verdiği şey budur ve bir işaretçiyi her geçtiğinizde referans olarak geçer, çünkü bir işaretçi değişkenlerin bellek konumuna bir referanstır .
YungGun

Yanıtlar:


315

Çünkü işaretçinin değerini yönteme geçiriyorsunuz ve daha sonra, işaret edilen tamsayıyı elde etmek için kayıttan çıkarıyorsunuz.


4
f (s); -> bu değere göre geçmek anlamına mı geliyor? sonra gösterilen tamsayıyı elde etmek için kayıttan çıkarma. -> lütfen daha fazla açıklama yapabilir misiniz?
bapi

4
@bapi, bir işaretçinin kayıttan çıkarılması "bu işaretçinin başvurduğu değeri almak" anlamına gelir.
Rauni Lillemets

İşaretçiyi geçmek yerine değişkenin adresini alan işlevi çağırma yöntemi için ne çağırıyoruz. Örnek: func1 (int & a). Bu referans ile yapılan bir çağrı değil mi? Bu durumda referans gerçekten alınır ve ibre durumunda hala ibre geçeriz, çünkü ibre sadece değere göre geçeriz.
Jon Wheelock

1
İşaretçileri kullanırken anahtar gerçek, işaretçinin kopyasının işleve geçirilmesidir. Fonksiyon daha sonra orijinali değil, o işaretçiyi kullanır. Bu hala değere sahip, ama işe yarıyor.
Danijel

1
@ Danijel Hiçbir şeyin kopyası olmayan bir işaretçiyi bir işlev çağrısına geçirmek mümkündür. Örneğin, işlevi çağırmak func: func(&A);Bu, hiçbir şey kopyalamadan işleve A işaretçisini işleve geçirir. Bu, by-value'tır, ancak bu değer bir referanstır, bu nedenle A değişkenine 'by-pass` olursunuz. Kopyalama gerekmez. Referans yoluyla olduğunu söylemek geçerlidir.
YungGun

124

Bu referans olarak değil, diğerlerinin belirttiği gibi by-pass-by değeridir.

C dili istisnasız her bir geçiş değeri. Bir işaretçiyi parametre olarak iletmek, referansla geçmek anlamına gelmez.

Kural şudur:

Bir işlev gerçek parametre değerini değiştiremez.


Bir fonksiyonun skaler ve işaretçi parametreleri arasındaki farkları görmeye çalışalım.

Skaler değişkenler

Bu kısa program, skaler değişken kullanarak by-pass değerini gösterir. paramformal parametre olarak adlandırılır ve variableişlev çağrısındaki gerçek parametre olarak adlandırılır. İşlevdeki artış paramartmaz variable.

#include <stdio.h>

void function(int param) {
    printf("I've received value %d\n", param);
    param++;
}

int main(void) {
    int variable = 111;

    function(variable);
    printf("variable %d\m", variable);
    return 0;
}

Sonuç

I've received value 111
variable=111

Referans yoluyla yanılsama

Kod parçasını biraz değiştiriyoruz. paramşimdi bir işaretçi.

#include <stdio.h>

void function2(int *param) {
    printf("I've received value %d\n", *param);
    (*param)++;
}

int main(void) {
    int variable = 111;

    function2(&variable);
    printf("variable %d\n", variable);
    return 0;
}

Sonuç

I've received value 111
variable=112

Bu, parametrenin referans olarak geçtiğine inanmanızı sağlar. O değildi. Değere göre geçti, param değeri bir adres. İnt türü değeri artırıldı ve bu, onu bir by-pass işlev çağrısı olduğunu düşündüren yan etkidir.

İşaretçiler - değere göre geçti

Bu gerçeği nasıl gösterebiliriz / kanıtlayabiliriz? Belki Skaler değişkenlerin ilk örneğini deneyebiliriz, ancak skaler yerine adresleri (işaretçiler) kullanıyoruz. Bakalım bu yardımcı olabilir mi?

#include <stdio.h>

void function2(int *param) {
    printf("param's address %d\n", param);
    param = NULL;
}

int main(void) {
    int variable = 111;
    int *ptr = &variable;

    function2(ptr);
    printf("ptr's address %d\n", ptr);
    return 0;
}

Sonuç, iki adresin eşit olması olacaktır (kesin değer için endişelenmeyin).

Örnek sonuç:

param's address -1846583468
ptr's address -1846583468

Kanımca bu, işaretçilerin değere göre geçildiğini açıkça kanıtlıyor. Aksi takdirde işlev çağırma işleminden sonra ptrolur NULL.


69

C'de, referansla geç, bir değişkenin (işaretçi) adresini geçirerek ve gerçek değişkenin okunması veya yazılması için bu adresin işlevden çıkarılmasıyla simüle edilir. Bu, "C tarzı referans by-pass" olarak anılacaktır.

Kaynak: www-cs-students.stanford.edu


50

Çünkü yukarıdaki kodda referans by pass yoktur. İşaretçiler (örneğin void func(int* p)) kullanmak adrese göre yapılır. Bu, C ++ 'da doğrudan başvurudır (C ile çalışmaz):

void func(int& ref) {ref = 4;}

...
int a;
func(a);
// a is 4 now

1
Adrese göre yanıtlamayı seviyorum . Daha mantıklı.
Afzaal Ahmad Zeeshan

Adres ve referans bu bağlamda eş anlamlıdır. Ancak bu terimleri ikisini farklılaştırmak için kullanabilirsiniz, orijinal anlamlarına dürüst değil.
YungGun

27

Örneğiniz, değişkeninizin adresini dereference operatörü ile değerini değiştiren bir işleve geçirdiğiniz için çalışır .

C, referans veri türlerini desteklemese de, örneğinizde olduğu gibi, işaretçi değerlerini açıkça geçirerek referansla geçişi simüle edebilirsiniz.

C ++ referans veri türü daha az güçlüdür ancak C'den devralınan işaretçi türünden daha güvenli kabul edilir. Bu örnek, C ++ referanslarını kullanacak şekilde uyarlanmış olacaktır :

void f(int &j) {
  j++;
}

int main() {
  int i = 20;
  f(i);
  printf("i = %d\n", i);

  return 0;
}

3
Bu Wikipedia makalesi C değil C ++ ile ilgilidir. Referanslar C ++ 'dan önce vardı ve varolan özel C ++ sözdizimine bağlı değil.

1
@Roger: İyi bir nokta ... Cevabımdan C ++ 'a açık referans kaldırdım.
Daniel Vassallo

1
Ve bu yeni makalede "bir referansa genellikle işaretçi denir" yazıyor, bu da cevabınızın söylediği gibi değil.

12

Bir işaretçi (adres konumu) değerine göre geçiyorsunuz .

"Güncellemenizi istediğim verilerin bulunduğu yer" demek gibi.


6

p, bir işaretçi değişkenidir. Değeri i'nin adresidir. F'yi aradığınızda , i'nin adresi olan p değerini iletirsiniz .



5

C'de her şey değerlidir. İşaretçilerin kullanımı bize , değişkenin değeri değiştiği için referans olarak geçtiğimiz yanılsamasını verir . Ancak, işaretçi değişkeninin adresini yazdıracaksanız, etkilenmeyeceğini görürsünüz. Bir kopyası ait değerinin adresi geçirildi, bir işleve. Aşağıda bunu gösteren bir pasaj bulunmaktadır.

void add_number(int *a) {
    *a = *a + 2;
}

int main(int argc, char *argv[]) {
   int a = 2;

   printf("before pass by reference, a == %i\n", a);
   add_number(&a);
   printf("after  pass by reference, a == %i\n", a);

   printf("before pass by reference, a == %p\n", &a);
   add_number(&a);
   printf("after  pass by reference, a == %p\n", &a);

}

before pass by reference, a == 2
after  pass by reference, a == 4
before pass by reference, a == 0x7fff5cf417ec
after  pass by reference, a == 0x7fff5cf417ec

4

Çünkü p değişkenine f işlevine bir işaretçi (bellek adresi) geçiriyorsunuz. Başka bir deyişle, referans değil işaretçi geçiriyorsunuz.


4

Kısa cevap: Evet, C, işaretçiler kullanarak referans yoluyla parametre geçirmeyi uygular.

Parametre geçişini uygularken, programlama dillerinin tasarımcıları üç farklı strateji (veya anlamsal modeller) kullanır: verileri alt programa aktarın, alt programdan veri alın veya her ikisini birden yapın. Bu modeller genel olarak mod, çıkış modu ve giriş modu olarak bilinir.

Bu üç temel parametre geçiş stratejisini uygulamak için dil tasarımcıları tarafından çeşitli modeller geliştirilmiştir:

Değere Göre Geçiş (mod semantiğinde) Sonuca Göre Geçiş (çıkış modu semantiği) semantik)

Referans-by-pass, giriş modu parametre geçişi için ikinci tekniktir. Çalışma programı, ana rutin ve alt program arasında ileri ve geri kopyalamak yerine, alt program verisine doğrudan erişim yolu gönderir. Bu stratejide alt program, verileri ana rutinle etkili bir şekilde paylaşan verilere doğrudan erişime sahiptir. Bu tekniğin ana avantajı, zaman ve mekanda kesinlikle etkili olmasıdır, çünkü alanı çoğaltmaya gerek yoktur ve veri kopyalama işlemleri yoktur.

C: C'de parametre geçirme uygulaması, parametreler olarak işaretçiler kullanarak, by-pass ve referans by-pass (giriş modu) semantiği uygular. İşaretçi alt programa gönderilir ve hiçbir gerçek veri kopyalanmaz. Ancak, bir işaretçi ana rutinin verilerine erişim yolu olduğundan, alt program ana rutindeki verileri değiştirebilir. C bu yöntemi ALGOL68'den almıştır.

C ++: C ++ 'da parametre geçirme uygulaması, işaretçiler kullanarak ve aynı zamanda referans türü adı verilen özel bir işaretçi türü kullanılarak, doğrudan başvuru (giriş modu) semantiği de uygular. Referans türü işaretçileri, alt program içinde dolaylı olarak kaydı kaldırılır, ancak semantikleri de referans yoluyla geçer.

Dolayısıyla, buradaki anahtar kavram, doğrudan referansın, verileri alt programa kopyalamak yerine verilere erişim yolu uygulamasıdır. Veri erişim yolları açıkça kaydı kaldırılmış işaretçiler veya otomatik kaydı kaldırılmış işaretçiler (referans türü) olabilir.

Daha fazla bilgi için lütfen Robert Sebesta, 10. Baskı, Bölüm 9'un Programlama Dilleri Kavramları kitabına bakınız.


3

Bir int'i referans olarak geçirmiyorsunuz, bir göstericiden int'e değere geçiyorsunuz. Farklı sözdizimi, aynı anlam.


1
+1 "Farklı sözdizimi, aynı anlam." .. Yani aynı şey, anlam sözdiziminden daha önemli olduğu için.

Doğru değil. Çağırarak void func(int* ptr){ *ptr=111; int newValue=500; ptr = &newvalue }ile int main(){ int value=0; func(&value); printf("%i\n",value); return 0; }, size referans olarak geçiyoruz 111 yerine 500., bu referans ile parametre geçirerek desteklemez 500. C yazdırmalısınız yazdırır.
Konfle Dolex

@Konfle, sözdizimsel olarak referans yoluyla geçiyorsanız ptr = &newvalueizin verilmez. Fark ne olursa olsun, "aynı anlamın" tam olarak doğru olmadığına dikkat çekiyorsunuz çünkü C'de ekstra işlevsellik var ("referansı" yeniden atama yeteneği).
xan

ptr=&newvalueReferansla geçilmiş gibi asla bir şey yazmayız. Bunun yerine, yazıyoruz ptr=newvalueBurada C ++ bir örnek: void func(int& ptr){ ptr=111; int newValue=500; ptr = newValue; }func () geçirilen parametrenin değeri olur 500.
Konfle Dolex

Yukarıdaki yorumumda, param'ı referans olarak geçmek anlamsızdır. Bununla birlikte, eğer param POD yerine bir nesne ise, bu önemli bir fark yaratacaktır, çünkü param = new Class()bir fonksiyonun içinde değiştirilen herhangi bir değer, değer (işaretçi) tarafından iletilirse arayan için herhangi bir etki yaratmayacaktır. Eğer paramreferans ile geçirilir, değişiklikler arayan için görünür olacaktır.
Konfle Dolex

3

C'de referans olarak geçmek için, &bir değişkene karşı kullanılması gereken operatörün adresini kullanırsınız , ancak sizin durumunuzda, işaretçi değişkenini kullandığınızdan, poperatörün adresinin önüne ön ek eklemeniz gerekmez. &i: Parametresi olarak kullansaydınız doğru olurdu f(&i).

Ayrıca, referansı kaldırmak pve bu değerin nasıl eşleştiğini görmek için de ekleyebilirsiniz i:

printf("p=%d \n",*p);

Neden ona printf eklemesi gerektiğini söylemek için tüm kodu (bu yorum bloğu dahil) tekrarlama gereği duydunuz?

@Neil: Bu hata @ William'ın düzenlemesiyle ortaya çıktı, şimdi tersine çevireceğim. Ve şimdi tommieb'in çoğunlukla doğru olduğu açıktır: sadece değişkenlere değil, herhangi bir nesneye & uygulayabilirsiniz.

2

işaretçiler ve referanslar iki farklı boyuttadır.

Bahsetmediğim birkaç şey var.

İşaretçi bir şeyin adresidir. Bir işaretçi diğer değişkenler gibi saklanabilir ve kopyalanabilir. Böylece bir boyutu vardır.

Bir referans, bir şeyin ALIAS'ı olarak görülmelidir. Bir boyutu yoktur ve saklanamaz. Bir şeye referans ZORUNLUDUR, yani. boş olamaz veya değiştirilemez. Bazen, derleyicinin referansı bir işaretçi olarak saklaması gerekir, ancak bu bir uygulama detayıdır.

Referanslarla sahiplik yönetimi, boş kontrol, kullanımda referans gösterme gibi işaretçilerle ilgili sorunlarınız olmaz.


1

'Referans ile geç' (işaretçiler kullanılarak) en başından beri C'de olmuştur. Neden olmadığını düşünüyorsun?


7
Çünkü teknik olarak referansla geçmiyor.
Mehrdad Afshari

7
Bir işaretçi değerinin iletilmesi, referans by pass ile aynı değildir. Değerinin güncelleştirilmesi j( değil *j ) in f()üzerinde hiçbir etkisi yoktur iiçinde main().
John Bode

6
Bu anlam aynı şey başvuruyla geçirme gibi ve bu kadar referans olarak geçiyor söylemek yeterince iyi. Doğru, C Standardı "referans" terimini kullanmaz, ama beni ne şaşırtır ne de bir problemdir. Standardı ifade edebilmemize rağmen, SO hakkında standart konuşmuyoruz, aksi takdirde hiç kimse değerlerden bahsetmezdik (C Standardı terimi kullanmaz).

4
@Jim: Bize John'un yorumunu onaylayan sensin olduğunu söylediğin için teşekkürler.

1

Bence C aslında referans ile geçişi destekliyor.

Çoğu dil, sözdizimsel şekerin değer yerine referansla geçmesini gerektirir. (Örneğin C ++, parametre bildiriminde & parametresini gerektirir).

C ayrıca bunun için sözdizimsel şeker gerektirir. * Parametre türü bildiriminde ve & bağımsız değişkeninde. Yani * ve & olduğunu referans olarak geçiş için C sözdizimi.

Şimdi referansla gerçek geçişin argüman tarafında değil, sadece parametre bildiriminde sözdizimi gerektirdiği söylenebilir.

Ama şimdi C # geliyor yapar referans geçmesiyle destek ve üzerinde sözdizimsel şeker gerektirir hem parametre ve argüman tarafı.

C'nin by-ref geçişine sahip olmadığı argümanı, sözdizimsel öğelerin onu temeldeki teknik uygulamayı sergilediğini ifade etmesine neden olur, çünkü bu, tüm uygulamalar için az çok geçerlidir.

Geriye kalan tek argüman, ref'ye C ile geçmenin monolitik bir özellik olmadığı, ancak mevcut iki özelliği birleştirdiğidir. (& İle argümanın ref'sini alın, ref'nin * ile yazmasını bekleyin.) Örneğin C #, iki sözdizimsel eleman gerektirir, ancak birbirleri olmadan kullanılamazlar.

Bu açıkçası tehlikeli bir argüman, dilde birçok başka özellik diğer özelliklerden oluşuyor. (C ++ 'da dize desteği gibi)


1

Yaptığınız şey referansla geçmeyen değere göre geçmektir. Çünkü 'f' değişkeninin değerini 'f' fonksiyonuna gönderiyorsunuz (ana olarak f (p);)

Referans ile pass ile aynı program gibi görünecektir, (!!! Bu program referans ile pass C desteklenmediği gibi 2 hata veriyor)

#include <stdio.h>

void f(int &j) {    //j is reference variable to i same as int &j = i
  j++;
}

int main() {
  int i = 20;
  f(i);
  printf("i = %d\n", i);

  return 0;
}

Çıktı:-

3:12: hata: '&' belirtecinden önce ';', ',' veya ')' bekleniyor
             void f (int & j);
                        ^
9: 3: uyarı: 'f' işlevinin dolaylı açıklaması
               f (a) '
               ^
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.