Dlaczego tablice nie mogą być przekazywane jako argumenty funkcji w C?

12

Po tym komentarzu próbowałem google dlaczego, ale moje google-fu nie powiodło się.

Komentarz z linku:

[...] Ale ważne jest to, że tablice i wskaźniki to różne rzeczy w C.

Zakładając, że nie używasz żadnych rozszerzeń kompilatora, ogólnie nie możesz przekazać samej tablicy do funkcji, ale możesz przekazać wskaźnik i zindeksować wskaźnik tak, jakby był tablicą.

Skutecznie narzekasz, że wskaźniki nie mają dołączonej długości. Powinieneś narzekać, że tablice nie mogą być przekazywane jako argumenty funkcji lub że tablice degradują się pośrednio do wskaźników.

Florian Margaine
źródło
Nie jestem pewien, czy to odpowiedź, ale częścią typu tablicy jest jej rozmiar, więc uważam, że musisz zdefiniować funkcję dla każdego rozmiaru, który chcesz zaakceptować.
clcto
Czy masz na myśli wskaźniki funkcji ? Proszę wyjaśnić pytanie.
user949300,
2
@ user949300 Nie, z kontekstu komentarza jest całkiem jasne; nie można przekazać tablicy do funkcji, ponieważ staje się ona wskaźnikiem, a on chce wiedzieć, dlaczego tak jest.
Doval
@DocBrown rlemon zaproponował edycję tego.
Florian Margaine,

Odpowiedzi:

18

Moje pierwsze przypuszczenie było spowodowane po prostu wydajnością i oszczędnością pamięci, a także łatwością implementacji kompilatora (szczególnie w przypadku komputerów w czasie, gdy wynaleziono C). Przekazywanie ogromnych tablic „według wartości” wydawało się mieć ogromny wpływ na stos, wymaga operacji kopiowania pełnej tablicy dla każdego wywołania funkcji i prawdopodobnie kompilator musi być mądrzejszy, aby wyprowadzić poprawny kod zestawu (choć ostatni punkt jest dyskusyjny) . Trudniej byłoby również traktować tablice alokowane dynamicznie w taki sam sposób, jak tablice alokowane statycznie (z punktu widzenia składni języka).

EDIT: po przeczytaniu niektórych części z tego linku , myślę, że prawdziwym powodem (i dlatego tablice w elemencie traktowane są jako typy wartości, natomiast podeszwa tablice nie są) jest wsteczna kompatybilność z C poprzednik B . Oto cytat z Dennis Ritchie:

[...} Rozwiązanie stanowiło kluczowy skok w ewolucyjnym łańcuchu między typem BCPL i typem C. Wyeliminowało materializację wskaźnika w pamięci, a zamiast tego spowodowało utworzenie wskaźnika, gdy nazwa tablicy jest wymieniona w wyrażeniu. Reguła, która obowiązuje w dzisiejszym C, jest taka, że ​​wartości typu tablicy są konwertowane, gdy pojawiają się w wyrażeniach, na wskaźniki do pierwszego z obiektów tworzących tablicę.

Dzięki temu wynalazkowi większość istniejącego kodu B nadal działa, pomimo zasadniczej zmiany semantyki języka. [..]

Doktor Brown
źródło
5
A struct Foo { int array[N]; } może być przekazane przez wartość. A ostatni fragment dotyczący traktowania przydziałów dynamicznych i statycznych to samo wydaje się podejrzany (tablica w najściślejszym sensie zawsze zawiera rozmiar, koncepcje ujednolicające dla takich rzeczy jak indeksowanie tablic są wskaźnikami połączonymi z rozkładem z tablicy na wskaźnik), czy mógłbyś to rozwinąć?
@delnan: Myślę, że podane tutaj ogólne zasady są rozsądne. Oczywiście, jeśli zawijasz tablicę w strukturę, określasz swoje zamiary. W ogólnym przypadku prawie zawsze przechodzisz przez odniesienie.
Robert Harvey,
Uważam też, że komentarz, o którym mowa w PO, jest niepotrzebnie pedantyczny. Oczywiście przekazujesz wskaźnik zamiast tablicy według wartości. Jednak równie prawdą jest to, że skutecznie przekazujesz tablicę przez referencję. Jeśli sprzeciwi się temu, że nie ma dołączonej długości, łatwo to również przekazać.
Robert Harvey,
@RobertHarvey To wciąż asymetria w systemie typów: wszystko jest przekazywane przez wartość, z wyjątkiem typów tablic (nawet jeśli tablice, które są częścią typu struktury przekazywane przez wartość), a nawet używa dokładnie tej samej notacji (zarówno w witrynie wywołania oraz w podpisie funkcji).
@delnan: Dlaczego to jest tak istotne, że nie musisz o tym pamiętać?
Robert Harvey
9

Minikomputer PDP z zaledwie 8 kB pamięci nie może przydzielić bardzo dużego stosu. Tak więc na takim komputerze należy zachować ostrożność przy projektowaniu języka (lub ewolucji), aby móc zminimalizować to, co musi znaleźć się na stosie w celu spodziewanego typowego użycia wywołań podprogramów. C jest nadal używany do programowania systemów o bardzo ograniczonej pamięci (kilka kB), więc kompromis jest zwykle dobry.

W architekturze procesora, która ma bardzo mało rejestrów, przekazywanie dowolnej tablicy przez wskaźnik zamiast według wartości częściej pozwala na wykorzystanie rejestru jako optymalizacji wywołania podprogramu.

hotpaw2
źródło
2
Mam kilka płyt, które mają 256 bajtów RAM dla danych i 2K EEPROM dla kodu. Nie chcesz tam tworzyć kopii tablicy.
Jerry Jeremiasz