2010-10-22 23 views
9

Kiedy przekazujemy element tablicy do funkcji, jest ona traktowana jako normalna zmienna, a wywołana funkcja tworzy kopię faktycznego argumentu i działa na nim. Wszelkie zmiany w formalnych argumentach nie mają wpływu na faktyczne argumenty.Przekazywanie całej tablicy do funkcji

Ale tak nie jest, kiedy mijamy całą tablicę. W tym przypadku funkcja (nazywana funkcją) uzyskuje dostęp do rzeczywistych argumentów, a wszelkie zmiany wprowadzone w formalnych argumentach wpływają na faktyczne argumenty. Dlaczego tak się dzieje?

+2

Wierzę, że moja (szczegółowa) odpowiedź na to inne pytanie http://stackoverflow.com/questions/3613302/passing-array-of-struc tures-to-function-c/3613350 # 3613350 również obejmuje ten. Zasadniczo jest to różnica między problemem macierzy i wskaźników. – kriss

+1

Nie możesz użyć const, jeśli nie chcesz modyfikować zawartości tablicy? – Nyan

Odpowiedz

16

Tablica jest przekazywana jako wskaźnik do elementów.

Jeśli piszę:

void doStuff(int *ptr) 
{ 
    ptr[1] = 5; 
} 

int main() 
{ 
    int arr[] = {0, 1, 2}; 
    doStuff(arr); 
    printf("%d %d %d\n", arr[0], arr[1], arr[2]); 
} 

wyjście będzie "0 5 2". Dzieje się tak, ponieważ C przekazuje cokolwiek jest rzeczywiście parametrem według wartości. Parametr jest wskaźnikiem do int. Tak więc wskaźnik do int jest przekazywany przez wartość. W ten sposób doStuff pobiera kopię wskaźnika do pamięci w ramce stosu głównej. Kiedy usuwa zaznaczenie tego wskaźnika za pomocą ptr[1], podąża za wskaźnikiem do pamięci głównej i modyfikuje tamtejszą tablicę.

C może przechodzić tylko według wartości, ale robi to "płytko". Jeśli poprosisz, aby przekazał int *, przejdzie on przez int *. Kopiuje tylko wartość wskaźnika, a nie wartości cokolwiek wskazuje.

Jeśli chcesz DoStuff dostać własną kopię tablicy, albo zawinąć tablicy w struct jak inni sugerują, lub używać memcpy ręcznie głęboko skopiować tablicę takiego:

void doStuff(int *ptr, int nElems) 
{ 
    int myCopyOfTheArray[nElems]; 
    memcpy(myCopyOfTheArray, ptr, sizeof(int) * nElems); 
    /* do stuff with the local copy */ 
} 

przeciwieństwie do korzystania struct, memcpy podejście działa, jeśli nElems jest znany tylko w czasie wykonywania.

+0

Drugi fragment kodu, czy jest prawidłowy C lub po prostu pseudo C ??? – Nyan

+0

Ok, z jakiego kompilatora korzystasz? po prostu ciekawy. – Nyan

+0

gcc wersja 4.1.2 – AlcubierreDrive

0

Tablica w C może być traktowana jako wskaźnik do pierwszego elementu tablicy. Wskaźnik jest przekazywany przez wartość-wartość (nie można modyfikować przekazanej wartości), ale można go usunąć i zmodyfikować pamięć, na którą wskazuje.

+4

Tablica w C nie jest wskaźnikiem, często * zdegradowana * do wskaźnika. –

+0

@ Matteo, @paxdiablo Naprawiono. – Jonathan

8

Tablic nie można przekazać według wartości w C. Są one zdegradowane do wskaźników. Tak zwana funkcja widzi wskaźnik do tablicy (przekazywany przez referencję) i działa na nim. Jeśli chcesz wykonać kopię, musisz to zrobić albo jawnie, albo umieść tablicę wewnątrz struktury (która może być przekazywana wartościom do funkcji).

0

Bo kiedy przekazujesz całą tablicę do funkcji Przekazujesz wskaźnik do tej tablicy, co oznacza, że ​​nadajesz funkcji miejsce w pamięci, w której znajduje się ta tablica. Więc kiedy zmienisz tablicę w funkcji, zmieniasz także oryginalną tablicę.

5

Kosztowne jest wykonanie kopii całej tablicy. W czasach, w których C był po raz pierwszy stworzony, mógł łatwo wyczerpać zasoby maszyny (w szczególności stos).

Jeśli naprawdę chcesz przekazać tablicę, zawinąć go w strukturze:

struct wrap { int array[100]; }; 

int somefunc(struct wrap large) { ... } 

void anotherfunc(void) 
{ 
    struct wrap a; 
    ...add data to array... 
    printf("%d\n", somefunc(a)); 
} 
2

„mijają odniesienia” jest czerwony śledź w C. Wszystkie argumenty funkcji są „przekazywane przez wartość” tylko . W przypadku tablic podaje się adres pierwszego elementu w postaci wskaźnika. Następnie można użyć tego wskaźnika, aby odnieść się do żądanej lokalizacji odczytu lub zapisu w pamięci.

2

Z online C standard:

6.3.2.1 lwartościami, tablice oraz określenia funkcji
...
3, z wyjątkiem gdy jest operandem sizeof operatora lub jednoargumentowy i operatora, czy Łańcuch znaków inicjalizowania tablicę wyrazem ma typ "tablica typu" jest konwertowany na wyrażenie o typie "wskaźnik do typu", który wskazuje na początkowy element obiektu tablicy i nie jest lwartością. Jeśli obiekt tablicy ma klasę pamięci rejestru, zachowanie jest niezdefiniowane.

Załóżmy następujący fragment kodu:

int a[10]; 
... 
foo(a); 

w wywołaniu foo, typ ekspresji a jest „10 elementu tablicy int”. Jednak ponieważ wyrażenie nie jest operandem ani operatorów sizeof ani &, a ponieważ nie jest literałem łańcuchowym używanym do inicjowania innej tablicy w deklaracji, jej typ jest niejawnie konwertowany ("zaniki") z "10- tablica elementów z int "do" wskaźnika do int ", a jego wartość będzie adresem pierwszego elementu w tablicy (tj. &a[0]).

W związku z tym, co otrzymuje foo jest wskaźnikiem do int, a nie tablicą. Dereferencja lub indeksowanie tego wskaźnika umożliwia zmianę wartości tablicy.

Jest to funkcja języka C; tablice nie są obiektami pierwszej klasy. W rzeczywistości w większości przypadków (włączając w to indeksowanie) tablice wyrażeń zostaną przekonwertowane na typy wskaźników.

Ciągle podkreślić słowo ekspresji do rozróżnienia między rzeczywistym macierzy obiektu (która jest zawsze i zawsze typ tablicy) i każdy odniesienia do tego obiektu w kodzie, który może być przekształcony do wskaźnika rodzaj.

3

Tak naprawdę nie widziałem jeszcze żadnych odpowiedzi, które jeszcze obejmują całe pytanie. Z tego co widzę, to wygląda na to pytanie zadaje coś takiego:

Biorąc pod uwagę następujący kod:

int a[5]; 

foo(a); 
bar(a[0]); 

Dlaczego foo manipulować oryginalne wartości podczas bar otrzymuje jedynie kopię wartości były przekazywane w ?

Dzieje się tak z powodu użycia notacji tablicowej: operator [] odejmuje obiekt, do którego się odwołuje w tablicy i przekazuje wartość w tej lokalizacji.

Więc

int a[5]; 
bar(a[0]); 

jest inna niż:

int* a; 
bar(a); 

Jeśli chcesz przekazać w odniesienia do określonego elementu w tablicy, należy użyć operatora &:

bar(&a[5]); 
Powiązane problemy