Đây là c++! Mọi thứ đều có thể! Nhưng đây là c++ vì vậy nó đòi hỏi một mức độ hiểu biết.
Cuối cùng chúng ta hãy bắt đầu với một ví dụ đơn giản của mảng 2 1 chiều: char firstName[4] = { 'J', 'o', 'n', '\0' }
và char lastName[4] = { 'M', 'e', 'e', '\0' }
Hãy xem xét một cách bố trí bộ nhớ có thể ở đây:
+------------+-------+
| Address | Value |
+------------+-------+
| 0x| 0x4A | <- firstName[0] - 'J'
| 0x76543211 | 0x6F | <- firstName[1] - 'o'
| 0x76543212 | 0x6E | <- firstName[2] - 'n'
| 0x76543213 | 0x00 | <- firstName[3] - '\0'
+------------+-------+
| 0x76543214 | 0x4D | <- lastName[0] - 'M'
| 0x76543215 | 0x65 | <- lastName[1] - 'e'
| 0x76543216 | 0x65 | <- lastName[2] - 'e'
| 0x76543217 | 0x00 | <- lastName[3] - '\0'
+------------+-------+
Với cách bố trí bộ nhớ này nếu bạn đã làm cout << firstName << ' ' << lastName
bạn 'd nhận được:
0x0x76543214
Những ar tia thực sự chỉ là một con trỏ đến yếu tố đầu tiên của chúng! Điều này minh họa Array Pointer Decay, mà bạn có thể đọc thêm về ở đây: http://en.cppreference.com/w/cpp/language/array#Array-to-pointer_decay
Trước khi chúng ta di chuyển trên có điều gì đó quan trọng ở đây cần lưu ý, char
s mất chính xác 1-byte, vì thế địa chỉ của từng tiếp theo char
trong mảng sẽ chỉ đơn giản là địa chỉ tiếp theo. Đó là đòn bẩy của Subscript Operator theo cách này: firstName[1]
tương đương với *(firstName + 1)
. Điều này đúng với char
s nhưng cũng đúng đối với bất kỳ loại nào khác chiếm hơn 1 byte. Chúng ta hãy xem ví dụ: short siArray = { 1, 2, 3, 4 }
, một cách bố trí bộ nhớ có thể có của siArray
sẽ trông như thế:
+------------+--------+
| Address | Value |
+------------+--------+
| 0x76543218 | 0x0001 | <- siArray[0] - 1
| 0x7654321A | 0x0002 | <- siArray[1] - 2
| 0x7654321C | 0x0003 | <- siArray[2] - 3
| 0x7654321E | 0x0004 | <- siArray[3] - 4
+------------+--------+
Mặc dù cout << siArray << ' ' << &(siArray[1])
chí đầu ra:
0x76543218 0x7654321A
*(siArray + 1)
sẽ vẫn chỉ số giống nhau phần tử của siArray
là siArray[1]
. Điều này là do khi thực hiện số học con trỏ c++ xem xét loại địa chỉ đang hoạt động, do đó việc tăng thêm short*
sẽ thực sự tăng địa chỉ theo số sizeof(short)
. Bạn có thể đọc thêm về số học con trỏ tại đây: http://en.cppreference.com/w/cpp/language/operator_arithmetic
Cuối cùng, hãy xem cách c++ lưu trữ mảng 2 chiều. Đưa ra: char name[2][4] = { { 'J', 'o', 'n', '\0' }, { 'M', 'e', 'e', '\0' } }
một cách bố trí bộ nhớ có thể sẽ là:
+------------+-------+
| Address | Value |
+------------+-------+
| 0x76543220 | 0x4A | <- name[0][0] - 'J'
| 0x76543221 | 0x6F | <- name[0][1] - 'o'
| 0x76543222 | 0x6E | <- name[0][2] - 'n'
| 0x76543223 | 0x00 | <- name[0][3] - '\0'
| 0x76543224 | 0x4D | <- name[1][0] - 'M'
| 0x76543225 | 0x65 | <- name[1][1] - 'e'
| 0x76543226 | 0x65 | <- name[1][2] - 'e'
| 0x76543227 | 0x00 | <- name[1][3] - '\0'
+------------+-------+
Vì chúng ta biết một giá trị mảng 1 chiều thực sự chỉ là một con trỏ, chúng ta có thể nhìn thấy từ cách bố trí bộ nhớ này mà name[0]
không phải là một con trỏ, nó chỉ là ký tự đầu tiên của mảng đầu tiên. Do đó name
không chứa 2 con trỏ mảng 1 chiều, nhưng chứa nội dung của 2 mảng. (Ngẫu nhiên trên một máy 32-bit không lưu trữ các con trỏ tiết kiệm 8-byte bộ nhớ, mà là khá đáng kể cho một mảng 2-chiều 2-byte.) Vì vậy, cố gắng để điều trị name
như là một char**
sẽ cố gắng sử dụng các ký tự như một con trỏ.
Có hiểu điều này chúng tôi thực sự chỉ cần tránh sử dụng số học con trỏ c++ để tìm giá trị dereference. Để làm được điều đó chúng tôi sẽ cần phải làm việc với một char*
để thêm 1 thực sự là chỉ cần thêm 1. Vì vậy, ví dụ:
const short si2DArray[2][3] = { { 11, 12, 13 }, { 21, 22, 23 } };
const auto psi2DPointer = reinterpret_cast<const char*>(si2DArray);
for(auto i = 0U; i < size(si2DArray); ++i) {
for(auto j = 0U; j < size(*si2DArray); ++j) {
cout << *reinterpret_cast<const short*>(psi2DPointer + i * sizeof(*si2DArray) + j * sizeof(**si2DArray)) << '\t';
}
cout << endl;
}
Live Example
Lưu ý rằng trong ví dụ này mặc dù tôi tham khảo si2DArray
nghĩ psi2DPointer
tôi vẫn đang sử dụng thông tin từ si2DArray
làm việc lập chỉ mục, cụ thể là:
- có bao nhiêu mảng đang trong chiều hướng chính:
size(si2DArray)
.210
- Có bao nhiêu yếu tố này trong chiều nhỏ:
size(*si2DArray)
- kích thước trong bộ nhớ của kích thước nhỏ là gì:
sizeof(**si2DArray)
Bạn do đó có thể thấy: sizeof(*si2DArray)
kiểu phần tử của mảng là gì rằng việc mất thông tin từ chuyển đổi từ một mảng sang con trỏ là đáng kể. Bạn có thể bị cám dỗ để bảo toàn kiểu phần tử, do đó cũng đơn giản hóa số học con trỏ. Đó là giá trị cần lưu ý rằng chỉ có một chuyển đổi để char*
được coi là hành vi được xác định bởi reinterpret_cast
: http://en.cppreference.com/w/cpp/language/reinterpret_cast#Type_aliasing
[Hỏi đáp liên quan] (http://stackoverflow.com/questions/4810664/) – fredoverflow