Dlaczego rozmiar parametru tablicy nie jest taki sam jak w main?

104

Dlaczego rozmiar tablicy wysyłanej jako parametr nie jest taki sam jak w main?

#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));
}
Chris_45
źródło

Odpowiedzi:

103

Typ tablicowy jest niejawnie konwertowany na typ wskaźnikowy podczas przekazywania go do funkcji.

Więc,

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

i

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

są równoważne. Więc to, co otrzymujesz, to wartośćsizeof(int*)

Prasoon Saurav
źródło
2
W C ++ możesz przekazać tablicę przez odniesienie do funkcji, ale nie możesz tego zrobić w C.
Prasoon Saurav,
13
Musisz przekazać rozmiar tablicy jako oddzielny parametr. Wtedy rozmiar tablicy byłby sizeof (* p_someArray) * length
Aric TenEyck
14
Mniejszy sizeofoperator nit: zwraca obiekt typu size_t, więc powinieneś wydrukować go za pomocą %zu(C99) lub rzutować na, intjeśli używasz tego %djak powyżej w swoich printfwywołaniach.
Alok Singhal
4
Oświadczenie Aloka jest poprawne. Użycie nieprawidłowego specyfikatora formatu w printf (..) to UB.
Prasoon Saurav
1
@ Chris_45: C nie ma odniesienia, ale w C można przekazać tablicę przez wskaźnik dla całej tablicy, jak w: void PrintSize(int (*p_someArray)[10]). Wewnątrz funkcji można uzyskać dostęp do tablicy za pomocą operatora wyłuskiwania *: sizeof(*p_someArray). Będzie to miało taki sam efekt, jak używanie odwołań w C ++.
AnT
18

Jest to wskaźnik, dlatego powszechną implementacją jest przekazywanie rozmiaru tablicy jako drugiego parametru do funkcji

user240312
źródło
16

Jak powiedzieli inni, tablice rozpadają się na wskaźniki do pierwszego elementu, gdy są używane jako parametry funkcji. Warto również zauważyć, że sizeof nie ocenia wyrażenia i nie wymaga nawiasów, gdy jest używany z wyrażeniem, więc parametr w rzeczywistości nie jest w ogóle używany, więc równie dobrze możesz napisać sizeof z typem, a nie z wartością.

#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] ) );
}
Pete Kirkham
źródło
12

Tak więc będziesz musiał przekazać długość tablicy jako drugi parametr. Kiedy piszesz kod, w którym zarówno deklarujesz tablicę o stałym rozmiarze, a później przekazujesz tę tablicę do funkcji, trudno jest mieć stałą długości tablicy, która pojawia się w kilku miejscach w kodzie ...

K&R na ratunek:

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

Teraz możesz np .:

int a[10];
...
myfunction(a, N_ELEMENTS(a));
SC Madsen
źródło
co się stanie, jeśli rozmiar tablicy nie jest dostępny w czasie kodowania, ale dostępny tylko w czasie wykonywania? Czy istnieje inny sposób obliczenia rozmiaru tablicy bez kodowania jej rozmiaru na stałe?
weefwefwqg3
Pokazana metoda działa tylko wtedy, gdy deklaracja tablicy jest „w widoku”. We wszystkich innych przypadkach musisz ręcznie podać długość tablicy.
SC Madsen
5

Ponieważ tablice rozpadają się na wskaźniki, gdy są przekazywane jako parametry. Tak działa C, chociaż możesz przekazywać „tablice” w C ++ przez referencje i przezwyciężyć ten problem. Zauważ, że możesz przekazywać tablice o różnych rozmiarach do tej funkcji:

 // 10 is superfluous here! You can pass an array of different size!
void PrintSize(int p_someArray[10]);
AraK
źródło
5

W C ++ możesz przekazać tablicę przez referencję w tym właśnie celu:

void foo(int (&array)[10])
{
    std::cout << sizeof(array) << "\n";
}
Alexandre C.
źródło
1
Jak to pomogłoby w przypadku pytania C?
spacer
5

Znalezione zachowanie jest w rzeczywistości wielką brodawką w języku C. Za każdym razem, gdy deklarujesz funkcję, która przyjmuje parametr tablicy, kompilator ignoruje Cię i zmienia parametr na wskaźnik. Zatem wszystkie te deklaracje zachowują się jak pierwsza:

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

a będzie wskaźnikiem do int we wszystkich czterech przypadkach. Jeśli przekażesz tablicę do func, natychmiast rozpadnie się ona na wskaźnik do jej pierwszego elementu. (W systemie 64-bitowym 64-bitowy wskaźnik jest dwa razy większy niż 32-bitowy int, więc współczynnik sizeof zwraca 2.)

Jedynym celem tej reguły jest zachowanie wstecznej kompatybilności z kompilatorami historycznymi, które nie obsługiwały przekazywania wartości zagregowanych jako argumentów funkcji.

Nie oznacza to, że niemożliwe jest przekazanie tablicy do funkcji. Możesz obejść tę brodawkę, osadzając tablicę w strukturze (jest to w zasadzie celem std :: array w C ++ 11):

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

lub przekazując wskaźnik do tablicy:

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

W przypadku, gdy rozmiar tablicy nie jest stałą czasu kompilacji, możesz użyć techniki wskaźnika do tablicy z tablicami o zmiennej długości C99:

void func(int n, const int (*a)[n])
{
printf("%zd\n", sizeof(*a)/sizeof((*a)[0]));  /* prints n */
}
Walter
źródło
3

W języku C nie ma metody określania rozmiaru nieznanej tablicy, dlatego należy przekazać ilość oraz wskaźnik do pierwszego elementu.


źródło
2
Ogólnie rzecz biorąc, należy zawsze przekazywać rozmiar (liczbę elementów) tablicy wraz z tablicą do funkcji, chyba że istnieje inny sposób określenia jej rozmiaru (np. Terminator znaku pustego na końcu char[]tablicy ciągów).
David R Tribble
Co to jest „ nieznana tablica ”?
spacer
3

Nie możesz przekazywać tablic do funkcji.

Jeśli naprawdę chcesz wydrukować rozmiar, możesz przekazać wskaźnik do tablicy, ale nie będzie on wcale ogólny, ponieważ musisz również zdefiniować rozmiar tablicy dla funkcji.

#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));
}
PAN.
źródło
0

Zachowanie jest zgodne z projektem.

Ta sama składnia w deklaracji parametrów funkcji oznacza zupełnie inną rzecz niż w definicji zmiennej lokalnej.

Powód jest opisany w innych odpowiedziach.

Pavel Radzivilovsky
źródło
0

W języku C, gdy przekazujesz tablicę jako argument do funkcji, jest ona automatycznie konwertowana na wskaźnik, tablica przekazująca z jednej funkcji inna funkcja jest nazywana wywołaniem przez odniesienie. To jest powód, dla którego wywołana funkcja otrzymuje tylko wskaźnik wskazujący na pierwszy element funkcji. To jest powód

fun (int a []) jest podobne do fun (int * a);

więc kiedy wypiszesz rozmiar tablicy, wypisze rozmiar pierwszego elementu.

dagrawal
źródło
W C nie ma „ wezwania przez odniesienie ”.
spacer
kiedy wypisujesz rozmiar tablicy, wypisze rozmiar pierwszego elementu. ” nie, wypisze rozmiar wskaźnika.
spacer
-1

W języku programowania 'C' 'sizeof ()' jest operatorem i zwraca rozmiar obiektu w bajtach.Argument operatora 'sizeof ()' musi być lewostronnym typem (liczba całkowita, liczba zmiennoprzecinkowa, struktura, tablica Więc jeśli chcesz poznać rozmiar tablicy w bajtach, możesz to zrobić bardzo prosto, po prostu użyj operatora `` sizeof () '', a jako argumentu użyj nazwy tablicy, na przykład:

#include <stdio.h>

main(){

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

}

Wynik w systemie 32-bitowym będzie wyglądał następująco: Rozmiar n wynosi: 40, ponieważ ineteger w systemie 32 to 4 bajty, w przypadku 64x jest to 8 bajtów, w tym przypadku mamy 10 liczb całkowitych zadeklarowanych w jednej tablicy. int) ”.

Kilka porad:

Jeśli mamy zadeklarowaną tablicę taką jak ta 'int n [] = {1, 2, 3, ... 155 ..};'. Chcemy więc wiedzieć, ile elementów jest przechowywanych w tej tablicy. Użyj tego algorytmu:

sizeof (name_of_the_array) / sizeof (array_type)

Kod: #include

Główny(){

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

}

RichardGeerify
źródło
2
Witamy w StackOverflow i dziękujemy za napisanie odpowiedzi. Niestety, to nie rozwiązuje problemu, który w szczególności dotyczy różnicy między sizeof(n)zmienną lokalną a sizeof(arg)argumentem funkcji, mimo że oba wydają się być typu int[10].
MicroVirus,
-3

Tablice mają tylko luźne rozmiary. W większości przypadków tablica jest wskaźnikiem do pamięci. Rozmiar w deklaracji mówi kompilatorowi tylko, ile pamięci należy przeznaczyć na tablicę - nie jest powiązany z typem, więc sizeof () nie ma nic do roboty.

cokół
źródło
4
Przepraszamy, ta odpowiedź jest myląca. Tablice nie mają też „luźnych rozmiarów” ani nie są „wskaźnikami do pamięci”. Tablice mają bardzo dokładny rozmiar, a miejsca, w których nazwa tablicy oznacza wskaźnik do jej pierwszego elementu, są precyzyjnie określone przez standard C.
Jens