Tại sao một phần kích thước của một mảng của loại này?


14

Trong khi đọc cuốn sách C ++ Primer, tôi đã bắt gặp câu nói này: "Số phần tử trong một mảng là một phần của kiểu của mảng." Vì vậy, tôi muốn tìm hiểu bằng cách sử dụng mã sau đây:

#include<iostream>

int main()
{
    char Array1[]{'H', 'e', 'l', 'p'};
    char Array2[]{'P', 'l', 'e', 'a', 's', 'e'};

    std::cout<<typeid(Array1).name()<<std::endl;        //prints  A4_c
    std::cout<<typeid(Array2).name()<<std::endl;        //prints  A6_c

    return 0;
}

Và điều thú vị là kết quả của typeid trên hai mảng cho thấy chúng khác nhau.

  • Điều gì đang xảy ra đằng sau hậu trường?
  • Tại sao cần phải có một mảng bao gồm kích thước của nó? Có phải chỉ vì kích thước của nó không nên thay đổi?
  • Làm thế nào điều này sẽ ảnh hưởng đến việc so sánh các mảng?

Chỉ muốn có thể hiểu sâu sắc khái niệm.


3
Không nhất thiết phải bao gồm thông tin kích thước trong loại, nhưng nó hữu ích
byxor 21/11/19

Bất kỳ hướng dẫn về mảng sẽ giải thích (1). Tôi không chắc ý của bạn là gì bởi (3), vì không có cách dựng sẵn để so sánh các mảng.
HolyBlackCat

Câu trả lời:


20

Điều gì đang xảy ra đằng sau hậu trường?

Theo định nghĩa, một vật liệu không được phân bổ động là một thùng chứa có kích thước cố định gồm các phần tử đồng nhất. Một mảng các Nphần tử của kiểu Tđược đặt trong bộ nhớ dưới dạng một chuỗi các Nđối tượng liền kề T.


Tại sao các mảng phải có kiểu bao gồm kích thước của nó?

Tôi không tin rằng "cần thiết" cho kiểu của một mảng để bao gồm kích thước của nó - vì thực tế, bạn có thể sử dụng một con trỏ để chỉ một chuỗi các Tđối tượng liền kề . Con trỏ như vậy sẽ mất thông tin kích thước về mảng.

Đó là, tuy nhiên, một điều hữu ích để có. Nó cải thiện an toàn loại và mã hóa thông tin hữu ích tại thời gian biên dịch có thể được sử dụng theo nhiều cách. Ví dụ: bạn có thể sử dụng các tham chiếu đến mảng để quá tải trên các mảng có kích thước khác nhau

void foo(int(&array)[4]) { /* ... */ }
void foo(int(&array)[8]) { /* ... */ }

hoặc để tìm ra kích thước của một mảng như là một biểu thức không đổi

template <typename T, std::size_t N>
constexpr auto sizeOf(const T(&array)[N]) { return N; }

Làm thế nào điều này sẽ ảnh hưởng đến việc so sánh các mảng?

Nó không, thực sự.

Bạn không thể so sánh các mảng kiểu C theo cùng một cách bạn sẽ so sánh hai số (ví dụ: intcác đối tượng). Bạn sẽ phải viết một số loại so sánh từ điển, và quyết định ý nghĩa của nó đối với các bộ sưu tập có kích cỡ khác nhau. std::vector<T>cung cấp điều đó , và logic tương tự có thể được áp dụng cho các mảng.


Phần thưởng: C ++ 11 trở lên cung cấp std::array, là trình bao bọc xung quanh mảng kiểu C với giao diện giống như container. Nó nên được ưu tiên cho các mảng kiểu C vì nó phù hợp hơn với các container khác (ví dụ std::vector<T>), và cũng hỗ trợ các so sánh từ điển ngoài hộp.


2
"Bạn sẽ phải viết một số loại so sánh từ điển, và quyết định ý nghĩa của nó đối với các bộ sưu tập có kích thước khác nhau." Bạn chỉ có thể sử dụng std::equal(thông qua std::beginstd::endđược xác định cho mảng). Trong trường hợp đó, các mảng có kích thước khác nhau là không bằng nhau.
Stu

3
Có thể đáng chú ý rằng các vòng lặp dựa trên phạm vi có phạm vi là một mảng cần đọc kích thước của mảng tại thời gian biên dịch - đó là một công cụ tinh tế hơn một chút vì (rất may!) Người ta không bao giờ viết ra loại mảng trong ví dụ này, nhưng nó dường như xuất hiện nhiều hơn là quá tải dựa trên kích thước.
Milo Brandt

8

Lượng không gian được phân bổ cho một đối tượng khi bạn tạo nó phụ thuộc hoàn toàn vào loại của nó. Phân bổ mà tôi đang nói đến không phải là phân bổ từ newhoặc malloc, nhưng không gian được phân bổ để bạn có thể chạy hàm tạo và khởi tạo đối tượng của mình.

Nếu bạn có một cấu trúc được định nghĩa là (ví dụ)

struct A { char a, b; }; //sizeof(A) == 2, ie an A needs 2 bytes of space

Sau đó, khi bạn xây dựng đối tượng:

A a{'a', 'b'};

Bạn có thể nghĩ về quá trình xây dựng đối tượng là một quá trình:

  • Phân bổ 2 byte không gian (trên ngăn xếp, nhưng ví dụ này không quan trọng)
  • Chạy hàm tạo của đối tượng (trong trường hợp này sao chép 'a''b'vào đối tượng)

Điều quan trọng cần lưu ý là 2 byte không gian cần thiết hoàn toàn được xác định bởi loại đối tượng, các đối số cho hàm không quan trọng. Vì vậy, đối với một mảng, quy trình là như nhau, ngoại trừ bây giờ lượng không gian cần thiết phụ thuộc vào số lượng phần tử trong mảng.

char a[] = {'a'}; //need space for 1 element
char b[] = {'a', 'b', 'c', 'd', 'e'}; //need space for 5 elements

Vì vậy, các loại abphải phản ánh thực tế là acần đủ không gian cho 1 char và bcần đủ không gian cho 5 ký tự. Điều đó không có nghĩa là kích thước của các mảng này không thể thay đổi đột ngột, một khi mảng 5 phần tử được tạo, nó luôn luôn là mảng 5 phần tử. Để có các đối tượng giống như "mảng" trong đó kích thước có thể thay đổi, bạn cần cấp phát bộ nhớ động, mà sách của bạn sẽ được che ở một số điểm.


0

Đó là vì một lý do nội bộ cho thư viện thời gian chạy. Nếu bạn xem xét các tuyên bố sau, ví dụ:

unsigned int i;
unsigned int *iPtr;
unsigned int *iPtrArr[2];
unsigned int **iPtrHandle;

Sau đó, nó trở nên rõ ràng vấn đề là gì: Ví dụ, việc đánh địa chỉ unsigned int *phải liên quan đến chính nó sizeof operatorhoặc địa chỉ của unsigned int.

Có một lời giải thích chi tiết hơn cho phần còn lại của những gì bạn thấy ở đây, nhưng phần lớn là tóm tắt lại những gì được trình bày trong Ngôn ngữ lập trình C, Phiên bản 2 của Kernighan và Ritchie liên quan đến chương trình in văn bản ngôn ngữ đơn giản của loại khai báo chuỗi.

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.