2011-07-27 31 views
14

Vì vậy, chúng ta hãy nói rằng tôi có một mảng:Khi một mảng-T đã phân rã thành một con trỏ tới T, nó có thể được tạo thành mảng-of-T một lần nữa không?

int a[3] = { 1, 2, 3 }; 

Bây giờ nếu tôi được kiểm tra các loại 'a', trên máy tính của tôi, tôi nhận được:

cout<<typeid(a).name(); // prints 'A3_i' 

Bây giờ nếu tôi có địa chỉ của 'a', sau đó dereference địa chỉ đó, các loại không thay đổi (mà tôi thực sự thích, bởi vì trong tâm trí của tôi 'dùng địa chỉ' và 'dereferencing' là hoạt động ngược lại):

cout<<typeid(*&a).name(); // also prints 'A3_i' 

Tuy nhiên nếu tôi derefere nce 'a' đầu tiên, sau đó lấy địa chỉ của điều đó, loại thay đổi (tôi thừa nhận rằng tôi không có thời gian khó thích, vì khi tôi đăng ký lại mảng, tôi sẽ nhận được int, và khi tôi lấy địa chỉ của rằng int, tôi sẽ nhận được một con trỏ-to-int, và nó quay ra tôi làm):

cout<<typeid(&*a).name(); // prints 'Pi' 

Vì vậy, đây là hai câu hỏi của tôi:

1) Khi một mảng kiểu đã phân rã thành một kiểu con trỏ, là có anyway để có được nó trở lại một mảng-loại?

Tôi đã thử các chiến lược rõ ràng đúc giống như-bạn-chỉ-đừng-care:

cout<<typeid((int[3]) &*a).name(); // does not compile 
// "error: ISO C++ forbids casting to an array type `int [3]'" 

Có một dàn diễn viên đó sẽ làm việc? Hoặc đó là loại chuyển đổi nghiêm ngặt off-giới hạn?

2) Có hay không bạn có thể quay lại kiểu mảng, chính xác thông tin nào được cắt và bị mất trong quá trình chuyển đổi từ con trỏ sang con trỏ?

Tôi hiểu rằng kiểu con trỏ và loại mảng không tương đương. Tôi giả sử rằng kiểu mảng là một siêu dữ liệu nghiêm ngặt của thông tin được lưu trữ trong kiểu con trỏ. Điều này có đúng không?

Tôi đã đọc trong các câu hỏi khác rằng thông tin bổ sung trong loại mảng là: kiến ​​thức về việc mảng có nằm trên ngăn xếp hay không và kích thước của nó (nó phải biết kích thước của mảng bằng cách nào đó, vì nó là một phần của loại, phải không?). Có bất kỳ thông tin nào khác bị ẩn trong mảng kiểu không?

+0

Tôi sẽ quan sát rằng int [3] không phải là một loại, mặc dù int [] là. –

+4

@Daniel: "Mảng 3 int" chắc chắn là khác biệt với "int []" và "int [3]" có vẻ như là viết tắt hợp lý cho trước đây. –

+0

Bất kể bạn nói gì. –

Trả lời

10

Tôi không chắc đây có phải là những gì bạn đang tìm kiếm hay không nhưng bạn có thể sử dụng typecasting để lấy lại một đối tượng có cùng loại với mảng ban đầu. Ý tưởng là sử dụng các kiểu con trỏ-to-mảng và tham chiếu đến mảng ít được biết đến để phục hồi thông tin. Ví dụ:

char arr[137]; 
cout << sizeof(arr) << endl; // Prints 137 
cout << sizeof(arr[0]) << endl; // Prints 1 
cout << sizeof(&arr[0]) << endl; // Prints 4 (on my system) 
cout << sizeof(*&arr[0]) << endl; // Prints 1 
cout << sizeof((char (&) [137]) *&arr[0]) << endl; // Prints 137 

Ý tưởng là chúng ta định kiểu tham chiếu được tạo ra bằng cách sử dụng *&arr[0]char (&)[137], một tham chiếu đến một mảng của 137 ký tự. Bây giờ tham chiếu có loại này, toán tử sizeof biết rằng nó sẽ in 137, vì kích thước của một mảng gồm 137 ký tự thực sự là 137.

Tuy nhiên, điều này chỉ hoạt động nếu bạn nhập đúng loại! Ví dụ: điều này là hoàn toàn hợp pháp:

char arr[137]; 
cout << sizeof((char (&) [42]) *&arr[0]) << endl; // Prints 42 

Vì vậy, bạn có thể lấy thông tin đó sai và dẫn đến trường hợp bạn đã khôi phục thông tin không chính xác.

Một lần nữa, tôi không chắc chắn nếu đây là những gì bạn đang tìm kiếm, nhưng nó cho thấy rằng bạn thực sự có thể sử dụng đúc để lấy lại thông tin kích thước mảng.

+1

Nhưng bạn không phải chỉ nói với diễn viên những gì bạn muốn nghe? Ngoài ra, tôi muốn nó hoạt động trên thành phần của & * a (làm cho loại thay đổi từ kiểu mảng thành kiểu con trỏ), không phải thành phần * & (để ​​lại kiểu mảng không thay đổi). – Jimmy

+0

@ Jimmy- Bạn hoàn toàn đúng - điều này chỉ nói với mảng kích thước của nó là gì, do đó "Tôi không chắc chắn những gì bạn đang tìm kiếm." Đối với trường hợp '& * a', bạn có thể sử dụng phép đúc' (T (*) [size]) & * a' để cho mảng đó biết kích thước ban đầu của nó, BTW. Tôi khá chắc chắn rằng nói chung điều này không thể được thực hiện và một khi kích thước mảng đã biến mất, nó đã biến mất, nhưng trong trường hợp bạn có kiến ​​thức đặc biệt về kích thước nên được tôi nghĩ rằng những gì tôi có là chính xác. – templatetypedef

+0

Đó là gọn gàng. Khi tôi thử: cout << typeid ((int (*) [3]) & * a) .name() << endl; // in PA3_i ... đó là một con trỏ đến kiểu tôi muốn ... vì vậy sau đó tôi đã thử: cout << typeid (* (int (*) [3]) & * a) .name() << endl ; // in A3_i ... đó là loại tôi sau. Tôi không hiểu thứ tự của phân tích (vì vậy tôi không biết nếu loại thực sự bị mất), nhưng tôi nghĩ rằng câu trả lời câu hỏi đầu tiên của tôi trong khẳng định (miễn là bạn biết kích thước, đó là tốt). Bất kỳ suy nghĩ về câu hỏi thứ hai? p.s. Tôi không biết tại sao nó cho thấy int() khi tôi thực sự gõ int (*) trong bình luận này ... – Jimmy

3

Đây không phải là một câu trả lời hoàn chỉnh cho câu hỏi của bạn, nhưng đây là những gì tôi phải cung cấp -

Tôi không nghĩ rằng có một cách để có được trở lại một kiểu mảng một khi nó đã mục nát đến một con trỏ.

Kiểu mảng ban đầu là T [N], nơi N là số phần tử trong mảng. Một khi nó đã bị phân rã thành một con trỏ, thông tin kích thước sẽ bị mất đi. Như các chương trình answer của @ templatetypdef, bạn có thể đưa con trỏ trở lại kiểu mảng, nhưng sau đó anh ta tiếp tục chứng minh rằng bằng cách truyền bạn chỉ đang nói trình biên dịch kích thước là gì, nó không thực sự có cách suy luận thông tin đó từ con trỏ.

Để giữ nguyên thông tin loại ban đầu, bạn phải chuyển mảng bằng tham chiếu thay vì bằng con trỏ.

#include <iostream> 
#include <typeinfo> 

template<size_t N> 
void ByRef(int(&array)[N]) 
{ 
    std::cout << typeid(array).name() << std::endl; 
} 

void ByPointer(int *array) 
{ 
    std::cout << typeid(array).name() << std::endl; 
    std::cout << typeid((int(&)[4])array).name() << std::endl; 
    std::cout << typeid((int(&)[42])array).name() << std::endl; 
} 

int main() 
{ 
    int a[4] = {1,2,3,4}; 

    ByRef(a); 
    ByPointer(a); 

    return 0; 
} 

Đầu ra của trên là

A4_i 
Pi 
A4_i 
A42_i 
2

Không chắc chắn những gì bạn đang nhận tại, nhưng một khi bạn dành & bất cứ điều gì, tất cả các bạn đã có được một địa chỉ. Và các cấu trúc dữ liệu phi đối tượng đơn giản không chứa các bộ mô tả, do đó, không có cách nào, được cung cấp địa chỉ, để nhận thêm thông tin cho bất kỳ địa chỉ nào. Ngay cả thực tế là nó địa chỉ, nói rằng, một char chỉ được truyền đạt bởi kiểu con trỏ - nếu bạn bỏ qua void* thì thông tin đó sẽ bị mất (đến mức nó đã từng tồn tại).

Tôi hiểu rằng kiểu con trỏ và loại mảng không tương đương. Tôi giả sử rằng kiểu mảng là một siêu dữ liệu nghiêm ngặt của thông tin được lưu trữ trong kiểu con trỏ. Điều này có đúng không?

Thông tin không được "lưu trữ" trong một loại. Thông tin (nếu nó tồn tại trong thời gian chạy) được lưu trữ trong các biến. Trong C/C++ không có gõ động cho các đối tượng không, do đó, không chỉ là thông tin không được "lưu trữ" trong các loại, nhưng loại thông tin không được "lưu trữ" ở bất kỳ đâu. Loại thông tin hoàn toàn là một khái niệm biên dịch-thời gian, và nó "chảy" từ một điểm khác trong một chương trình hoàn toàn là kết quả của phân tích tĩnh của trình biên dịch. Tức là, đó là một tiểu thuyết tiện lợi.

+1

Tôi tin rằng tôi hiểu lời giải thích của bạn. Những gì tôi gọi là 'loại thông tin' chỉ là một đặc điểm kỹ thuật mà chỉ đạo trình biên dịch để hoạt động đúng. Vì vậy, 'thông tin bổ sung' trong một mảng-loại chỉ số tiền để kiểm tra thêm thực hiện trong thời gian biên dịch (tức là biên dịch xác minh thời gian mà một loại con trỏ không nhất thiết phải chịu)? – Jimmy

+0

Không có thông tin loại thời gian chạy trừ khi bạn bật nó cho các lớp đa hình, hoặc trừ khi bạn đếm những thứ được sử dụng để thực hiện đa hình ở nơi đầu tiên. Tuy nhiên, thông tin kiểu thời gian biên dịch kiểm soát nhiều hơn chỉ là "kiểm tra bổ sung"; mảng không phải là con trỏ. 'int x [3]', khái niệm, phân bổ bộ nhớ trên ngăn xếp cho một khối 3 số nguyên, có địa chỉ có thể được tìm ra theo yêu cầu (quá trình phân rã); 'int * x', phân bổ khái niệm bộ nhớ trên ngăn xếp cho một địa chỉ, mà không chỉ định địa chỉ đó là ** của **. –

+0

Jimmy, tôi nghĩ bạn đã có nó. Trên hầu hết các máy, thậm chí không có cách nào để biết liệu một nhóm bit có phải là một con trỏ hay không, so với một số nguyên, một nhóm các ký tự hoặc một số thứ khác. Có một vài máy (IBM iSeries là máy duy nhất vẫn được tạo, tôi tin) là "dựa trên năng lực" và có cách để phân biệt con trỏ với dữ liệu khác (và, ngoài ra, có một số đầu mối thời gian chạy theo kiểu của dữ liệu được giải quyết), nhưng đây là những ngoại lệ nhất định. –

Các vấn đề liên quan