C'deki bir işleve argüman olarak bir dizi geçirme


88

Argüman olarak dizi içeren bir fonksiyon yazdım ve aşağıdaki gibi dizi değerini ileterek çağırdım.

Bulduğum şey arraytest(), değerleri ileterek fonksiyonu çağırıyor olsam da , orijinal kopyası int arr[]değiştirildi.

Lütfen nedenini açıklar mısınız?


1
Diziyi referans olarak geçiriyorsunuz ancak içeriğini değiştiriyorsunuz - bu nedenle verilerde bir değişiklik görüyorsunuz
Shaun Wilde

main()geri dönmelidir int.
undercore_d

Yanıtlar:


140

Bir diziyi parametre olarak iletirken, bu

tam olarak aynı anlama gelir

böylece edilir ana değerleri değiştirerek.

Tarihsel nedenlerden ötürü, diziler birinci sınıf vatandaş değildir ve değer üzerinden geçirilemez.


3
Hangi koşullar altında hangi gösterim daha iyidir?
Ramon Martinez

22
@Ramon - Daha az kafa karıştırıcı göründüğü ve dizinin bir kopyasını almadığınızı daha iyi gösterdiği için ikinci seçeneği kullanırdım.
Bo Persson

2
"Tarihsel nedenleri" açıklayabilir misiniz? Ben .. bir kopyasını ve hafıza kaybı böylece gerekir değerlerle geçen sayesinde varsayalım
Jacquelyn.Marquardt

5
@lucapozzobon - Başlangıçta C, tek değerler dışında herhangi bir değere göre geçişe sahip değildi. Bunun structdeğiştirildiği dile eklenene kadar değildi. Ve sonra diziler için kuralları değiştirmek için çok geç kabul edildi. Zaten 10 kullanıcı vardı. :-)
Bo Persson

1
..., void arraytest(int a[1000])vb. ile tam olarak aynı anlamına gelir . Burada genişletilmiş yanıt: stackoverflow.com/a/51527502/4561887 .
Gabriel Staples

9

Tek boyutlu bir diziyi bir işlevde bağımsız değişken olarak geçirmek istiyorsanız , aşağıdaki üç yoldan biriyle biçimsel bir parametre bildirmeniz gerekir ve üç bildirim yöntemi de benzer sonuçlar üretir çünkü her biri derleyiciye bir tamsayı işaretçisinin gittiğini söyler. alınacak .

Yani, orijinal değerleri değiştiriyorsunuz.

Teşekkürler !!!


İkinci örneğinizi arıyordum, her yöntemin avantajları nelerdir?
Puck

8

Diziyi kopya olarak geçirmiyorsunuz. Yalnızca dizinin ilk elemanının bellekte olduğu adresi gösteren bir göstericidir.



7

C'deki diziler, çoğu durumda, dizinin kendisinin ilk öğesine bir göstericiye dönüştürülür. Ve ayrıntılı olarak işlevlere aktarılan diziler her zaman işaretleyicilere dönüştürülür.

İşte K & R2nd'den bir alıntı :

Bir dizi adı bir işleve iletildiğinde, iletilen şey ilk öğenin konumudur. Çağrılan işlev içinde, bu bağımsız değişken yerel bir değişkendir ve dolayısıyla bir dizi adı parametresi bir işaretçi, yani bir adres içeren bir değişkendir.

Yazı:

yazmakla aynı anlama sahiptir:

Yani, açıkça yazmamanıza rağmen, bir işaretçiyi geçtiğiniz ve böylece ana değerdeki değerleri değiştirdiğiniz gibidir.

Daha fazlası için gerçekten okumanızı öneririz bu .

Dahası, SO ile ilgili diğer cevapları burada bulabilirsiniz


6

Dizinin ilk üyesinin bellek konumunun değerini iletiyorsunuz.

Bu nedenle, işlevin içindeki diziyi değiştirmeye başladığınızda, orijinal diziyi değiştirmiş olursunuz.

Unutmayın a[1]olduğunu *(a+1).


1
Sanırım * a + 1 için eksik () eksik olmalı * (a + 1)
ShinTakezou

@Shin Teşekkürler, C ile oynadığımdan beri bir süredir
alex

6

C'de, birkaç özel durum dışında, bir dizi başvurusu her zaman dizinin ilk elemanına bir göstericiye "bozunur". Bu nedenle, bir dizi "değere göre" geçirmek mümkün değildir. Bir işlev çağrısındaki bir dizi, diziyi başvuruya göre iletmeye benzer şekilde işleve işaretçi olarak geçirilecektir.

DÜZENLEME: Bir dizinin ilk elemanına bir göstericiye bozulmadığı üç özel durum vardır:

  1. sizeof aile aynı değil sizeof (&a[0]).
  2. &aile aynı değil &(&a[0])(ve tamamen aynı değil &a[0]).
  3. char b[] = "foo"ile aynı değil char b[] = &("foo").

Bir diziyi bir işleve aktarırsam. Örneğin, bir dizi yaptım int a[10]ve her öğeye rastgele bir değer atadım. Şimdi bu diziyi int y[]veya int y[10]veya kullanarak bir işleve geçirirsem int *yve sonra bu işlevde sizeof(y)Cevapla kullanırım bayt işaretçisi tahsis edilmiş olur. Yani bu durumda bir işaretçi olarak çürüyecek, Bunu da dahil ederseniz Yararlı Olacaktır. Bu gönderiye bakınimg.org/image/prhleuezd
Suraj Jain

sizeofBaşlangıçta tanımladığımız dizide işlevde çalışmayı kullanırsam , o zaman bir dizi olarak bozulur, ancak başka bir işlevi geçirirsem o zaman sizeofoperatörü kullanırsam bir işaretçi olarak bozulur.
Suraj Jain


Bunun eski olduğunu biliyorum. Bunu biri :) 1. @ThomSmith yazdı görmek olursa İki soru &aile aynı değil ise &a[0]ne zaman abir dizidir. Nasıl yani? Benim test programımda, her ikisi de dizinin bildirildiği işlevde ve farklı bir işleve aktarıldığında aynı olduğunu gösteriyor. 2. Yazar " char b[] = "foo"ile aynı şey değil " diye yazar char b[] = &("foo"). Benim için ikincisi derlenmiyor bile. Sadece ben mi?
Aviv Cohn

6

Çok boyutlu bir diziyi bir işleve argüman olarak iletmek. Bağımsız değişken olarak tek sönük bir diziyi aktarmak aşağı yukarı önemsizdir. 2 sönük diziyi geçmenin daha ilginç durumuna bir göz atalım. C'de int **2 dim dizi yerine construct ( ) işaretçisi için bir işaretçi kullanamazsınız . Bir örnek verelim:

Burada, ilk argüman olarak 5 tam sayılık bir diziye işaretçi alan bir işlev belirledim. 5 sütunlu herhangi bir 2 dim dizisini bağımsız değişken olarak geçirebilirim:

Herhangi bir 2 sönük diziyi kabul edebilecek ve işlev imzasını aşağıdaki gibi değiştirebilecek daha genel bir işlev tanımlama fikri aklınıza gelebilir:

Bu kod derlenir, ancak değerleri ilk işlevde olduğu gibi atamaya çalışırken bir çalışma zamanı hatası alırsınız. Yani C'de çok boyutlu diziler, işaretçilerden işaretçilerle aynı değildir ... işaretçiler. An int(*arr)[5], 5 öğeden oluşan diziye bir göstericidir, bir int(*arr)[6] 6 öğeden oluşan diziye bir göstericidir ve bunlar, farklı türlere işaretçilerdir!

Peki, daha yüksek boyutlar için fonksiyon argümanları nasıl tanımlanır? Basit, biz sadece kalıbı takip ediyoruz! Burada aynı işlev 3 boyutlu bir dizi alacak şekilde ayarlanmıştır:

Beklediğiniz gibi, ikinci boyutta 4 öğe ve üçüncü boyutta 5 öğe bulunan herhangi bir 3 boyutlu diziyi bağımsız değişken olarak alabilir. Bunun gibi herhangi bir şey tamam olabilir:

Ancak ilk boyuta kadar tüm boyutları belirlememiz gerekiyor.


6

Diziden ptr'ye doğal tür bozunumuyla C'de standart dizi kullanımı

@Bo Persson doğru onun büyük cevap devletler burada :

Bir diziyi parametre olarak iletirken, bu

tam olarak aynı anlama gelir

Bununla birlikte, yukarıdaki iki formun da şunu ekleyeyim:

  1. tam olarak aynı demek

  2. bu tamamen aynı anlamına gelir

  3. bu tamamen aynı anlamına gelir

  4. bu tamamen aynı anlamına gelir

  5. vb.

Yukarıdaki dizi örneklerinin her birinde, girdi parametresi türü bir değerine düşerint * ve derleme seçenekleri -Wall -Wextra -Werroraçık olsa bile hiçbir uyarı ve hata olmadan çağrılabilir ( bu 3 derleme seçeneğiyle ilgili ayrıntılar için burada depomuza bakın ) bu:

Nitekim olarak, "boyut" değeri ( [0], [1], [2], [1000]dizi parametrenin içine, vs.) burada estetik / öz dokümantasyon amaçlı görünüşe bakılırsa, ve herhangi bir pozitif tam sayı olabilir ( size_tistediğiniz sanırım tip)!

Bununla birlikte, pratikte, işlevin almasını beklediğiniz dizinin minimum boyutunu belirtmek için kullanmalısınız, böylece kod yazarken izlemeniz ve doğrulamanız kolay olur. MISRA-C-2012 standardı ( al / burada £ 15.00 için standart 236-pg 2012-sürüm PDF bugüne kadar devlet (vurgu eklenmiştir) olarak geçer):

Kural 17.5 Bir dizi türüne sahip olduğu bildirilen bir parametreye karşılık gelen işlev bağımsız değişkeni, uygun sayıda elemana sahip olmalıdır.

...

Bir parametre belirtilen boyuta sahip bir dizi olarak bildirilirse, her işlev çağrısındaki karşılık gelen bağımsız değişken, en az dizi kadar öğeye sahip bir nesneyi işaret etmelidir.

...

Bir işlev parametresi için bir dizi tanımlayıcının kullanılması, işlev arabirimini bir işaretçi kullanmaktan daha açık bir şekilde belirtir. İşlev tarafından beklenen minimum eleman sayısı açıkça belirtilmiştir, ancak bu bir işaretçi ile mümkün değildir.

Başka bir deyişle, C standardı teknik olarak onu zorlamasa da, açık boyut biçimini kullanmanızı önerirler - en azından bir geliştirici olarak size ve kodu kullanan diğerlerine, işlevin hangi boyut dizisini beklediğini netleştirmeye yardımcı olur. geçmek için.


C'deki dizilerde tür güvenliğini zorlama

@Winger Sendon'un cevabımın altındaki bir yorumda işaret ettiği gibi, C'yi bir dizi tipini dizi boyutuna bağlı olarak farklı şekilde ele almaya zorlayabiliriz !

Öncelikle, kullandığınız, sadece yukarıda benim örnekte tanımak gerekir int array1[2];böyle: arraytest(array1);nedenleri array1otomatik çürüme bir içine int *. ANCAK, yerine adresini alıp ararsanız , tamamen farklı bir davranışla karşılaşırsınız! array1arraytest(&array1)Şimdi, bir int *! Bunun yerine, &array1is türü, "boyut 2 int dizisine işaretçi " veya " int türünde boyut 2 dizisine işaretçi"int (*)[2] anlamına gelir . Bu nedenle, bir dizide tür güvenliğini kontrol etmek için C'yi şu şekilde yapabilirsiniz:

Bu sözdiziminin okunması zordur, ancak bir işlev göstericisininkine benzer . Çevrimiçi araç, cdecl , bize şunu söyler int (*a)[2]: "int dizisinin 2. dizisine işaretçi olarak bir işaretçi bildir " (2 ints dizisine işaretçi ). : Parantez olmadığı versiyon ile karıştırmayın Do NOT int * a[2], araçlar: "int pointer dizisi 2. olarak bir beyan" (2 dizisi işaretçiler için int).

Şimdi, bu işlev, bunu adres işleci ( &) ile bu şekilde çağırmanızı GEREKTİRİR , bir girdi parametresi olarak DOĞRU BOYUTLU BİR DİZAYA İŞARETLEYİCİ !:

Ancak bu, bir uyarı oluşturacaktır:

Bu kodu burada test edebilirsiniz .

C derleyicisini bu uyarıyı bir hataya dönüştürmeye zorlamak için, her zaman arraytest(&array1);yalnızca doğru boyut ve türde ( int array1[2];bu durumda) bir giriş dizisi kullanarak çağırmanız GEREKİR -Werror, derleme seçeneklerinize ekleyin . Yukarıdaki test kodunu onlinegdb.com'da çalıştırıyorsanız, bunu sağ üstteki dişli çark simgesini tıklayarak yapın ve bu seçeneği yazmak için "Ekstra Derleyici Bayrakları" na tıklayın. Şimdi, bu uyarı:

bu yapı hatasına dönüşecek:


Ayrıca, belirli bir boyuttaki diziler için "güvenli tür" işaretçileri oluşturabileceğinizi unutmayın, örneğin:

... ama yok illa bana C ++ dil sözdizimi karmaşıklığı, ayrıntı ve zorluk architecting kod olağanüstü yüksek maliyetle, her yerde kuvvet tip güvenliği için kullanılan antikalar çok hatırlatıyor olarak, bu tavsiye ve hangi ben sevmediğim ve daha önce defalarca mırıldandı (örn: burada "C ++ Üzerine Düşüncelerim" konusuna bakın ).


Ek testler ve deneyler için, hemen aşağıdaki bağlantıya da bakın.

Referanslar

Yukarıdaki bağlantılara bakın. Ayrıca:

  1. Çevrimiçi kod denemem: https://onlinegdb.com/B1RsrBDFD

2
void arraytest(int (*a)[1000])daha iyidir çünkü boyut yanlışsa derleyici hata verecektir.
Winger Sendon

@WingerSendon, ben buraya doğrulamak için gereken bazı incelikleri olduğunu biliyordu ve benim zaman aldı ve sonunda başlıklı geniş yeni bölümü ile benim cevap güncelledik böylece sözdizimi, (olup kafa karıştırıcı bir işlev ptr sözdizimi gibi) kafa karıştırıcı Forcing type safety on arrays in Ckapsayan senin nokta.
Gabriel Staples

@ GabrielStaples, Teşekkürler. Cevabınız çok yardımcı oldu. Bu şekilde ileri düzey c öğrenmek için bana bir referans verebilir misiniz?
daryooosh

@daryooosh, maalesef yapamam. Harika referanslarım yok. Yıllar boyunca derinlere inerek bunu biraz buradan, biraz da oradan anladım. Yapabileceğim en iyi şey, bu şekilde öğrendiklerimin bir kısmını ara sıra buradaki eRCaGuy_hello_world depoma bıraktığımı söylemektir . Yukarıda kullandığım C tipi güvenlik malzemelerinin ÇOK idareli kullanılması gerektiğini unutmayın. Kodunuzu karmaşıklaştıracak ve okunabilirliği bir ton azaltacak ve buna değmez. Mümkün olduğunda basit sözdizimine odaklanın ve her şeyi okunaklı hale getirin.
Gabriel Staples

Ayrıca, kanonik klasik C ders kitabının şu K&R C Programlama Dili kitabı olduğuna dikkat edin : en.wikipedia.org/wiki/The_C_Programming_Language .
Gabriel Staples

0

a[]Veya kullanırsanız diziler her zaman referans olarak aktarılır *a:


Ben buna olumlu oy veriyorum. Neden reddedildiğinden emin değilim.
Gabriel Staples
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.