Tại sao x [0]! = X [0] [0]! = X [0] [0] [0]?


149

Tôi đang nghiên cứu một chút về C ++ và tôi đang chiến đấu với con trỏ. Tôi hiểu rằng tôi có thể có 3 cấp con trỏ bằng cách khai báo:

int *(*x)[5];

vì vậy đó *xlà một con trỏ tới một mảng gồm 5 phần tử là con trỏ tới int. Ngoài ra tôi biết điều đó x[0] = *(x+0);, x[1] = *(x+1)vân vân ....

Vì vậy, đưa ra tuyên bố trên, tại sao là x[0] != x[0][0] != x[0][0][0]?


58
x[0], x[0][0]x[0][0][0]có nhiều loại khác nhau. Họ không thể được so sánh. Bạn có ý nghĩa !=gì?
bolov

4
@celticminstrel chúng không giống nhau: int **x[5]là một mảng gồm 5 phần tử. Một phần tử là một con trỏ tới con trỏ tới int`
bolov

5
@celticminstrel int** x[5]sẽ là một mảng gồm năm con trỏ trỏ đến các con trỏ trỏ tới int. int *(*x)[5]là một con trỏ tới một mảng gồm năm con trỏ trỏ tới int.
emlai

5
@celticminstrel quy tắc phải trái , quy tắc xoắn ốc , C vô nghĩa ↔ Tiếng Anh và 'trên con đường trở thành lập trình viên ba sao :)
bolov

5
@ Leo91: Thứ nhất, bạn có hai cấp con trỏ ở đây, không phải ba cấp. Thứ hai, x[0] != x[0][0] != x[0][0][0]có nghĩa là gì? Đây không phải là một so sánh hợp lệ trong C ++. Ngay cả khi bạn chia nó thành x[0] != x[0][0]x[0][0] != x[0][0][0]nó vẫn không hợp lệ. Vì vậy, câu hỏi của bạn có ý nghĩa gì?
AnT

Câu trả lời:


261

xlà một con trỏ tới một mảng gồm 5 con trỏ tới int.
x[0]là một mảng gồm 5 con trỏ tới int.
x[0][0]là một con trỏ đến một int.
x[0][0][0]là một int.

                       x[0]
   Pointer to array  +------+                                 x[0][0][0]         
x -----------------> |      |         Pointer to int           +-------+
               0x500 | 0x100| x[0][0]---------------->   0x100 |  10   |
x is a pointer to    |      |                                  +-------+
an array of 5        +------+                        
pointers to int      |      |         Pointer to int                             
               0x504 | 0x222| x[0][1]---------------->   0x222                    
                     |      |                                             
                     +------+                                             
                     |      |         Pointer to int                              
               0x508 | 0x001| x[0][2]---------------->   0x001                    
                     |      |                                             
                     +------+                                             
                     |      |         Pointer to int                              
               0x50C | 0x123| x[0][3]---------------->   0x123                    
                     |      |                                             
                     +------+                                             
                     |      |         Pointer to int                              
               0x510 | 0x000| x[0][4]---------------->   0x000                    
                     |      |                                             
                     +------+                                             

Bạn có thể thấy rằng

  • x[0]là một mảng và sẽ được chuyển đổi thành con trỏ thành phần tử đầu tiên của nó khi được sử dụng trong một biểu thức (với một số ngoại lệ). Do đó x[0]sẽ cung cấp cho các địa chỉ của phần tử đầu tiên của nó x[0][0]0x500.
  • x[0][0]chứa địa chỉ của một intmà là 0x100.
  • x[0][0][0]chứa một intgiá trị của 10.

Vì vậy, x[0]là bằng &x[0][0]và do đó , &x[0][0] != x[0][0].
Do đó , x[0] != x[0][0] != x[0][0][0].


Sơ đồ này hơi khó hiểu với tôi: 0x100nên xuất hiện ngay bên trái hộp chứa 10, giống như cách 0x500xuất hiện ở bên trái hộp của nó. Thay vì nó ở xa bên trái, và bên dưới.
MM

@MattMcNabb; Tôi không nghĩ nó nên gây nhầm lẫn, nhưng thay đổi theo đề nghị của bạn để rõ ràng hơn.
haccks 7/07/2015

4
@haccks - Niềm vui của tôi :) Lý do tại sao sơ ​​đồ đó là tuyệt vời là bởi vì bạn thậm chí không cần lời giải thích mà bạn đã đưa ra sau nó. Sơ đồ đó tự giải thích rằng nó đã trả lời câu hỏi. Các văn bản sau đây chỉ đơn giản là một phần thưởng.
rayryeng 7/07/2015

1
Bạn cũng có thể sử dụng yed, một phần mềm biểu đồ. Nó giúp tôi rất nhiều cho việc tổ chức những suy nghĩ của mình
rpax

@GrijeshChauhan Tôi sử dụng asciiflow cho các nhận xét về mã, yeD cho các bài thuyết trình :)
rpax

133
x[0] != x[0][0] != x[0][0][0]

là, theo bài viết của riêng bạn,

*(x+0) != *(*(x+0)+0) != *(*(*(x+0)+0)+0)`  

được đơn giản hóa

*x != **x != ***x

Tại sao nó phải bằng nhau?
Cái đầu tiên là địa chỉ của một số con trỏ.
Cái thứ hai là địa chỉ của một con trỏ khác.
Và thứ ba là một số intgiá trị.


Tôi không thể hiểu ... nếu x [0], x [0] [0], x [0] [0] [0] tương đương với * (x + 0), * (x + 0 + 0) , * (x + 0 + 0 + 0), tại sao họ nên có địa chỉ khác nhau?
Leo91

41
@ Leo91 x[0][0](x[0])[0], nghĩa là *((*(x+0))+0), không phải *(x+0+0). Dereference xảy ra trước khi thứ hai [0].
emlai

4
@ Leo91 x[0][0] != *(x+0+0)chỉ thích x[2][3] != x[3][2].
özg

@ Leo91 Nhận xét thứ hai mà bạn "hiểu ngay" đã bị xóa. Bạn không hiểu điều gì đó (có thể được giải thích tốt hơn trong câu trả lời), hoặc đây không phải là việc của bạn? (một số người thích xóa bình luận mà không có nhiều nội dung thông tin)
deviantfan 6/07/2015

@deviantfan xin lỗi tôi không thể hiểu ý của bạn. Tôi hiểu các câu trả lời cũng như nhiều ý kiến ​​đã giúp meto làm rõ khái niệm này.
Leo91

50

Đây là cách bố trí bộ nhớ của con trỏ của bạn:

   +------------------+
x: | address of array |
   +------------------+
            |
            V
            +-----------+-----------+-----------+-----------+-----------+
            | pointer 0 | pointer 1 | pointer 2 | pointer 3 | pointer 4 |
            +-----------+-----------+-----------+-----------+-----------+
                  |
                  V
                  +--------------+
                  | some integer |
                  +--------------+

x[0]mang lại "địa chỉ của mảng",
x[0][0]mang lại "con trỏ 0",
x[0][0][0]mang lại "một số nguyên".

Tôi tin rằng, nó nên rõ ràng bây giờ, tại sao tất cả chúng đều khác nhau.


Ở trên là đủ gần để hiểu cơ bản, đó là lý do tại sao tôi viết nó theo cách tôi đã viết nó. Tuy nhiên, như haccks chỉ ra đúng, dòng đầu tiên không chính xác 100%. Vì vậy, đây là tất cả các chi tiết tốt:

Từ định nghĩa của ngôn ngữ C, giá trị của x[0]là toàn bộ mảng con trỏ nguyên. Tuy nhiên, mảng là thứ bạn thực sự không thể làm gì với C. Bạn luôn thao tác cả địa chỉ hoặc thành phần của chúng, không bao giờ là toàn bộ mảng:

  1. Bạn có thể chuyển x[0]cho sizeofnhà điều hành. Nhưng đó không thực sự là việc sử dụng giá trị, kết quả của nó chỉ phụ thuộc vào loại.

  2. Bạn có thể lấy địa chỉ của nó mang lại giá trị của x, tức là "địa chỉ của mảng" với loại int*(*)[5]. Nói cách khác:&x[0] <=> &*(x + 0) <=> (x + 0) <=> x

  3. Trong tất cả các bối cảnh khác , giá trị của x[0]sẽ phân rã thành một con trỏ đến phần tử đầu tiên trong mảng. Đó là, một con trỏ có giá trị "địa chỉ của mảng" và kiểu int**. Hiệu ứng này giống như khi bạn xsử dụng một con trỏ kiểu int**.

Do sự phân rã của con trỏ mảng trong trường hợp 3., tất cả các lần sử dụng x[0]cuối cùng dẫn đến một con trỏ trỏ đến điểm bắt đầu của mảng con trỏ; cuộc gọi printf("%p", x[0])sẽ in nội dung của các ô nhớ được dán nhãn là "địa chỉ của mảng".


1
x[0]không phải là địa chỉ của mảng.
haccks

1
@haccks Có, theo chữ cái của tiêu chuẩn, x[0]không phải là địa chỉ của mảng, nó là mảng. Tôi đã thêm một lời giải thích sâu sắc về điều này, và tại sao tôi viết đó x[0]là "địa chỉ của mảng". Tôi hy vọng bạn thích nó.
cmaster - phục hồi monica

đồ thị tuyệt vời mà giải thích nó hoàn toàn tốt!
MK

"Tuy nhiên, mảng là thứ bạn thực sự không thể làm gì với C." -> Ví dụ về bộ đếm: printf("%zu\n", sizeof x[0]);báo cáo kích thước của mảng, không phải kích thước của một con trỏ.
chux - Tái lập lại

@ chux-ReinstateMonica Và tôi tiếp tục nói "Bạn luôn thao túng địa chỉ của họ hoặc các thành phần của chúng, không bao giờ là toàn bộ mảng", tiếp theo là điểm 1 của bảng liệt kê mà tôi nói về tác dụng của sizeof x[0]...
cmaster - phục hồi monica

18
  • x[0]hủy bỏ con trỏ ngoài cùng ( con trỏ tới mảng có kích thước 5 của con trỏ tới int) và dẫn đến một mảng có kích thước 5 của con trỏ tới int;
  • x[0][0]hủy bỏ con trỏ ngoài cùng lập chỉ mục cho mảng, dẫn đến một con trỏ tới int;
  • x[0][0][0] hội nghị tất cả mọi thứ, dẫn đến một giá trị cụ thể.

Nhân tiện, nếu bạn cảm thấy bối rối bởi những loại khai báo này có nghĩa gì, hãy sử dụng cdecl .


11

Hãy xem xét từng bước biểu thức x[0], x[0][0]x[0][0][0].

Như xđược định nghĩa theo cách sau

int *(*x)[5];

sau đó biểu thức x[0]là một mảng của loại int *[5]. Hãy tính đến biểu thức đó x[0]tương đương với biểu thức *x. Đó là hội thảo một con trỏ tới một mảng, chúng ta có được chính mảng đó. Hãy biểu thị nó giống như y là chúng ta có một tuyên bố

int * y[5];

Biểu hiện x[0][0]tương đương y[0]và có loại int *. Hãy biểu thị nó như z đó là chúng ta có một tuyên bố

int *z;

biểu thức x[0][0][0]tương đương với biểu thức y[0][0]mà lần lượt là tương đương với biểu thức z[0]và có loại int.

Vì vậy chúng tôi có

x[0] có loại int *[5]

x[0][0] có loại int *

x[0][0][0] có loại int

Vì vậy, chúng là các đối tượng của các loại khác nhau và bằng cách kích thước khác nhau.

Chạy chẳng hạn

std::cout << sizeof( x[0] ) << std::endl;
std::cout << sizeof( x[0][0] ) << std::endl;
std::cout << sizeof( x[0][0][0] ) << std::endl;

10

Điều đầu tiên tôi phải nói rằng

x [0] = * (x + 0) = * x;

x [0] [0] = * (* (x + 0) + 0) = * * x;

x [0] [0] [0] = * (* (* (x + 0) + 0)) = * * * x;

Vậy * x ≠ * * x ≠ * * * x

Từ hình ảnh sau đây tất cả mọi thứ là rõ ràng.

  x[0][0][0]= 2000

  x[0][0]   = 1001

  x[0]      = 10

nhập mô tả hình ảnh ở đây

Đây chỉ là một ví dụ, trong đó giá trị của x [0] [0] [0] = 10

và địa chỉ của x [0] [0] [0]1001

địa chỉ đó được lưu trữ trong x [0] [0] = 1001

và địa chỉ của x [0] [0]2000

và địa chỉ đó được lưu trữ tại x [0] = 2000

Vậy x [0] [0] [0] x [0] [0] x [0]

.

CHỈNH SỬA

Chương trình 1:

{
int ***x;
x=(int***)malloc(sizeof(int***));
*x=(int**)malloc(sizeof(int**));
**x=(int*)malloc(sizeof(int*));
***x=10;
printf("%d   %d   %d   %d\n",x,*x,**x,***x);
printf("%d   %d   %d   %d   %d",x[0][0][0],x[0][0],x[0],x,&x);
}

Đầu ra

142041096 142041112 142041128 10
10 142041128 142041112 142041096 -1076392836

Chương trình 2:

{
int x[1][1][1]={10};
printf("%d   %d   %d   %d \n ",x[0][0][0],x[0][0],x[0],&x);
}

Đầu ra

10   -1074058436   -1074058436   -1074058436 

3
Câu trả lời của bạn là sai lệch. x[0]không chứa địa chỉ kiến. Đó là một mảng. Nó sẽ phân rã để con trỏ đến phần tử đầu tiên của nó.
haccks

Ừm ... có nghĩa là gì? Chỉnh sửa của bạn giống như anh đào trên bánh cho câu trả lời sai của bạn. Thật vô nghĩa
haccks 6/07/2015

@haccks Nếu chỉ sử dụng con trỏ, câu trả lời này sẽ đúng. Sẽ có một số thay đổi trong phần địa chỉ khi sử dụng Array
apm

7

Nếu bạn xem các mảng từ góc nhìn trong thế giới thực, nó sẽ xuất hiện như sau:

x[0]là một container hàng hóa đầy thùng
x[0][0]là một thùng duy nhất, chứa đầy hộp đựng giày, trong thùng chở hàng.
x[0][0][0]là một hộp đựng giày duy nhất bên trong thùng, bên trong thùng chở hàng.

Ngay cả khi đó là hộp đựng giày duy nhất trong thùng duy nhất trong container chở hàng, thì nó vẫn là hộp đựng giày và không phải là container chở hàng


1
sẽ không phải x[0][0]là một thùng đầy những mảnh giấy có vị trí của hộp đựng giày được viết trên đó chứ?
wchargein

4

Có một nguyên tắc trong C ++ để: khai báo một biến chỉ chính xác cách sử dụng biến. Xem xét tuyên bố của bạn:

int *(*x)[5];

có thể được viết lại thành (cho rõ ràng hơn):

int *((*x)[5]);

Do nguyên tắc, chúng tôi có:

*((*x)[i]) is treated as an int value (i = 0..4)
 (*x)[i] is treated as an int* pointer (i = 0..4)
 *x is treated as an int** pointer
 x is treated as an int*** pointer

Vì thế:

x[0] is an int** pointer
 x[0][0] = (x[0]) [0] is an int* pointer
 x[0][0][0] = (x[0][0]) [0] is an int value

Vì vậy, bạn có thể tìm ra sự khác biệt.


1
x[0]là một mảng gồm 5 ints, không phải là một con trỏ. (nó có thể phân rã thành một con trỏ trong hầu hết các bối cảnh nhưng sự khác biệt rất quan trọng ở đây).
MM

Được rồi, nhưng bạn nên nói: x [0] là một mảng gồm 5 con trỏ int *
Nghĩa Bùi

Để đưa ra đạo hàm chính xác cho mỗi @MattMcNabb: *(*x)[5]là một int, cũng (*x)[5]là một int *, cũng *xlà một (int *)[5], cũng xlà một *((int *)[5]). Đó là, xlà một con trỏ tới 5 mảng con trỏ tới int.
wchargein

2

Bạn đang cố gắng so sánh các loại khác nhau theo giá trị

Nếu bạn lấy địa chỉ, bạn có thể nhận được nhiều hơn những gì bạn mong đợi

Hãy nhớ rằng tuyên bố của bạn làm cho một sự khác biệt

 int y [5][5][5];

sẽ cho phép so sánh mà bạn muốn, kể từ khi y, y[0], y[0][0], y[0][0][0]sẽ có giá trị khác nhau và các loại nhưng cùng một địa chỉ

int **x[5];

không chiếm không gian tiếp giáp.

xx [0]có cùng một địa chỉ, nhưng x[0][0]x[0][0][0]mỗi địa chỉ khác nhau


2
int *(*x)[5]khác vớiint **x[5]
MM

2

pmột con trỏ: bạn đang sắp xếp các cuộc hội thảo với p[0][0], tương đương với*((*(p+0))+0) .

Trong tham chiếu C (&) và ký hiệu (*):

p == &p[0] == &(&p[0])[0] == &(&(&p[0])[0])[0])

Tương đương với:

p == &*(p+0) == &*(&*(p+0))+0 == &*(&*(&*(p+0))+0)+0

Hãy nhìn xem, & * có thể được cấu trúc lại, chỉ cần xóa nó:

p == p+0 == p+0+0 == p+0+0+0 == (((((p+0)+0)+0)+0)+0)

Bạn đang cố gắng thể hiện điều gì với mọi thứ sau câu đầu tiên? Bạn chỉ có rất nhiều biến thể trên p == p . &(&p[0])[0]khác vớip[0][0]
MM

Chàng trai hỏi tại sao 'x [0]! = X [0] [0]! = X [0] [0] [0]' khi x là một con trỏ, phải không? Tôi đã cố gắng cho anh ta thấy rằng anh ta có thể bị mắc kẹt bởi ký hiệu C về tính quy tắc (*) khi anh ta xếp chồng [0]. Vì vậy, hãy thử chỉ cho anh ta ký hiệu chính xác để có x bằng x [0], tham chiếu x [0] lần nữa với &, v.v.
Luciano

1

Các câu trả lời khác là đúng, nhưng không ai trong số họ nhấn mạnh ý tưởng rằng cả ba đều có thể chứa cùng một giá trị , và vì vậy chúng theo một cách nào đó không đầy đủ.

Lý do điều này không thể hiểu được từ các câu trả lời khác là vì tất cả các minh họa, trong khi hữu ích và chắc chắn hợp lý trong hầu hết các trường hợp, không bao gồm tình huống mà con trỏ xchỉ vào chính nó.

Điều này là khá dễ dàng để xây dựng, nhưng rõ ràng là một chút khó hiểu. Trong chương trình bên dưới, chúng ta sẽ thấy làm thế nào chúng ta có thể buộc cả ba giá trị giống hệt nhau.

LƯU Ý: Hành vi trong chương trình này không được xác định, nhưng tôi đăng nó ở đây hoàn toàn là một minh chứng thú vị về những điều mà con trỏ có thể làm, nhưng không nên .

#include <stdio.h>

int main () {
  int *(*x)[5];

  x = (int *(*)[5]) &x;

  printf("%p\n", x[0]);
  printf("%p\n", x[0][0]);
  printf("%p\n", x[0][0][0]);
}

Điều này biên dịch mà không có cảnh báo trong cả C89 và C99, và đầu ra là như sau:

$ ./ptrs
0xbfd9198c
0xbfd9198c
0xbfd9198c

Thật thú vị, cả ba giá trị là giống hệt nhau. Nhưng điều này không phải là một bất ngờ! Đầu tiên, hãy chia nhỏ chương trình.

Chúng tôi khai báo xlà một con trỏ tới một mảng gồm 5 phần tử trong đó mỗi phần tử thuộc kiểu con trỏ tới int. Tuyên bố này phân bổ 4 byte trên ngăn xếp thời gian chạy (hoặc nhiều hơn tùy thuộc vào việc triển khai của bạn; trên con trỏ máy của tôi là 4 byte), do đó, xđề cập đến một vị trí bộ nhớ thực tế. Trong họ ngôn ngữ C, nội dung xchỉ là rác, thứ gì đó còn sót lại từ việc sử dụng vị trí trước đó, vì vậy xbản thân nó không chỉ ra bất cứ nơi nào mà chắc chắn không phân bổ không gian.

Vì vậy, một cách tự nhiên, chúng ta có thể lấy địa chỉ của biến xvà đặt nó ở đâu đó, vì vậy đó chính xác là những gì chúng ta làm. Nhưng chúng ta sẽ tiếp tục và đặt nó vào x chính nó. Vì &xcó một loại khác vớix , chúng tôi cần thực hiện một vai diễn để chúng tôi không nhận được cảnh báo.

Mô hình bộ nhớ sẽ trông giống như thế này:

0xbfd9198c
+------------+
| 0xbfd9198c |
+------------+

Vì vậy, khối bộ nhớ 4 byte tại địa chỉ 0xbfd9198cchứa mẫu bit tương ứng với giá trị thập lục phân 0xbfd9198c. Đủ đơn giản.

Tiếp theo, chúng tôi in ra ba giá trị. Các câu trả lời khác giải thích những gì mỗi biểu thức đề cập đến, vì vậy mối quan hệ nên rõ ràng ngay bây giờ.

Chúng ta có thể thấy rằng các giá trị là như nhau, nhưng chỉ ở mức độ rất thấp ... các mẫu bit của chúng giống hệt nhau, nhưng dữ liệu loại được liên kết với mỗi biểu thức có nghĩa là các giá trị được giải thích của chúng là khác nhau. Chẳng hạn, nếu chúng tôi in ra x[0][0][0]bằng chuỗi định dạng%d , chúng ta sẽ nhận được một số âm rất lớn, do đó, "các giá trị" trên thực tế là khác nhau, nhưng mẫu bit là như nhau.

Điều này thực sự rất đơn giản ... trong các sơ đồ, các mũi tên chỉ vào cùng một địa chỉ bộ nhớ chứ không phải là các địa chỉ khác nhau. Tuy nhiên, trong khi chúng tôi có thể buộc một kết quả mong đợi từ hành vi không xác định, thì đó chỉ là điều không xác định. Đây không phải là mã sản xuất mà chỉ đơn giản là một minh chứng cho mục đích hoàn thiện.

Trong một tình huống hợp lý, bạn sẽ sử dụng mallocđể tạo mảng 5 con trỏ int và một lần nữa để tạo các int được trỏ đến trong mảng đó.mallocluôn trả về một địa chỉ duy nhất (trừ khi bạn hết bộ nhớ, trong trường hợp đó, nó trả về NULL hoặc 0), vì vậy bạn sẽ không bao giờ phải lo lắng về các con trỏ tự giới thiệu như thế này.

Hy vọng rằng đó là câu trả lời đầy đủ mà bạn đang tìm kiếm. Bạn không nên mong đợi x[0], x[0][0]x[0][0][0]bằng nhau, nhưng chúng có thể là nếu bị ép buộc. Nếu bất cứ điều gì đi qua đầu của bạn, cho tôi biết để tôi có thể làm rõ!


Tôi sẽ nói rằng một cách sử dụng con trỏ kỳ lạ khác mà tôi đã thấy.
haccks 11/07/2015

@haccks Vâng, nó khá kỳ lạ, nhưng khi bạn phá vỡ nó, nó cũng cơ bản như các ví dụ khác. Nó chỉ là một trường hợp trong đó các mẫu bit đều giống nhau.
Purag 11/07/2015

Mã của bạn gây ra hành vi không xác định. x[0]không thực sự đại diện cho một đối tượng hợp lệ của loại chính xác
MM

@MattMcNabb nó không được xác định, và tôi thực sự rất rõ về điều đó. Tôi không đồng ý về các loại. xlà một con trỏ tới một mảng, vì vậy chúng ta có thể sử dụng []toán tử để chỉ định một phần bù từ con trỏ đó và hủy bỏ nó. Có gì lạ ở đó? Kết quả x[0]là một mảng và C không phàn nàn nếu bạn in bằng cách sử dụng %pvì đó là cách nó được triển khai bên dưới.
Purag

Và việc biên dịch điều này với -pedanticcờ không tạo ra cảnh báo nào, vì vậy C rất ổn với các loại ...
Purag 20/07/2015

0

Các loại int *(*x)[5]int* (*)[5]tức là một con trỏ tới một mảng của 5 con trỏ đến kiểu int.

  • xlà địa chỉ của mảng đầu tiên gồm 5 con trỏ tới ints (một địa chỉ có kiểu int* (*)[5])
  • x[0]địa chỉ của mảng đầu tiên gồm 5 con trỏ tới ints (cùng địa chỉ với loại int* [5]) (địa chỉ bù x theo 0*sizeof(int* [5])nghĩa là chỉ số * size-of-type-Being-nhọn-to-dereference)
  • x[0][0]là con trỏ đầu tiên đến một int trong mảng (cùng địa chỉ với loại int*) (bù địa chỉ x theo 0*sizeof(int* [5])và dereference và sau đó bởi 0*sizeof(int*)và dereference)
  • x[0][0][0]là int đầu tiên được trỏ đến bởi một con trỏ tới một int (địa chỉ offset x theo 0*sizeof(int* [5])và tính quy định và bù địa chỉ đó bằng 0*sizeof(int*)và tính chính quy và bù địa chỉ đó theo 0*sizeof(int)và theo quy định )

Các loại int *(*y)[5][5][5]int* (*)[5][5][5]tức là một con trỏ đến một mảng 3d 5x5x5 con trỏ để ints

  • x là địa chỉ của mảng 3d đầu tiên của con trỏ 5x5x5 đến ints với kiểu int*(*)[5][5][5]
  • x[0]là địa chỉ của mảng 3d đầu tiên của con trỏ 5x5x5 đến ints (bù địa chỉ x theo 0*sizeof(int* [5][5][5])và quy định)
  • x[0][0]là địa chỉ của mảng 2d đầu tiên của 5x5 con trỏ tới ints (bù địa chỉ x theo 0*sizeof(int* [5][5][5])và quy định sau đó bù địa chỉ đó theo 0*sizeof(int* [5][5]))
  • x[0][0][0]là địa chỉ của mảng đầu tiên gồm 5 con trỏ tới ints (bù địa chỉ x theo 0*sizeof(int* [5][5][5])và tính quy định và bù địa chỉ đó theo 0*sizeof(int* [5][5])và bù địa chỉ đó theo 0*sizeof(int* [5]))
  • x[0][0][0][0]là con trỏ đầu tiên đến một int trong mảng (bù địa chỉ x theo 0*sizeof(int* [5][5][5])và tính quy định và bù địa chỉ đó bằng cách 0*sizeof(int* [5][5])và bù địa chỉ đó bằng 0*sizeof(int* [5])và bù địa chỉ đó theo 0*sizeof(int*)và theo quy định )
  • x[0][0][0][0][0]là int đầu tiên được trỏ đến bởi một con trỏ tới một int (offset địa chỉ x theo 0*sizeof(int* [5][5][5])và dereference và offset địa chỉ đó bằng cách 0*sizeof(int* [5][5])và bù địa chỉ đó bằng cách 0*sizeof(int* [5])và bù địa chỉ đó bằng 0*sizeof(int*)và dereference và offset địa chỉ đó theo 0*sizeof(int)và dereference)

Đối với phân rã mảng:

void function (int* x[5][5][5]){
  printf("%p",&x[0][0][0][0]); //get the address of the first int pointed to by the 3d array
}

Điều này tương đương với việc vượt qua int* x[][5][5]hoặc int* (*x)[5][5]tức là tất cả chúng đều phân rã sang cái sau. Đây là lý do tại sao bạn không nhận được cảnh báo trình biên dịch để sử dụng x[6][0][0]trong hàm nhưng bạn sẽ làm x[0][6][0]vì thông tin kích thước đó được giữ nguyên

void function (int* (*x)[5][5][5]){
  printf("%p",&x[0][0][0][0][0]); //get the address of the first int pointed to by the 3d array
}
  • x[0] là địa chỉ của mảng 3d đầu tiên của con trỏ 5x5x5 tới ints
  • x[0][0] là địa chỉ của mảng 2d đầu tiên của 5x5 con trỏ tới ints
  • x[0][0][0] là địa chỉ của mảng đầu tiên gồm 5 con trỏ tới ints
  • x[0][0][0][0] là con trỏ đầu tiên đến một int trong mảng
  • x[0][0][0][0][0] là int đầu tiên được trỏ đến bởi một con trỏ tới một int

Trong ví dụ cuối, nó rõ ràng hơn về mặt ngữ nghĩa để sử dụng *(*x)[0][0][0]hơn là x[0][0][0][0][0], điều này là do đầu tiên và cuối cùng [0]ở đây được hiểu là một biểu tượng con trỏ thay vì một chỉ mục thành một mảng nhiều chiều, vì loại. Tuy nhiên, chúng giống hệt nhau bởi vì (*x) == x[0]bất kể ngữ nghĩa. Bạn cũng có thể sử dụng *****x, trông giống như bạn đang hủy bỏ con trỏ 5 lần, nhưng nó thực sự được hiểu chính xác như nhau: một phần bù, một phần tử, một phần tử, 2 phần bù vào một mảng và một phần bổ sung, hoàn toàn là do loại bạn đang áp dụng các hoạt động để.

Về cơ bản khi bạn [0]hoặc *một *loại không phải là một mảng, nó là một phần bù và một sự quy định do thứ tự ưu tiên *(a + 0).

Khi bạn [0]hoặc *một *kiểu mảng thì đó là một sự bù trừ sau đó là sự vô hiệu hóa tạm thời (tính chính xác được trình biên dịch giải quyết để mang lại cùng một địa chỉ - đó là một hoạt động tạm thời).

Khi bạn [0]hoặc *một loại có kiểu mảng 1d thì đó là phần bù trừ

Nếu bạn [0]hoặc **một kiểu mảng 2d thì đó chỉ là phần bù, tức là phần bù trừ bình thường.

Nếu bạn [0][0][0]hoặc ***một kiểu mảng 3d thì đó là một sự bù trừ + sự bình thường của idempotent, sau đó là một sự bù trừ + sự bình thường của idempotent, sau đó là một sự bù trừ + sự bình thường của idempotent Sự quy định thực sự chỉ xảy ra khi loại mảng bị loại bỏ hoàn toàn.

Đối với ví dụ về int* (*x)[1][2][3]loại được mở ra theo thứ tự.

  • x có một loại int* (*)[1][2][3]
  • *xcó một loại int* [1][2][3](bù 0 + biểu thị trạng thái bình thường)
  • **xcó một loại int* [2][3](bù 0 + biểu thị trạng thái bình thường)
  • ***xcó một loại int* [3](bù 0 + biểu thị trạng thái bình thường)
  • ****xcó một loại int*(bù 0 + bổ sung)
  • *****xcó loại int(bù 0 + bổ sung)
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.