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ì?
const
và volatile
vòng loại, cả hai đều quan trọng và khó khăn, bị thiếu trong bài viết đó.
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ì?
const
và volatile
vòng loại, cả hai đều quan trọng và khó khăn, bị thiếu trong bài viết đó.
Câu trả lời:
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.
( ) [ ]
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
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 )
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.
int *a[][10]
cái sau thành công.
( ) [ ]
và từ phải sang trái của* &
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
[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.
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 arr2
trô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 < 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!)
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
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)
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
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.
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?
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
" ptr
là 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"
)
, 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"
Đây là một trang web thú vị giải thích cách đọc các loại phức tạp trong C: http://www.unixwiz.net/techtips/reading-cdecl.html
Đâ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 num
nghĩanum
là mộtint
,int *ptr
hoặcint (*ptr)
phương tiện, (giá trị tạiptr
) là mộtint
, làm choptr
một con trỏ tớiint
.
Đ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ế somethingElse
bằ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) .
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.
int *arr1[5]
Trong khai báo này, arr1
là 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, arr2
là 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).