C'deki işleve dizi ve dizi işaretçisini geçirme arasındaki fark


110

C'deki iki işlev arasındaki fark nedir?

void f1(double a[]) {
   //...
}

void f2(double *a) {
   //...
}

İşlevleri büyük ölçüde uzun bir dizide çağıracak olsaydım, bu iki işlev farklı davranır mıydı, yığın üzerinde daha fazla yer kaplar mıydı?

Yanıtlar:


114

İlk olarak, bazı standartlar :

6.7.5.3 Fonksiyon tanımlayıcıları (prototipler dahil)
...
7 Bir parametrenin '' tip dizisi '' olarak bildirimi , tip niteleyicilerinin (varsa) belirtilenler olduğu '' tipe uygun işaretçi '' olarak ayarlanacaktır. içinde [ve ]dizi türü türetilmesi. Anahtar kelime , dizi türü türetmenin ve staticiçinde de görünüyorsa , işleve yapılan her çağrı için, karşılık gelen gerçek bağımsız değişkenin değeri, boyut tarafından belirtilen en az sayıda öğeye sahip bir dizinin ilk öğesine erişim sağlayacaktır. ifadesi. []

Yani, kısacası, herhangi bir fonksiyon parametresi olarak ilan T a[]veya T a[N]tedavi edilir sanki o ilan edildi T *a.

Öyleyse, neden dizi parametreleri işaretçi olarak bildirilmiş gibi davranılıyor? İşte nedeni:

6.3.2.1 SolDeğerler, diziler ve işlev koordinat belirleme
...
bunun işlenen olduğunda hariç 3 sizeofoperatör veya tekli &operatör veya bir diziyi başlatmak için kullanılan bir dize, türü '' dizisi olan bir ifadedir türü ' ', dizi nesnesinin ilk öğesine işaret eden ve bir l değeri olmayan' ' tipe işaretçi' 'türüne sahip bir ifadeye dönüştürülür . Dizi nesnesinin kayıt depolama sınıfı varsa, davranış tanımsızdır.

Aşağıdaki kod verildiğinde:

int main(void)
{
  int arr[10];
  foo(arr);
  ...
}

Çağrıda foo, dizi ifadesi arrya ya sizeofda işlenenlerinden biri değildir &, bu nedenle türü örtük olarak 6.2.3.1/3 uyarınca "10 elemanlı dizi int" den "işaretçi int" ye dönüştürülür. Böylece, foobir dizi değeri yerine bir işaretçi değeri alacaktır.

6.7.5.3/7 nedeniyle şu şekilde yazabilirsiniz foo:

void foo(int a[]) // or int a[10]
{
  ...
}

ama şu şekilde yorumlanacak

void foo(int *a)
{
  ...
}

Bu nedenle, iki form aynıdır.

6.7.5.3 / 7'deki son cümle C99 ile tanıtıldı ve temel olarak şu anlama gelir:

void foo(int a[static 10])
{
  ...
}

değerine karşılık gelen gerçek parametre a, en az 10 öğeli bir dizi olmalıdır .


1
MSVC C ++ derleyicilerini kullanırken (en azından bazı eski), derleyicinin işlev adını iki durumda farklı şekilde yanlış bir şekilde karıştırması nedeniyle (aksi takdirde aynı olduklarını kabul ederken) bağlantı sorunlarına neden olan bir fark vardır. "
Düzeltilmeyecek

29

Fark tamamen sözdizimseldir. C'de, dizi gösterimi bir işlev parametresi için kullanıldığında, otomatik olarak bir işaretçi bildirimine dönüştürülür.


1
@Kaushik: Bu durumda aynı olsalar da , genel durumda
BlueRaja - Danny Pflughoeft

@BlueRaja: evet, C'nin tuzaklarından biridir. İşlev parametrelerinin bildirimi yerel değişkenlerin bildirimine çok benzer , ancak birkaç ince farklılık vardır (bu diziden işaretçiye otomatik dönüşüm gibi) dikkatsiz programcıyı ısırmaya eğilimli.
Thomas Pornin

0

Hayır, aralarında bir fark yok. Test etmek için bu C kodunu Dev C ++ (mingw) derleyicisine yazdım:

#include <stdio.h>

void function(int* array) {
     int a =5;
}

void main() {  
     int array[]={2,4};
     function(array);
     getch();
}

IDA'da ikili dosyanın her iki çağıran sürümünün .exe'deki ana işlevi parçalarına ayırdığımda, aşağıdaki gibi tam olarak aynı derleme kodunu alıyorum:

push    ebp
mov     ebp, esp
sub     esp, 18h
and     esp, 0FFFFFFF0h
mov     eax, 0
add     eax, 0Fh
add     eax, 0Fh
shr     eax, 4
shl     eax, 4
mov     [ebp+var_C], eax
mov     eax, [ebp+var_C]
call    sub_401730
call    sub_4013D0
mov     [ebp+var_8], 2
mov     [ebp+var_4], 4
lea     eax, [ebp+var_8]
mov     [esp+18h+var_18], eax
call    sub_401290
call    _getch
leave
retn

Yani bu çağrının iki versiyonu arasında bir fark yok, en azından derleyici onları eşit şekilde tehdit ediyor.


18
Üzgünüz, ancak bu yalnızca gcc'nin bazı sürümlerinin her ikisi için de x86'da aynı derlemeyi oluşturduğunu kanıtlıyor. Doğru cevap, yanlış açıklama.
lambdapower
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.