2013-09-06 11 views
7

Moja XCode (domyślnie kompilator powinien być dzyń?) Pokazuje mi na tym kodzie ostrzeżenie:Nie można przekazać adres tablicy typu char * [2], aby funkcjonować biorąc char ***

Incompatible pointer types passing 'char *(*)[2]' to parameter of type 'char ***' (Dzwoniąc func)

void func (char ***arrayref, register size_t size) { 
    /// ... 
} 

int main() 
{ 
    char *array[2] = {"string", "value"}; 
    func(&array, 2); 
} 

natomiast kod ten nie jest problemem (= bez ostrzeżenia):

void func (char **arrayref, register size_t size) { 
    /// ... 
} 

int main() 
{ 
    char *array[2] = {"string", "value"}; 
    func(array, 2); 
} 

Chociaż usuwa ostrzeżenia

func((char***)&array, 2); 

Wciąż nie wiem, dlaczego pierwszy wysyła ostrzeżenie, podczas gdy drugi nie.

Ponadto, gdy pierwsza jest problemem, bym również oczekiwać, że pierwsza emituje ostrzeżenie jak:

Incompatible pointer types passing 'char *[2]' to parameter of type 'char **'

+2

Błąd jest całkiem jasny: 'char * [10]' i 'char **' to dwie różne rzeczy. – LihO

+1

Ponieważ obsada mówi "Wiem, co robię, nie ostrzegaj mnie" – dcaswell

+0

@LihO tak. Ale dlaczego drugi kod NIE wysyła wtedy ostrzeżenia? – bwoebi

Odpowiedz

10

Czas na krótką przypominające semantyki tablicy.

wyjątkiem sytuacji, gdy jest to argument operacji z sizeof lub jednoskładnikowa & operatorów, lub jest Łańcuch znaków używany do inicjalizacji kolejnego tablicę w deklaracji, wyrażenie typu „N elementu tablicy T” zostaną przekształcone (” rozpad ") do wyrażenia typu" wskaźnik do T ", a wartością wyrażenia będzie adres pierwszego elementu w tablicy.

Wyrażenie array w kodzie ma typ "2-elementowa macierz z char *" lub char *[2]. Po przekazać ją jako argument do func, jak w

func(array, 2); 

wyrażenie jest konwertowane do wyrażenia typu „wskaźnik do char *” lub char **, która jest typem czynność spodziewa się, a wartość wyrażenie to adres pierwszego elementu: array == &array[0].Właśnie dlatego nie otrzymujesz ostrzeżenia o drugim fragmencie kodu.

W pierwszym fragmencie tablica jest operandem jednoargumentowego operatora &, dlatego konwersja na typ wskaźnika nie ma miejsca; zamiast tego typem tego wyrażenia jest "wskaźnik do 2-elementowej macierzychar *" lub char *(*)[2], który nie jest zgodny z char **. Adres o wartości jest taki sam (adres pierwszego elementu tablicy jest taki sam jak adres samej tablicy), ale typy nie są zgodne, stąd ostrzeżenie.

Dlaczego to ma znaczenie? Wskaźnik jest po prostu adresem, a cały adres jest taki sam, prawda? Więc nie. Wskaźnik jest abstrakcją adresu, z powiązaną semantyką typu. Wskaźniki różnych typów nie muszą mieć tego samego rozmiaru lub reprezentacji, a arytmetyczna miara zależy od typu wskazanego typu.

Na przykład, jeśli zadeklarować wskaźnik jako char **p;, wówczas wyrażenie p++ będzie przesunąć kursor do punktu do następnego obiektu typu char * lub sizeof (char *) bajtów z aktualnym adresem. Jeśli p jest zadeklarowana jako char *(*p)[2] jednak wyrażenie p++ awansuje p wskazać do następnego dwóch elementów tablicy z char *, czyli 2 * sizeof (char *) bajtów z aktualnym adresem.

+0

Musiałem przeczytać to 3 razy ... Dziękuję. (Myślę, że jest to coś, o czym wielu ludzi nie rozumie ...) – bwoebi

+1

Wiem, że wielu ludzi źle to rozumie, ponieważ semantyka wskaźnika i tablicy nie jest intuicyjna i prawie powszechnie nauczana źle, bez żadnego [tła historycznego] (http: // cm.bell-labs.com/cm/cs/who/dmr/chist.html), aby nadać mu kontekst. To dobrze, że nie uczę C, ponieważ połowa semestru byłaby spędzana na porzucaniu typów. –

+0

Mam ponad połowę z lekturą ... będę kontynuować jutro. Ale jest ________ informacyjny. Dziękuję Ci. (Używam słów "dziękuję" i "ty" zbyt często w połączeniu ...) – bwoebi

5
char *array[2] = {"string", "value"}; 

jest tablicą z 2 elementów char *.

Używanie array jako adresu powoduje wyświetlenie wskaźnika do pierwszego elementu, tj. mi. typu char **.

Używanie wyników do wskaźnika w to samo miejsce, ale typu char *(*)[2] (nie ma pewności, czy pisownia jest prawidłowa).

To nie to samo co char *** - reprezentacja w pamięci jest zupełnie inna.

Aby więcej komunikatów

+++++++++++++++++++++++ 
+ array[0] + array[1] + 
+++++++++++++++++++++++ 

jest matryca.

char ** p1 = array; // is a pointer to the first element, which in turn is a pointer. 
char *(*p2)[2] = &array; // is a pointer to the whole array. Same address, but different type, i. e. sizeof(*p1) != sizeof(*p2) and other differences. 

char ***p3 = &p1; // Now, p1 is a different pointer variable which has an address itself which has type `char ***`. 
+0

Teraz jest to mylące: w jaki sposób powinienem zrobić wskaźnik do tablicy (np. Do wskaźnika wskazującego na pierwszy element)? – bwoebi

+0

@bwoebi Wskaźnik do pierwszego elementu, który jest "char *", byłby 'char **'. Jest to 'i tablica [0]' i może być skrócone do 'tablica', ponieważ nazwa tablicy w większości przypadków ulega degradacji do wskaźnika do pierwszego elementu. – glglgl

+0

Nie, wskaźnik do wskaźnika do pierwszego elementu. – bwoebi

3

Oto przykład tego, jak robić to, co chcesz i zmienić punkty, co do tablicy:

char *array2[] = {"string", "NewValue"}; 

void func0 (char **arrayref, register size_t size) { 
    puts(arrayref[1]); 
} 

void func1 (char ***arrayref, register size_t size) { 
    puts(arrayref[0][1]); 
    *arrayref= (char **) array2; 

} 

int main() 
{ 
    char *array[] = {"string", "value"}; 
    char **foo = array; 
    func0(foo, 2); 
    func1(&foo,2); 
    func0(foo, 2); 
} 
+0

Jest to zasadniczo takie samo, jak napisał @glglgl, ale z bardziej samodzielnym przykładem kodu. (+1) – bwoebi

2

Masz tablicę typu char *[2], tj. Tablicę 2 wskaźników do char. Jest to tablica o stałym rozmiarze z automatycznym czasem przechowywania. Jedyną rzeczą, którą twoja funkcja może zrobić z tego rodzaju tablicą, jest albo użycie jej elementów, albo ich zmiana (nie może zmienić rozmiaru ani zwolnić ... dlatego nie ma sensu próbować umożliwić zmianę tablicy się ~> innymi słowy: tak naprawdę nie potrzebujesz wskaźnika do tego rodzaju tablicy).

Oto prosty przykład:

void func (char *arrayref[2]) { 
    printf("%s", arrayref[1]); 
    arrayref[1] = "new value"; 
} 

int main() { { 
    char *array[2] = {"string", "value"}; 
    func(array); 
    printf(" -> %s", array[1]); 
    return 0; 
} 

lub alternatywnie zmieniając func wziąć tablicę nieokreślonej wielkości co czyni go użytkowej z char *[X] dla każdego X, nie tylko 2 (w tym przypadku ma to sens już zdać rozmiar tablicy jest):

void func (char *arrayref[], size_t size) { 
    if (size > 1) { 
     printf("%s", arrayref[1]); 
     arrayref[1] = "new value"; 
    } 
} 

ze taki czy inny sposób, to program będzie wyjście value -> new value.

Jeśli potrzebujesz funkcji, aby móc zmienić rozmiar tej tablicy lub wpłynąć tablicę sam w jakiś inny sposób, należy rozważyć użycie dynamicznie przydzielonego tablicy i przechodzącej w postaci char**.