2011-06-24 16 views
9

osób.Co oznacza "char (* p) [5];"?

Próbuję uchwycić różnice pomiędzy tymi trzema deklaracje:

char p[5]; 
char *p[5]; 
char (*p)[5]; 

próbuję się tego dowiedzieć, wykonując kilka testów, ponieważ każdy przewodnik deklaracji czytania i takie, które nie pomogło do tej pory. Napisałem ten mały program i nie działa (próbowałem inne rodzaje wykorzystania trzeciej deklaracji i mam zabrakło opcji):

#include <stdio.h>                
#include <string.h>                
#include <stdlib.h>                

int main(void) {                 
     char p1[5];                
     char *p2[5];                
     char (*p3)[5];               

     strcpy(p1, "dead");              

     p2[0] = (char *) malloc(5 * sizeof(char));        
     strcpy(p2[0], "beef");             

     p3[0] = (char *) malloc(5 * sizeof(char));        
     strcpy(p3[0], "char");             

     printf("p1 = %s\np2[0] = %s\np3[0] = %s\n", p1, p2[0], p3[0]);   

     return 0;                
} 

Pierwsze i drugie działa dobrze, a ja rozumieli, co robią. Jakie jest znaczenie trzeciej deklaracji i prawidłowy sposób jej użycia?

Dziękujemy!

+0

Deklaracje deklaracji to moja najmniej ulubiona część C i C++. –

Odpowiedz

12

trzeci jest wskaźnik do tablicy 5 znaków, podczas gdy drugi jest Tablica 5 wskaźniki char.

Wyobraź to tak:

________   _____________________ 
|0x7777| -------> | H | e | l | l | o | 
|______|   |_0_|_1_|_2_|_3_|_4_| 
    p   ^
        | 
        0x7777 

natomiast druga wygląda tak:

"abc" "def" "ghi" "jkl" "mno" 
    ^ ^ ^ ^ ^
    |  |  |  |  | 
____________________________________ 
|0x9999|0x7777|0x3333|0x2222|0x6666| 
|___0__|___1__|___2__|___3__|___4__| 
        p 

Jest to jeden z przypadków, w których zrozumienie różnicy między wskaźnikami i tablic ma kluczowe znaczenie. Tablica jest obiektem, którego rozmiar jest wielkością każdego z jego elementów pomnożoną przez liczbę, podczas gdy wskaźnik jest jedynie adresem.

W drugim przypadku sizeof(p) przyniesie 5 * the_size_of_a_pointer.

W trzecim przypadku sizeof(p) przyniesie the_size_of_a_pointer, co zwykle jest 4 lub 8, w zależności od maszyny docelowej.

+9

[Zgodnie z regułami spiralnymi] (http://c-faq.com/decl/spiral.anderson.html) to przydatna rzecz do zrozumienia, kiedy mieszasz się z 'C'. –

+0

Próbowałem użyć p3 przydzielając 5 znaków i wskazując p3 na te i to nie działało. To nie działa tak jak powiedziałeś ... Czy mógłbyś napisać jakiś przykładowy kod lub coś wyraźniejszego? – jpmelos

+0

W trzecim przypadku sizeof (p3) nie daje tego, co powiedziałeś, to daje 8, co jest wielkością wskaźnika w mojej maszynie. – jpmelos

4

Jest to wskaźnik do tablicy znaków. Jest wyjaśnione w C FAQ. W przyszłości, jeśli nie zrozumiesz deklaracji, użyj cdecl lub cdecl(1).

+0

Dziękuję bardzo, twój link do C FAQ pomógł mi bardzo zrozumieć ten temat. – jpmelos

3
char (*p3)[5]; 

faktycznie deklaruje wskaźnik do tablicy 5 char. Oznacza to, że powinien wskazywać wskaźnik wskazujący na tablicę 5 char. Nie przydziela 5 nic, tylko wskaźnik, i powinien wskazywać na coś, co wskazuje na 5 char.

więc prawidłowe stosowanie jest:

#include <stdio.h>                
#include <string.h>                
#include <stdlib.h>                

int main(void) {                 
     char p1[5];                
     char *p2[5];                
     char (*p3)[5];               

     strcpy(p1, "dead");              

     p2[0] = (char *) malloc(5 * sizeof(char));        
     strcpy(p2[0], "beef");             

     p3 = &p1;                

     printf("p1 = %s\np2[0] = %s\np3 = %s\n", p1, p2[0], *p3);    

     return 0;                
} 

Jeśli chcesz uzyskać dostęp do pojedynczego stanowiska tego łańcucha p1 użyciem P3 do niego dostęp, trzeba zrobić (*p3)[2], na przykład. Będzie miał dostęp do trzeciej pozycji. Dzieje się tak dlatego, że najpierw musisz przekształcić p3 w p1 (wyrejestrowanie z (*p3) przed wykonaniem arytmetyki pozycji), a następnie uzyskać dostęp do trzeciej pozycji tego adresu (wskazanej przez wskaźnik dereferencji).

+0

Świetnie, podsumowałeś moją odpowiedź lepiej :-) –

4
char p[5];  // p is a 5-element array of char 
char *p[5];  // p is a 5-element array of pointer to char 
char (*p)[5]; // p is a pointer to a 5-element array of char 

C składnia deklaracja jest zbudowany wokół rodzaje wyrażeń, a nie obiektów.Chodzi o to, że forma deklaracji pasuje do formy wyrażenia, tak jak w kodzie.

W powyższych przypadkach mamy do czynienia z tablicami. W pierwszym przypadku p jest tablicą char; aby uzyskać dostęp do określonej wartości znaków, my po prostu wskaźnik do tablicy:

val = p[i]; 

typ ekspresji p[i] jest char, więc deklaracja p jest char p[5].

W następnym przypadku p jest tablicą wskaźników do char; dostęp do wartości, to indeks w tablicy i nieprawidłowego wyniku:

val = *p[i]; 

Postfix operatorów jak [] mają wyższy priorytet niż pojedynczych, operatorami takimi jak *, powyższe jest analizowany jako

val = *(p[i]); 

The typ wyrażenia *p[i] to char, zatem deklaracja p to char *p[5].

W ostatnim przypadku p jest wskaźnik do tablicy odbarwiającego, więc dostęp do char mamy do nieprawidłowego wskaźnika tablicy i indeks dolny w rezultacie:

val = (*p)[i]; 

Ponieważ [] ma wyższą pierwszeństwo niż unitarne *, musimy użyć nawiasów, aby jawnie zgrupować * z p (w przeciwieństwie do p[i]). Ponownie, typ wyrażenia (*p)[i] jest char, więc deklaracja p jest char (*p)[5].

EDIT

Wskaźniki do tablic pojawiają się w następujących kontekstach:

  1. Ty wyraźnie biorąc adres N-wymiarowej tablicy:

    int x[10]; 
    int (*px)[10] = &x; 
    
    pamiętać, że wyrażenia x i &x przynosi tę samą wartość (adres pierwszego elementu tablicy), mają różne typy (int * vs int (*)[10]).

  2. Ty dynamiczne przydzielanie obiekt array-type:

    int (*px)[10] = malloc(sizeof *px); 
    

  3. n-wymiarowej tablicy wyraz "rozpadów" na wskaźnik do (n-1) -dimension tablicy:

    int x[10][20]; 
    foo(x); 
    ... 
    void foo(int (*px)[20]){...} 
    

+0

+1. Bardzo dobre wytłumaczenie! Dzięki, naprawdę wyjaśniłeś, jak myśleć o deklaracjach! – jpmelos

+0

W edytorze, pierwszym kontekście, mówisz 'x' ma typ' int * 'oraz' & x' ma typ 'int (*) [10]'. Masz na myśli, że 'x' ma typ' int [10] ', tak myślę? – jpmelos

+1

@jpmelos: Ponieważ nie jest operandem dla 'sizeof' lub' & ',' x' rozpadnie się na 'int *'. –