C con trỏ tới mảng / mảng của con trỏ định hướng


463

Sự khác biệt giữa các tuyên bố sau đây là gì:

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

Quy tắc chung để hiểu các khai báo phức tạp hơn là gì?


54
Đâ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: unixwiz.net/techtips/reading-cdecl.html
jesper

@jesper Thật không may, constvolatilevòng loại, cả hai đều quan trọng và khó khăn, bị thiếu trong bài viết đó.
không phải người dùng

@ không phải là người dùng không liên quan đến câu hỏi này. Nhận xét của bạn không liên quan. Xin hãy kiềm chế.
dùng64742

Câu trả lời:


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

Cái thứ ba giống như cái thứ nhất.

Nguyên tắc chung là ưu tiên toán tử . Nó thậm chí có thể phức tạp hơn nhiều khi các con trỏ hàm đi vào hình ảnh.


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

10
Không. int * Array [8]: Tổng cộng 8 byte được phân bổ , 4 byte cho mỗi con trỏ. int (* Array) [8] là đúng, 4 byte.
Mehrdad Afshari

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

4
Lý do mà cái đầu tiên giống với cái cuối cùng là vì nó luôn được phép bọc dấu ngoặc đơn xung quanh người khai báo. P [N] là một công cụ khai báo mảng. P (....) là một công cụ khai báo hàm và * P là một công cụ khai báo con trỏ. Vì vậy, mọi thứ sau đây đều giống như không có dấu ngoặc đơn (ngoại trừ một trong các hàm '"()": int (((* p))); void ((g (void))); int * (a [1]); void (* (p ())).
Johannes Schaub - litb

2
Cũng được thực hiện trong lời giải thích của bạn. Để tham khảo chuyên sâu về quyền ưu tiên và tính kết hợp của các nhà khai thác, hãy tham khảo trang 53 của Ngôn ngữ lập trình C (phiên bản thứ hai ANSI C) của Brian Kernighan và Dennis Ritchie. Các toán tử ( ) [ ] liên kết từ trái sang phải và có độ ưu tiên cao hơn *đọc int* arr[8]thành một mảng có kích thước 8 trong đó mỗi phần tử trỏ đến một int và int (*arr)[8]như một con trỏ tới một mảng có kích thước 8 chứa các số nguyên
Mushy

267

Sử dụng chương trình cdecl , theo đề xuất 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 quá.

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

@ankii Hầu hết các bản phân phối Linux nên có một gói. Bạn cũng có thể xây dựng nhị phân của riêng bạn.
sigjuice

ah xin lỗi vì đã không đề cập, macOS ở đây. sẽ thấy nếu có, nếu không trang web cũng tốt. ^^ cảm ơn vì đã cho tôi biết về điều này .. Hãy gắn cờ NLN.
ankii

2
@ankii Bạn có thể cài đặt từ Homebrew (và có thể cả MacPorts?). Nếu những thứ đó không hợp với sở thích của bạn, thì việc xây dựng của riêng bạn từ liên kết Github ở phía trên bên phải của cdecl.org là điều không quan trọng (tôi chỉ xây dựng nó trên macOS Mojave). Sau đó, chỉ cần sao chép nhị phân cdecl vào PATH của bạn. Tôi khuyên bạn nên $ PATH / bin, vì không cần phải liên quan đến root trong một cái gì đó đơn giản như thế này.
sigjuice

Oh đã không đọc đoạn nhỏ về cài đặt trong readme. chỉ một số lệnh và cờ để xử lý các phụ thuộc .. Được cài đặt bằng brew. :)
ankii

1
Khi tôi lần đầu tiên đọc điều này, tôi đã nghĩ: "Tôi sẽ không bao giờ xuống cấp độ này". Ngày hôm sau, tôi đã tải nó.
Ciro Santilli 郝海东 冠状 病 事件

126

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

Bắt đầu tại biến, sau đó rẽ phải, trái và phải ... và cứ thế.

int* arr1[8];

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

int (*arr2)[8];

arr2 là một con trỏ (dấu ngoặc đơn bên phải) cho một mảng gồm 8 số nguyên.

int *(arr3[8]);

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

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


19
Tôi đã nghe nói nó được gọi bằng tên của "Quy tắc xoắn ốc", có thể được tìm thấy ở đây .
bốn giờ

6
@InkBlend: Quy tắc xoắn ốc khác với quy tắc phải trái . Cái trước thất bại trong trường hợp như int *a[][10]cái sau thành công.
huyền thoại2k

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

1
Như InkBlend và legends2k đã nói, đây là Quy tắc xoắn ốc phức tạp hơn và không hoạt động trong mọi trường hợp, vì vậy không có lý do gì để sử dụng nó.
kotlomoy

Đừng quên sự kết hợp từ phải sang phải của ( ) [ ]và từ phải sang trái của* &
Mushy

28
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 

Không phải cái thứ 3 phải là: a là một mảng các con trỏ tới mảng số nguyên có kích thước 8? Ý tôi là mỗi mảng số nguyên sẽ có kích thước 8 phải không?
Rushil Paul

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

Cảm ơn vì thứ ba. Tôi đang tìm cách viết mảng con trỏ vào mảng.
De Khánh

15

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

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

int (*arr2)[8];

Điều gì xảy ra nếu bạn thiếu ý thức 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 một yếu 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 chức năng. Lấy ví dụ của sigjuice:

float *(*x)(void )

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

Ưu tiên người vận hành luôn luôn là khó khăn, mặc dù. Tuy nhiên, sử dụng dấu ngoặc đơn thực sự 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 arr2trông giống như một mảng 8 con trỏ đến ints, nhưng nó thực sự là cách khác. Chỉ cần một số làm quen với. Đủ lý do để luôn thêm một bình luận cho những tuyên bố này, nếu bạn hỏi tôi :)

chỉnh sửa: ví dụ

Nhân tiện, tôi chỉ tình cờ gặp tình huống sau: một hàm có 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 không. Thí 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 &lt; 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;
}

Đầu ra:

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 đường viền không bao giờ thay đổi, vì vậy trình biên dịch có thể tối ưu hóa đ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. Thay vào đó, chúng tôi muốn loại hành vi đó cho đối số (vì hàm này không thay đổi bất kỳ số nguyên nào trong số đó). Tuyên bố sau khi sử dụng.

(ps: cứ thoải mái cải thiện mẫu này!)



3

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


Đó là sự thật, nhưng nó cũng bất hợp pháp. Bạn không thể có một hàm trả về một mảng. Tôi đã thử và nhận được điều này: error: ‘foo’ declared as function returning an array int foo(int arr_2[5][5])[5];theo GCC 8 với$ gcc -std=c11 -pedantic-errors test.c
Cacahuete Frito

1
Lý do trình biên dịch đưa ra lỗi đó là vì nó đang diễn giải hàm là trả về một mảng, như cách giải thích chính xác của các trạng thái quy tắc ưu tiên. Nó là bất hợp pháp như một tuyên bố, nhưng tuyên bố pháp lý int *(*ptr)();cho phép một biểu thức như p()[3](hoặc (*p)()[3]) được sử dụng sau này.
Luis Colorado

Ok, nếu tôi hiểu nó, bạn đang nói về việc tạo một hàm trả về một con trỏ đến phần tử đầu tiên của một mảng (không phải là một mảng), và sau đó sử dụng hàm đó như thể nó đang trả về một mảng? Ý tưởng thú vị. Tôi sẽ thử nó. int *foo(int arr_2[5][5]) { return &(arr_2[2][0]); }và gọi nó như thế này: foo(arr)[4];cái nào nên chứa arr[2][4], phải không?
Cacahuete Frito

đúng ... nhưng bạn cũng đúng, và tuyên bố là bất hợp pháp. :)
Luis Colorado

2

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

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

" ptrlà một con trỏ để" đi về phía bên phải ..its ")" bây giờ đi bên trái một "(" đi ra bên phải "()" vì vậy "đến một hàm không có đối số" đi bên trái "và trả về một con trỏ" đi bên phải "đến một mảng" bên trái "số nguyên"


Tôi sẽ cải thiện điều đó một chút: "ptr là một cái tên đề cập đến" đi bên phải ... nó ), bây giờ đi bên trái ... đó là *"một con trỏ để" đi bên phải ... nó ), bây giờ đi bên trái ... nó một (đi ra, đi đúng ()như vậy "một chức năng mà không có đối số" đi đúng ... []"và trả về một mảng của" quyền đi ;kết thúc, nên đi lại ... *"gợi ý" đi trái ... int"số nguyên"
Cacahuete Frito


2

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

int *something[n];

Lưu ý về mức độ ưu tiên: toán tử mảng con ( []) 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 *, đưa ra tuyên bố tương đương với:

int *(something[i]);

Lưu ý về cách khai báo có ý nghĩa: int numnghĩa numlà mộtint , int *ptrhoặc int (*ptr)phương tiện, (giá trị tại ptr) là một int, làm cho ptrmột con trỏ tới int.

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

Trong cái thứ hai,

int (*something)[n];

Để hiểu được tuyên bố này, bạn phải làm quen với thực tế này:

Lưu ý về biểu diễn con trỏ của mảng: somethingElse[i]tương đương với*(somethingElse + i)

Vì vậy, thay thế somethingElsebằng (*something), chúng tôi nhận được *(*something + i), đó là một số nguyên theo khai báo. Vì vậy, đã (*something)cho chúng tôi một mảng, 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 là khó hiểu với nhiều người. Đây là một cách dễ dàng để hiểu nó.

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

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

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


0
int *arr1[5]

Trong khai báo này, arr1là một mảng gồm 5 con trỏ tới số nguyên. Lý do: Dấu ngoặc vuông có mức độ ưu tiên cao hơn * (toán tử dereferncing). Và trong loại này, số lượng hàng được cố định (5 ở đây), nhưng số lượng cột là biến.

int (*arr2)[5]

Trong khai báo này, arr2là một con trỏ tới một mảng nguyên gồm 5 phần tử. Lý do: Ở đây, dấu ngoặc () có độ ưu tiên cao hơn []. Và trong loại này, số lượng hàng là biến, nhưng số lượng cột được cố định (5 ở đây).


-7

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

trong mảng con trỏ nếu con trỏ tăng lên, nó nhảy sang mảng tiếp theo


" trong mảng con trỏ nếu con trỏ tăng lên, nó nhảy sang mảng tiếp theo " điều này hoàn toàn sai.
kiềm
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.