2012-10-15 40 views
5

Định lý nói rằng chúng ta không thể khởi tạo một mảng làm bản sao của mảng khác. Nhưng chúng ta có thể khởi tạo con trỏ như một bản sao của con trỏ trỏ đến phần tử đầu tiên của mảng khác:Tại sao chúng ta có thể sao chép trực tiếp các con trỏ, nhưng chúng ta không thể trực tiếp sao chép mảng trong C++

int a[] = {0, 1, 2}; 
int a2[] = a;       //error 
int *a3 = a;        //OK 

Tại sao int a2[] = a; là một lỗi?

+11

Bởi vì: ** Mảng là * NOT * con trỏ!** –

+3

Câu chuyện dài ngắn, sử dụng 'std :: array' và sao chép-khởi tạo, vv tất cả các bạn muốn. – chris

+0

@Als Điều đó không giải thích tại sao không có cú pháp (đơn giản, gán-vận hành dựa trên) để sao chép-khởi tạo một mảng. – jogojapan

Trả lời

12

Không thể chỉ định hoặc khởi tạo mảng từ đối tượng mảng khác trong C++ vì chúng không thể trong C và chúng không thể trong C vì lý do lịch sử không thực sự liên quan nữa.

Trong rất sớm proto-C, sẽ có một số nhầm lẫn cho dù một bài tập như int a[] = {0}; int b[] = {0}; a = b; nên sao chép nội dung của mảng b-a, hoặc tái ghế tên a để chỉ b. Tương tự như vậy với việc khởi tạo, liệu a có phải là bản sao của b hoặc bí danh hay không. Sự mơ hồ này đã không tồn tại trong 40 năm: nó nhanh chóng trở nên rõ ràng rằng nếu nó được cho phép thì ý nghĩa hợp lý trong C (và C++) sẽ được sao chép, nhưng các mảng trong C chưa bao giờ được đưa vào "các loại giá trị.

Không có lý do kỹ thuật nào khiến nó không thể, và ví dụ bạn có thể gán kiểu cấu trúc có mảng làm thành viên dữ liệu. Tiêu chuẩn đơn giản không xác định mã của bạn là đúng C++.

Hành vi của con trỏ không liên quan trực tiếp đến điều này. Khởi tạo một con trỏ (trỏ đến phần tử đầu tiên của một mảng) là một hoạt động khác với việc khởi tạo một mảng và nội dung của nó, và ngôn ngữ cho phép những thứ khác nhau trên RHS.

+0

+1, cảm ơn lòng tốt cho câu trả lời đúng này. – avakar

0

Có thể gán một mảng cho mảng khác - nhưng chỉ bằng cách sao chép dữ liệu cơ bản của mảng đầu tiên vào mảng thứ hai (sử dụng vòng lặp, ví dụ). Các dòng mã trong câu hỏi của bạn:

int *a3 = a; 

chỉ đơn giản là gán con trỏ gọi a3 để trỏ đến địa chỉ của phần tử đầu tiên của mảng a, nhưng nó không làm thay đổi dữ liệu cơ bản mà hai biến những tham khảo (trong trường hợp này là số nguyên).

Ngoài ra, hãy kiểm tra this để làm rõ mối quan hệ giữa mảng và con trỏ.

+0

'a' không phải là một con trỏ. Nó phân rã thành một con trỏ đến phần tử đầu tiên, đó là những gì được gán cho 'a3'. Dừng "mảng này là một con trỏ" điên rồ. – avakar

+0

Điểm tốt. Đã chỉnh sửa để giải quyết vấn đề này. – nickolayratchev

0

Một mảng không chỉ là một con trỏ, trong ví dụ của bạn, mảng a[] biểu thị rằng 3 địa chỉ bộ nhớ liên tiếp có kích thước nguyên đã được đặt trước trong bộ nhớ. Nếu bạn cố gắng 'sao chép' mảng đó, nó cũng sẽ phải đặt trước 3 địa chỉ bộ nhớ liên tiếp có kích thước nguyên và sao chép 0 1 2 cho chúng. Toán tử gán cho nguyên thủy sẽ không được mong đợi thực hiện nhiều công việc này.

Tuy nhiên, khi bạn tạo con trỏ, bạn chỉ cần đặt tên cho vị trí là a[].

+0

"Toán tử gán cho nguyên thủy sẽ không được mong đợi thực hiện nhiều công việc này" - Tôi không nghĩ rằng điều này là khá phù hợp, vì mảng * không phải là * kiểu nguyên thủy (chúng là tổng hợp). Dòng suy nghĩ đó có lẽ sẽ giúp giải thích tại sao không ai bận tâm đến việc tạo ra các mảng được gán trong C, mặc dù vậy. Structs có thể gán được mặc dù đòi hỏi phải sao chép nhiều, vì vậy các mảng bị bỏ lại ở giữa. –

0

nếu bạn làm điều này

int *a3 = a; 

Bạn không sao chép các giá trị, chỉ là memoryadress, được gọi là con trỏ

Nếu bạn wont để sao chép các giá trị của một mảng, bạn phải xác định một Sao chép Constructor.

http://en.wikipedia.org/wiki/Copy_constructor

+2

Phát biểu cuối cùng của bạn ... sai đáng kinh ngạc. – jogojapan

+0

có: D, tôi nên xóa nó – user1572130

0

Hãy xem xét theo cách khác. Mảng hoạt động trên phân bổ tĩnh trong C/C++. Vì vậy, chúng ta phải chắc chắn rằng trình biên dịch có thể tìm ra kích thước của mảng tại thời gian biên dịch chính nó, đó là điều kiện bắt buộc với mảng vì rất nhiều lý do chết tiệt. Vì vậy, bất cứ khi nào trình biên dịch gặp ký hiệu '[]', nó hy vọng rằng kích thước bộ nhớ (cần phải được cấp phát khi chạy) có thể được tìm ra từ chính mã.

Khi xác định một mảng trong C/C++, bộ nhớ cho mảng được cấp phát (Khi tôi nói mảng, tôi có nghĩa là các ô nơi các phần tử mảng sẽ được lưu trữ). Bây giờ, khi bạn nói giống như

int a[] = {3,4,5,6}; 

Nó sẽ phân bổ một số bộ nhớ, nói 16 trình biên dịch cho 4 phần tử, có thể được trình biên dịch tìm ra trong thời gian biên dịch.

Bây giờ khi bạn nói

int a2[] = a; 

Trình biên dịch thấy các ký hiệu mảng '[]', nhưng không thể xác định có bao nhiêu bộ nhớ cần được phân bổ cho các tế bào mảng, mang đến cho bạn một lỗi.

Khi chúng ta nói về một mảng, nó luôn luôn là một ô không quan tâm được trỏ bởi một con trỏ liên tục, trong khi chúng ta nói về một con trỏ, nó có thể tồn tại mà không cần tham chiếu bất cứ thứ gì. Hy vọng nó giúp.

+1

Bạn đang nghĩ về cách trình biên dịch hoạt động không chính xác. Kiểu 'a' trong đoạn thứ hai của bạn là' int [4] ', do đó không có lý do gì khiến trình biên dịch không thể suy ra mảng bị ràng buộc cho' a2'. Hơn nữa, bạn không thể sao chép mảng ngay cả khi bạn cung cấp giới hạn rõ ràng, vì vậy bạn đang thiếu điểm của câu hỏi. – avakar

+0

@avakar Đồng ý. Điểm mà tôi đang cố gắng tạo ra là chúng ta không thể xác định rõ vị trí của một mảng mà chúng ta đang làm khi chúng ta nói int a1 [] = a, nó là tối đa trình biên dịch quyết định. Vui lòng sửa tôi nếu tôi sai – Vivek

+0

Chết tiệt, tôi có thực sự viết "ý tưởng của bạn" không? Xin lỗi vì điều đó ... – avakar

-1

Khi bạn muốn khai báo một mảng mà sẽ được cấp phát trên stack, nó là cần thiết cho trình biên dịch biết số lượng của các yếu tố, đó là lý do tại sao bạn phải khai báo:

int a[] = {x,y,etc..}; 

hoặc

int a[NUMELEMENTS]; 

Mặc dù có thể trỏ đến một mảng đã tồn tại, bạn không thể tạo một mảng ngăn xếp mới từ một con trỏ tới một mảng đã tồn tại, đó là những gì int a2[]= a; đang cố gắng thực hiện. Trình biên dịch không biết có bao nhiêu phần tử để phân bổ cho a2.

Thông thường, cách tốt nhất để sao chép mảng sang mảng khác là sử dụng std::copy.

+1

Kích thước của mảng được xác định tại thời gian biên dịch. Vì vậy, trình biên dịch về cơ bản biết nó lớn như thế nào (đó là lý do tại sao 'sizeof a' hoạt động). – jogojapan

5

Mảng không phải là con trỏ, vì vậy bạn không thể mong đợi sử dụng chúng như con trỏ mà không gặp sự cố. Hãy truy cập https://blogs.oracle.com/ksplice/entry/the_ksplice_pointer_challenge để biết hoạt động sẽ giúp bạn tìm hiểu sự khác biệt.

Bạn có thể khởi tạo mảng dưới dạng bản sao của mảng khác nếu bạn quấn mảng trong cấu trúc.

struct myarray { 
    int a[3]; 
}; 

Nếu trình biên dịch của bạn cho phép GNU C++ phong cách thiết kế initializers:

myarray a = {a: {0, 1, 2}}; 

này làm một bản sao của các cấu trúc, bao gồm một mảng vào mảng a2 của.

myarray a2 = a; 

Các mảng sẽ có mặt tại nhiều nơi khác nhau trong bộ nhớ:

bool is_different_array = a2.a != a.a; // true 
Các vấn đề liên quan