2011-06-24 83 views
9

người.Ý nghĩa của 'char (* p) [5];' là gì?

Tôi đang cố gắng để nắm bắt sự khác nhau giữa ba tờ khai sau đây:

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

Tôi đang cố gắng để tìm thấy điều này bằng cách thực hiện một số xét nghiệm, bởi vì tất cả các hướng dẫn của tờ khai đọc và các công cụ như điều đó không giúp tôi cho đến nay. Tôi đã viết chương trình nhỏ này và nó không hoạt động (tôi đã thử sử dụng các loại khai báo thứ ba khác và tôi đã hết các lựa chọn):

#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;                
} 

Tác phẩm đầu tiên và thứ hai được rồi, và tôi đã hiểu những gì họ làm. Ý nghĩa của tuyên bố thứ ba và cách chính xác để sử dụng nó là gì?

Cảm ơn bạn!

+0

Khai báo giải mã là phần yêu thích nhất của tôi trong C và C++. –

Trả lời

12

Thứ ba là con trỏ thành một mảng gồm 5 ký tự, trong khi thứ hai là một mảng trong tổng số 5 con trỏ đến char.

Hãy tưởng tượng nó như thế này:

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

Trong khi cái thứ hai trông như thế:

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

Đây là một trong những trường hợp tìm hiểu sự khác biệt giữa con trỏ và mảng là rất quan trọng. Một mảng là một đối tượng có kích thước là kích thước của mỗi phần tử của nó tính lần, trong khi một con trỏ chỉ là một địa chỉ.

Trong trường hợp thứ hai, sizeof(p) sẽ mang lại 5 * the_size_of_a_pointer.

Trong trường hợp thứ ba, sizeof(p) sẽ mang lại the_size_of_a_pointer, thường là 4 hoặc 8, tùy thuộc vào máy mục tiêu.

+9

[Quy tắc xoắn ốc theo chiều kim đồng hồ] (http://c-faq.com/decl/spiral.anderson.html) là một điều hữu ích để biết khi nào bạn đang mắc kẹt với 'C'. –

+0

Tôi đã cố gắng sử dụng p3 phân bổ 5 ký tự và chỉ p3 cho những ký tự đó và nó không hoạt động. Nó không hoạt động như bạn đã nói ... Bạn có nhớ đăng một số mã ví dụ hay cái gì đó rõ ràng hơn không? – jpmelos

+0

Trong trường hợp thứ ba, sizeof (p3) không sinh lời những gì bạn nói, nó có năng suất 8, đó là kích thước của một con trỏ trong máy của tôi. – jpmelos

4

Đó là con trỏ tới một mảng ký tự. Nó được giải thích trong C FAQ. Trong tương lai, khi bạn không hiểu tuyên bố, hãy sử dụng cdecl hoặc cdecl(1).

+0

Cảm ơn bạn rất nhiều, liên kết của bạn đến C FAQ đã giúp tôi hiểu rõ chủ đề này. – jpmelos

3
char (*p3)[5]; 

thực sự khai báo con trỏ tới mảng 5 char. Nó có nghĩa là nó sẽ trỏ đến một con trỏ trỏ đến một mảng của 5 char. Nó không phân bổ 5 của bất cứ điều gì, chỉ cần một con trỏ, và nó nên trỏ đến một cái gì đó mà trỏ đến 5 char.

Vì vậy, việc sử dụng đúng là:

#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;                
} 

Nếu bạn muốn truy cập vào một vị trí duy nhất của chuỗi đó p1 sử dụng p3 truy cập vào nó, bạn phải làm (*p3)[2], ví dụ. Nó sẽ truy cập vào vị trí thứ ba. Đó là bởi vì trước tiên bạn phải chuyển đổi p3 thành p1 (các tham chiếu (*p3) trước khi thực hiện vị trí arithmetics) và sau đó truy cập vào vị trí thứ ba của địa chỉ đó (được trỏ bởi con trỏ bị bỏ qua).

+0

Tuyệt vời, bạn tóm tắt câu trả lời của tôi tốt hơn :-) –

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ú pháp khai báo được xây dựng xung quanh các loại biểu thức, không phải đối tượng.Ý tưởng là hình thức tuyên bố phù hợp với hình thức của biểu thức như nó sẽ xuất hiện trong mã.

Trong các trường hợp trên, chúng tôi đang xử lý các mảng. Trong trường hợp đầu tiên, p là một mảng của char; để truy cập vào một giá trị nhân vật đặc biệt, chúng tôi sẽ chỉ đơn giản là chỉ số vào mảng:

val = p[i]; 

Kiểu của biểu thức p[i]char, do đó việc kê khai của pchar p[5].

Trong trường hợp tiếp theo, p là một mảng con trỏ trỏ tới char; để truy cập giá trị, chúng tôi chỉ số vào mảng và dereference kết quả:

val = *p[i]; 

khai thác Postfix như [] có một ưu tiên cao hơn các nhà khai thác unary như *, vì vậy ở trên được phân tách như

val = *(p[i]); 

Các loại biểu thức *p[i]char, do đó việc khai báo pchar *p[5].

Trong trường hợp cuối cùng, p là một con trỏ đến một mảng của char, vì vậy để truy cập một giá trị char chúng ta phải dereference con trỏ mảng và sau đó subscript vào kết quả:

val = (*p)[i]; 

[] có cao hơn ưu tiên hơn unsemble *, chúng tôi phải sử dụng dấu ngoặc đơn để nhóm rõ ràng * với p (ngược với p[i]). Một lần nữa, loại biểu thức (*p)[i]char, do đó, khai báo pchar (*p)[5].

EDIT

Con trỏ tới mảng hiển thị trong bối cảnh sau đây:

  1. Bạn đang dùng một cách rõ ràng địa chỉ của một mảng N-chiều:

    int x[10]; 
    int (*px)[10] = &x; 
    
    Lưu ý rằng trong khi các biểu thức x&x có cùng giá trị (địa chỉ của phần tử đầu tiên của mảng), chúng có các loại khác nhau ()và int (*)[10]).

  2. Bạn đang tự động phân bổ một đối tượng mảng kiểu:

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

  3. Một N-chiều biểu thức mảng "phân rã" vào một con trỏ đến một (N-1) -dimension mảng:

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

+0

+1. Lời giải thích rất tốt! Cảm ơn bạn, thực sự làm cho nó rõ ràng như thế nào để suy nghĩ về tờ khai! – jpmelos

+0

Trong bản chỉnh sửa của bạn, ngữ cảnh đầu tiên, bạn nói 'x' có kiểu' int * 'và' & x' có kiểu 'int (*) [10]'. Bạn thực sự có nghĩa là 'x' có kiểu' int [10] ', tôi đoán vậy? – jpmelos

+1

@jpmelos: Vì nó không phải là toán hạng cho 'sizeof' hoặc' & ',' x' sẽ phân rã thành 'int *'. –