2009-05-13 40 views
401

phần chênh lệch giữa tờ khai sau là gì:C con trỏ đến mảng/mảng các con trỏ định hướng

int* arr1[8]; 
int (*arr2)[8]; 
int *(arr3[8]); 

quy tắc chung để hiểu tờ khai phức tạp hơn là gì?

+45

Đây là một bài viết tuyệt vời về việc đọc các khai báo phức tạp trong C: http://unixwiz.net/techtips/reading-cdecl.html – jesper

+0

@jesper Thật không may, các 'const' và' volatile' qualifiers, cả hai đều quan trọng và khôn lanh, bị thiếu trong bài viết đó. –

Trả lời

380
int* arr[8]; // An array of int pointers. 
int (*arr)[8]; // A pointer to an array of integers 

Kiểu thứ ba giống như giá trị đầu tiên.

Quy tắc chung là operator precedence. Nó có thể phức tạp hơn nhiều khi các con trỏ hàm xuất hiện trong bức ảnh.

+4

Vì vậy, đối với hệ thống 32 bit: int * arr [8];/* 8x4 byte được cấp phát, cho mỗi con trỏ */ int (* arr) [8];/* 4 byte được cấp phát, chỉ có một con trỏ */ – George

+10

Không. int * arr [8]: 8x4 byte được cấp phát * tổng *, 4 byte cho mỗi con trỏ. int (* arr) [8] là đúng, 4 byte. –

+2

Tôi nên đọc lại những gì tôi đã viết. Tôi có nghĩa là 4 cho mỗi con trỏ. Cảm ơn đã giúp đỡ! – George

114

Tôi không biết liệu nó có tên chính thức hay không, nhưng tôi gọi nó là Điều trái phải (TM).

Bắt đầu từ biến, sau đó đi sang phải, trái và phải ... v.v.

int* arr1[8]; 

arr1 là một mảng gồm 8 con trỏ đến số nguyên.

int (*arr2)[8]; 

arr2 là một con trỏ (ngoặc chặn bên phải bên trái) để một mảng của 8 số nguyên.

int *(arr3[8]); 

arr3 là một mảng gồm 8 con trỏ đến số nguyên.

Điều này sẽ giúp bạn khai báo phức tạp.

+16

Tôi đã nghe nó được gọi bằng tên" Quy tắc Xoắn ốc ", có thể tìm thấy ở đây (http: //www.c-faq .com/decl/spiral.anderson.html). – fouric

+5

@InkBlend: Quy tắc xoắn ốc khác với [quy tắc trái phải] (http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html). [Lỗi cũ] (http://stackoverflow.com/questions/16260417/the-spiral-rule-about-declarations-when-is-it-in-error) trong các trường hợp như 'int * a [] [10] 'trong khi sau này thành công. – legends2k

+1

@dogeen Tôi nghĩ rằng thuật ngữ đó có liên quan đến Bjarne Stroustrup :) –

244

Sử dụng các chương trình cdecl, theo đề nghị của K & R.

$ cdecl 
Type `help' or `?' for help 
cdecl> explain int* arr1[8]; 
declare arr1 as array 8 of pointer to int 
cdecl> explain int (*arr2)[8] 
declare arr2 as pointer to array 8 of int 
cdecl> explain int *(arr3[8]) 
declare arr3 as array 8 of pointer to int 
cdecl> 

Nó hoạt động theo cách khác nữa.

cdecl> declare x as pointer to function(void) returning pointer to float 
float *(*x)(void) 
14

Câu trả lời cho cả hai cuối cùng cũng có thể được trích từ quy tắc vàng trong C:

Tuyên bố sau sử dụng.

int (*arr2)[8];

gì sẽ xảy ra nếu bạn dereference arr2? Bạn nhận được một mảng gồm 8 số nguyên.

int *(arr3[8]);

Điều gì xảy ra nếu bạn lấy phần tử từ arr3? Bạn nhận được một con trỏ đến một số nguyên.

Điều này cũng giúp khi xử lý các con trỏ đến các hàm. Để lấy một ví dụ sigjuice của:

float *(*x)(void)

gì xảy ra khi bạn dereference x? Bạn nhận được một hàm mà bạn có thể gọi mà không có đối số. Điều gì sẽ xảy ra khi bạn gọi nó? Nó sẽ trả về một con trỏ tới một phao.

Ưu tiên toán tử luôn là điều khó khăn. Tuy nhiên, việc sử dụng dấu ngoặc đơn cũng có thể gây nhầm lẫn vì khai báo sau khi sử dụng. Ít nhất, với tôi, trực giác arr2 trông giống như một mảng của 8 con trỏ để ints, nhưng nó thực sự là cách khác xung quanh. Chỉ cần một số nhận được sử dụng để. Lý do đủ để luôn luôn thêm một comment cho những tờ khai, nếu bạn hỏi tôi :)

chỉnh sửa: Ví dụ

Bằng cách này, tôi chỉ tình cờ tình huống sau đây: một chức năng mà có một ma trận tĩnh và sử dụng số học con trỏ để xem con trỏ hàng có nằm ngoài giới hạn hay không. Ví dụ:

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

#define NUM_ELEM(ar) (sizeof(ar)/sizeof((ar)[0])) 

int * 
put_off(const int newrow[2]) 
{ 
    static int mymatrix[3][2]; 
    static int (*rowp)[2] = mymatrix; 
    int (* const border)[] = mymatrix + NUM_ELEM(mymatrix); 

    memcpy(rowp, newrow, sizeof(*rowp)); 
    rowp += 1; 
    if (rowp == border) { 
     rowp = mymatrix; 
    } 

    return *rowp; 
} 

int 
main(int argc, char *argv[]) 
{ 
    int i = 0; 
    int row[2] = {0, 1}; 
    int *rout; 

    for (i = 0; i < 6; i++) { 
     row[0] = i; 
     row[1] += i; 
     rout = put_off(row); 
     printf("%d (%p): [%d, %d]\n", i, (void *) rout, rout[0], rout[1]); 
    } 

    return 0; 
} 

Output:

0 (0x804a02c): [0, 0] 
1 (0x804a034): [0, 0] 
2 (0x804a024): [0, 1] 
3 (0x804a02c): [1, 2] 
4 (0x804a034): [2, 4] 
5 (0x804a024): [3, 7]

Lưu ý rằng giá trị của biên giới bao giờ thay đổi, vì vậy trình biên dịch có thể tối ưu hóa mà đi. Điều này khác với những gì ban đầu bạn có thể muốn sử dụng: const int (*border)[3]: khai báo đường viền dưới dạng con trỏ tới một mảng gồm 3 số nguyên sẽ không thay đổi giá trị miễn là biến tồn tại. Tuy nhiên, con trỏ đó có thể được trỏ đến bất kỳ mảng nào khác như vậy bất cứ lúc nào. Chúng tôi muốn loại hành vi đó cho đối số, thay vào đó (vì hàm này không thay đổi bất kỳ số nguyên nào trong số các số nguyên đó). Tuyên bố sau sử dụng.

(ps: cảm thấy tự do để cải thiện mẫu này)

5
typedef int (*PointerToIntArray)[]; 
typedef int *ArrayOfIntPointers[]; 
23
int *a[4]; // Array of 4 pointers to int 

int (*a)[4]; //a is a pointer to an integer array of size 4 

int (*a[8])[5]; //a is an array of pointers to integer array of size 5 
+0

Không phải là thứ ba là: a là một mảng con trỏ đến mảng nguyên của kích thước 8? Tôi có nghĩa là mỗi mảng số nguyên sẽ có kích thước 8 phải không? – Rushil

+2

@Rushil: không, chỉ số cuối cùng ('[5]') đại diện cho thứ nguyên bên trong. Điều này có nghĩa là '(* a [8])' là thứ nguyên đầu tiên, và do đó là biểu diễn bên ngoài của mảng. Mỗi phần tử trong 'a' ** trỏ tới ** là một mảng số nguyên khác nhau có kích thước 5. – zeboidlund

+0

Cảm ơn phần tử thứ ba. Tôi đang tìm cách viết mảng con trỏ tới mảng. – Deqing

2

Tôi nghĩ chúng ta có thể sử dụng các quy tắc đơn giản ..

example int * (*ptr)()[]; 
start from ptr 

"ptr là một con trỏ đến" đi về phía bên phải .. ")" bây giờ đi bên trái của nó một "(" đi ra bên phải "()" để "đến một chức năng mà không có đối số" đi bên trái "và trả về một con trỏ "đi đúng" tới một mảng "đi bên trái" các số nguyên "

-7

Trong con trỏ tới số nguyên nếu con trỏ được tăng lên thì nó sẽ đi tới số nguyên tiếp theo.

trong mảng của con trỏ nếu con trỏ được tăng lên nó nhảy vào mảng tiếp theo

+0

"* trong mảng con trỏ nếu con trỏ được tăng lên, nó nhảy tới mảng tiếp theo *" điều này là sai. – alk

2

Như một quy tắc của ngón tay cái, các nhà khai thác unary đúng (như [], (), vv) mất ưu tiên hơn những người còn lại. Vì vậy, int *(*ptr)()[]; sẽ là một con trỏ trỏ đến một hàm trả về một mảng con trỏ tới int (có được toán tử ngay khi bạn có thể thoát khỏi dấu ngoặc đơn)

2

Đây là cách tôi giải thích nó:

int *something[n]; 

lưu ý ưu tiên: toán tử chỉ số mảng ('[]') có mức ưu tiên cao hơn toán tử dereference ('*').

Vì vậy, ở đây chúng tôi sẽ áp dụng '[]' trước khi '*', làm cho báo cáo kết quả tương đương với:

int *(something[i]); 

lưu ý về cách một tuyên bố có ý nghĩa: int num phương tiện (num) là một (int), int *ptr hoặc int (*ptr) có nghĩa là, (giá trị tại ptr) là một (int), mà làm cho ptr một con trỏ đến int.

Điều này có thể được đọc là, (giá trị của (giá trị tại chỉ số thứ i của thứ gì đó)) là một số nguyên. Vì vậy, (giá trị tại chỉ số thứ i của một cái gì đó) là một (con trỏ nguyên), mà làm cho một cái gì đó một mảng con trỏ số nguyên.

Trong một giây,

int (*something)[n]; 

Để thực hiện ý nghĩa của tuyên bố này, bạn phải làm quen với thực tế này:

lưu ý về đại diện con trỏ của mảng: somethingElse [i] là tương đương với * (somethingElse + i)

Vì vậy, thay thế một cái gì đó với (* cái gì đó), chúng tôi nhận * (* something + i), là số nguyên theo khai báo . Vì vậy, (* một cái gì đó) cho chúng ta một mảng, mà làm cho một cái gì đó tương đương với (con trỏ đến một mảng).

0

Tôi đoán tuyên bố thứ hai gây nhầm lẫn với nhiều người. Đây là cách dễ hiểu.

Cho phép có một dãy số nguyên, tức là int B[8].

Hãy cũng có biến A trỏ đến B. Bây giờ, giá trị tại A là B, tức là (*A) == B. Do đó Một điểm đến một mảng các số nguyên. Trong câu hỏi của bạn, arr tương tự như A.

Tương tự, trong int* (*C) [8], C là một con trỏ tới một mảng con trỏ đến số nguyên.

Các vấn đề liên quan