Neden bir dizi parametresinin boyutu main içindeki ile aynı değil?


104

Parametre olarak gönderilen bir dizinin boyutu neden main içindeki ile aynı değil?

#include <stdio.h>

void PrintSize(int p_someArray[10]);

int main () {
    int myArray[10];
    printf("%d\n", sizeof(myArray)); /* As expected, 40 */
    PrintSize(myArray);/* Prints 4, not 40 */
}

void PrintSize(int p_someArray[10]){
    printf("%d\n", sizeof(p_someArray));
}

Yanıtlar:


103

Bir dizi türü, onu bir işleve aktardığınızda örtük olarak işaretçi türüne dönüştürülür.

Yani,

void PrintSize(int p_someArray[10]) {
    printf("%zu\n", sizeof(p_someArray));
}

ve

void PrintSize(int *p_someArray) {
    printf("%zu\n", sizeof(p_someArray));
}

eşdeğerdir. Öyleyse elde ettiğiniz şey,sizeof(int*)


2
C ++ 'da diziyi işleve göre iletebilirsiniz, ancak bunu C'de
yapamazsınız

13
Dizinin boyutunu ayrı bir parametre olarak iletmeniz gerekir. O zaman dizinin boyutu sizeof (* p_someArray) * length
Aric TenEyck

14
Minor nit: sizeofoperatör, türde bir nesne döndürür size_t, bu nedenle %zu(C99) ile yazdırmanız veya çağrılarınızda yukarıdaki gibi intkullanırsanız, atmanız %dgerekir printf.
Alok Singhal

4
Alok'un ifadesi doğrudur. Printf (..) 'de yanlış biçim belirteci kullanmak UB'dir.
Prasoon Saurav

1
Chris_45 @ C bir referans vardır, ama C nda olduğu gibi, tüm dizi için işaretçi ile bir dizi geçebilir: void PrintSize(int (*p_someArray)[10]). İşlevi içinde sen KQUEUE operatörünü kullanarak dizisi erişebilirsiniz *: sizeof(*p_someArray). Bu, C ++ 'daki referansları kullanmakla aynı etkiye sahip olacaktır.
AnT

18

Bu bir işaretçi, bu nedenle dizinin boyutunu işleve ikinci bir parametre olarak geçirmek yaygın bir uygulamadır.


16

Diğerlerinin de belirttiği gibi, diziler işlev parametreleri olarak kullanıldıklarında ilk elemanlarına işaretçiler olarak bozulurlar. Sizeof'un ifadeyi değerlendirmediğini ve bir ifadeyle kullanıldığında parantez gerektirmediğini de belirtmek gerekir, bu nedenle parametreniz aslında hiç kullanılmıyor, bu nedenle sizeof değerini değer yerine türle de yazabilirsiniz.

#include <stdio.h>

void PrintSize1 ( int someArray[][10] );
void PrintSize2 ( int someArray[10] );

int main ()
{
    int myArray[10];
    printf ( "%d\n", sizeof myArray ); /* as expected 40 */
    printf ( "%d\n", sizeof ( int[10] ) ); /* requires parens */
    PrintSize1 ( 0 ); /* prints 40, does not evaluate 0[0] */
    PrintSize2 ( 0 ); /* prints 40, someArray unused */
}

void PrintSize1 ( int someArray[][10] )
{
    printf ( "%d\n", sizeof someArray[0] );
}

void PrintSize2 ( int someArray[10] )
{
    printf ( "%d\n", sizeof ( int[10] ) );
}

12

Yani, dizinin uzunluğunu ikinci bir parametre olarak aktarmanız gerekecek. Her ikinizin de sabit büyüklükte bir dizi bildirdiğiniz ve daha sonra bu diziyi bir işleve geçirdiğiniz kod yazarken, dizi uzunluğu sabitinin kodunuzda birkaç yerde gösterilmesi bir acıdır ...

Kurtarmaya K&R:

#define N_ELEMENTS(array) (sizeof(array)/sizeof((array)[0])) 

Yani şimdi örneğin şunları yapabilirsiniz:

int a[10];
...
myfunction(a, N_ELEMENTS(a));

dizinin boyutu kodlama zamanında mevcut değilse, ancak yalnızca çalışma zamanında mevcutsa ne olur? Dizinin boyutunu, boyutunu sabit kodlamadan hesaplamanın başka bir yolu var mı?
weefwefwqg3

Gösterilen yöntem yalnızca dizinin bildirimi "görünümde" olduğunda çalışır. Diğer tüm durumlar için, dizi uzunluğunu manuel olarak iletmeniz gerekir.
SC Madsen

5

Çünkü diziler parametre olarak aktarıldıklarında göstericilere dönüşürler. C ++ 'da "dizileri" referans olarak iletebilir ve bu sorunun üstesinden gelebilirsiniz, ancak C böyle çalışır. Bu işleve farklı boyutlardaki dizileri aktarabileceğinizi unutmayın:

 // 10 is superfluous here! You can pass an array of different size!
void PrintSize(int p_someArray[10]);

5

C ++ 'da bu amaç için bir diziyi referans olarak iletebilirsiniz:

void foo(int (&array)[10])
{
    std::cout << sizeof(array) << "\n";
}

1
Bu bir C sorusuna nasıl yardımcı olur?
alk

5

Bulduğunuz davranış aslında C dilinde büyük bir siğil. Bir dizi parametresi alan bir işlevi her bildirdiğinizde, derleyici sizi yok sayar ve parametreyi bir işaretçi olarak değiştirir. Yani bu beyannamelerin hepsi ilki gibi davranır:

void func(int *a)
void func(int a[])
void func(int a
typedef int array_plz[5];
void func(array_plz a)

a, dört durumda da int'e bir gösterici olacaktır. Bir diziyi func'a geçirirseniz, hemen ilk elemanına bir göstericiye dönüşür. (64 bitlik bir sistemde, 64 bitlik bir işaretçi 32 bitlik bir int'in iki katı büyüklüğündedir, bu nedenle sizeof oranınız 2'yi döndürür.)

Bu kuralın tek amacı, toplam değerleri işlev bağımsız değişkenleri olarak geçirmeyi desteklemeyen geçmiş derleyicilerle geriye dönük uyumluluğu korumaktır.

Bu, bir diziyi bir işleve aktarmanın imkansız olduğu anlamına gelmez. Diziyi bir yapıya yerleştirerek bu siğilden kurtulabilirsiniz (bu temelde C ++ 11'in std :: array'in amacıdır):

struct array_rly {
int a[5];
};
void func(struct array_rly a)
{
printf("%zd\n", sizeof(a.a)/sizeof(a.a[0]));  /* prints 5 */
}

veya diziye bir işaretçi ileterek:

void func(const int (*a)[5])
{
printf("%zd\n", sizeof(*a)/sizeof((*a)[0]));  /* prints 5 */
}

Dizi boyutunun bir derleme zamanı sabiti olmaması durumunda, C99 değişken uzunluklu dizilerle diziye işaretçi tekniğini kullanabilirsiniz:

void func(int n, const int (*a)[n])
{
printf("%zd\n", sizeof(*a)/sizeof((*a)[0]));  /* prints n */
}

3

C dilinde, bilinmeyen bir dizinin boyutunu belirlemek için bir yöntem yoktur, bu nedenle miktarın ilk öğeye bir göstericinin yanı sıra iletilmesi gerekir.


2
Genel olarak, bir dizinin boyutunu (eleman sayısını) bir diziyle birlikte bir dizinin boyutunu belirlemenin başka bir yoluna (örneğin, char[]dizgi dizilerinin sonunda bir boş karakter sonlandırıcı) sahip olmadığınız sürece bir işleve geçirmelisiniz.
David R Tribble

Lütfen " bilinmeyen dizi " nedir?
alk

3

Dizileri fonksiyonlara geçiremezsiniz.

Boyutu gerçekten yazdırmak istiyorsanız, bir diziye bir işaretçi iletebilirsiniz, ancak işlev için dizi boyutunu da tanımlamanız gerektiğinden, bu hiçbir şekilde genel olmayacaktır.

#include <stdio.h>

void PrintSize(int (*p_anArray)[10]);

int main(void) {
    int myArray[10];
    printf("%d\n", sizeof(myArray)); /* as expected 40 */
    PrintSize(&myArray);/* prints 40 */
}

void PrintSize(int (*p_anArray)[10]){
    printf("%d\n", (int) sizeof(*p_anArray));
}

0

Davranış tasarım gereğidir.

İşlev parametresi bildirimindeki aynı sözdizimi, yerel değişken tanımından tamamen farklı bir anlama gelir.

Nedeni diğer cevaplarda açıklanmıştır.


0

C dilinde diziyi işleve bir argüman olarak ilettiğinizde, otomatik olarak göstericiye dönüştürülür, bir işlevden geçen dizi diğer işlevin gönderimli çağrı olarak bilinir. Bu, çağrılan işlevin yalnızca işlevin ilk öğesine işaret eden işaretçiyi almasının nedenidir.

eğlence (int a []), eğlenceye benzer (int * a);

böylece dizinin boyutunu yazdırdığınızda, ilk elemanın boyutunu yazdıracaktır.


C'de " referansla arama " yoktur.
alk

" dizinin boyutunu yazdırdığınızda, ilk elemanın boyutunu yazdıracaktır. " hayır, bir göstericinin boyutunu yazdırır.
alk

-1

'C' programlama dilinde 'sizeof ()' operatördür ve nesnenin boyutunu bayt olarak döndürür. 'Sizeof ()' operatörünün argümanı sol değer türü olmalıdır (tamsayı, kayan sayı, yapı, dizi Yani bir dizinin bayt cinsinden boyutunu bilmek istiyorsanız bunu çok basit bir şekilde yapabilirsiniz. 'Sizeof ()' operatörünü ve argümanı için dizi adını kullanın. Örneğin:

#include <stdio.h>

main(){

 int n[10];
 printf("Size of n is: %d \n", sizeof(n)); 

}

32 bit sistemde çıktı: n'nin boyutu: 40. Çünkü 32 sistemde ineteger 4 bayttır. 64x'te 8 bayttır.Bu durumda bir dizide tanımlanmış 10 tam sayıya sahibiz. Sonuç '10 * sizeof ( int) '.

Bazı ipuçları:

Bunun gibi tanımlanmış bir dizimiz varsa 'int n [] = {1, 2, 3, ... 155 ..};'. Bu yüzden, bu dizide kaç eleman saklandığını bilmek istiyoruz. Bu algoritmayı kullanın:

sizeof (dizinin_ismi) / sizeof (dizi_türü)

Kod: #include

ana(){

int n[] = { 1, 2, 3, 44, 6, 7 };
printf("Number of elements: %d \n", sizeof(n) / sizeof(int));
return 0;

}


2
StackOverflow'a hoş geldiniz ve bir cevap yazdığınız için teşekkürler. Ne yazık ki, bu, özellikle sizeof(n)yerel bir değişken için ve sizeof(arg)bir işleve yönelik bir argüman arasındaki farkla ilgili olan soruyu ele almıyor , her ikisi de görünüşte tipte olsa bile int[10].
MicroVirus

-3

Diziler yalnızca gevşek boyuttadır. Çoğunlukla, bir dizi belleğe bir göstericidir. Bildiriminizdeki boyut, derleyiciye yalnızca dizi için ne kadar bellek ayıracağını söyler - türle ilişkili değildir, bu nedenle sizeof () 'un devam edecek hiçbir şeyi yoktur.


4
Üzgünüz, bu cevap yanıltıcıdır. Ne diziler "gevşek boyutta" ne de "belleğe işaretçiler". Diziler çok kesin bir boyuta sahiptir ve bir dizi adının ilk elemanına bir gösterici anlamına geldiği yerler kesin olarak C standardı tarafından belirtilir.
Jens
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.