Các phần tử vectơ std :: có được đảm bảo là liền kề không?


111

Câu hỏi của tôi rất đơn giản: các phần tử vectơ std :: có được đảm bảo là liền kề không? Theo thứ tự, tôi có thể sử dụng con trỏ đến phần tử đầu tiên của vectơ std :: dưới dạng mảng C không?

Nếu bộ nhớ của tôi phục vụ tôi tốt, tiêu chuẩn C ++ đã không đảm bảo như vậy. Tuy nhiên, các yêu cầu vectơ std :: rất khó đáp ứng nếu các phần tử không liền nhau.

Ai đó có thể làm rõ điều này?

Thí dụ:

std::vector<int> values;
// ... fill up values

if( !values.empty() )
{
    int *array = &values[0];
    for( int i = 0; i < values.size(); ++i )
    {
        int v = array[i];
        // do something with 'v'
    }
}

Tôi biết rằng bạn sẽ gặp rắc rối nếu bạn đột biến valuesbên trong ifkhối đó . Tôi không biết câu trả lời cho câu hỏi của bạn, vì vậy tôi chỉ để lại một bình luận. :)
Greg D

@Greg: Rắc rối gì - bạn có thể giải thích một chút được không?
Reunanen

Tôi cho rằng ý của ông ấy là việc đẩy các giá trị mới có thể kích hoạt "phân bổ lại" khiến mảng trở nên không hợp lệ.
Martin Cote

Các lệnh gọi thay đổi values, cụ thể là thay đổi kích thước của nó (ví dụ push_back():), có thể dẫn đến việc phân bổ lại vectơ cơ bản làm mất hiệu lực con trỏ được sao chép vào array. Đó là nguyên tắc tương tự đằng sau việc sử dụng vector :: iterator thay vì một con trỏ vào vector. :)
Greg D

1
Vâng, tôi đặt các giá trị của `` xung quanh để cố gắng làm rõ ràng rằng tôi đang nói về bản thân lớp, không phải các giá trị chứa bên trong nó. :) Đặt tên không may và tất cả những điều đó. Tôi không nghĩ rằng nó thực sự là một vấn đề trong trường hợp chung khi câu hỏi này có liên quan - tại sao ai đó lại lấy một con trỏ vào bộ nhớ, sau đó bắt đầu ghi lại bằng vector thay vì sử dụng con trỏ? Phần bên dưới.
Greg D

Câu trả lời:


118

Điều này đã bị bỏ sót khỏi tiêu chuẩn C ++ 98 nhưng sau đó được thêm vào như một phần của TR. Tiêu chuẩn C ++ 0x sắp ra mắt tất nhiên sẽ chứa điều này như một yêu cầu.

Từ n2798 (bản nháp của C ++ 0x):

23.2.6 Vectơ mẫu lớp [vector]

1 Vectơ là một vùng chứa trình tự hỗ trợ các trình vòng lặp truy cập ngẫu nhiên. Ngoài ra, nó hỗ trợ (khấu hao) các hoạt động chèn và xóa theo thời gian không đổi ở cuối; chèn và xóa ở giữa mất thời gian tuyến tính. Quản lý lưu trữ được xử lý tự động, mặc dù có thể đưa ra các gợi ý để cải thiện hiệu quả. Các phần tử của vectơ được lưu trữ liền kề, có nghĩa là nếu v là vectơ trong đó T là kiểu nào đó không phải là bool, thì nó tuân theo danh tính & v [n] == & v [0] + n với mọi 0 <= n <v .kích thước().


3
Điều này cũng được nêu trong ISO 14882, Ấn bản lần 2: Mục 23.2.4 [lib.vector]: "Các phần tử của vectơ được lưu trữ liền kề, có nghĩa là nếu v là vectơ <T, Allocator> trong đó T là một số kiểu khác bool, thì nó tuân theo danh tính & v [n] == & v [0] + n cho mọi 0 <= n <v.size (). "
Mike Caron

4
so s, TR, TC, :) Trên thực tế, C ++ 03 còn được gọi là C ++ 98-TC1 (mô hình kỹ thuật) từ những gì tôi đọc
Johannes Schaub - litb

2
Vectơ của vectơ thì sao? Vectơ İnner nằm ngay sau vectơ trong nhóm cuối cùng?
huseyin tugrul buyukisik

1
@huseyin tugrul buyukisik bạn đã tìm hiểu câu trả lời cho điều này chưa? Tôi cũng đang tự hỏi điều này hoạt động như thế nào
David Doria

1
@huseyin tugrul buyukisik Tất nhiên là đúng, nhưng các trường hợp tiếp theo std::vectorlại liền nhau. Ví dụ: trong std::vector<std::vector<int>> vcác phần tử v[0], v[1]... được lưu trữ sau đó trong bộ nhớ, nhưng phần tử v[0].back()v[1].front()không được đảm bảo là.
jarzec

27

Như các câu trả lời khác đã chỉ ra, nội dung của một vectơ được đảm bảo là liên tục (ngoại trừ tính chất kỳ lạ của bool).

Nhận xét mà tôi muốn thêm, là nếu bạn thực hiện việc chèn hoặc xóa trên vectơ, điều này có thể khiến vectơ phân bổ lại bộ nhớ của nó, thì bạn sẽ khiến tất cả các con trỏ và trình vòng lặp đã lưu của mình bị vô hiệu.


1
Các phần tử sẽ vẫn được lưu trữ trong một khối bộ nhớ liền kề, nó sẽ chỉ ở một nơi khác. Câu hỏi đặc biệt là về sự tiếp giáp.
Dima

2
Nhưng các con trỏ và trình vòng lặp hiện có sẽ bị vô hiệu.
Bill Lynch

Điểm tốt. Bạn nên đưa điều đó vào câu trả lời của mình để làm rõ ý bạn.
Dima

nhất hữu ích câu trả lời cho tôi
CoffeDeveloper

Bây giờ tôi biết tại sao chương trình của tôi mặc định ngày hôm qua, khi tôi lặp lại nó trong một vòng lặp kép loại bỏ các phần tử nhất định :) Cảm ơn!
user2891462,

9

Trên thực tế, tiêu chuẩn đảm bảo rằng a vectorlà liên tục trong bộ nhớ và &a[0]có thể được truyền cho một Chàm mong đợi một mảng.

Ngoại lệ đối với quy tắc này là quy tắc vector<bool>chỉ sử dụng một bit cho mỗi booldo đó mặc dù nó có bộ nhớ liên tục, nó không thể được sử dụng như một bool*(điều này được nhiều người coi là tối ưu hóa sai và một sai lầm).

BTW, tại sao bạn không sử dụng trình lặp? Đó là những gì họ dành cho.


1
> BTW, tại sao bạn không sử dụng trình lặp? Đó là những gì họ dành cho. Có thể anh ấy đã đọc bài báo mới của Alexanrescu về chủ đề: boostcon.com/site-media/var/sphene/sphwiki/attachment/2009/05/…
Nemanja Trifunovic

Cảm ơn vì liên kết, tôi sẽ đưa nó vào danh sách đọc của tôi (tôi cố gắng không bỏ lỡ các bài viết của Alexandresu)
Motti

Mwahaha, mọi người dường như đang nói về bài thuyết trình đó mấy ngày nay. Xem này, cuộc thảo luận vẫn còn nóng hổi về nó: groups.google.com/group/comp.lang.c++.moderated/browse_thread/…
Johannes Schaub - litb

Nếu bạn đọc kỹ, bài viết của Alexandrescu không thực sự nói "Không sử dụng trình lặp trong C ++", nó nói "Kiểm tra D". Cách tiếp cận mà ông mô tả trong bài báo đó rất giống với bất kỳ ngôn ngữ và khuôn khổ nào hiện có đã hấp thụ di sản chức năng (Danh sách, Đề án, Haskell) và tôi thực sự nghi ngờ liệu cú pháp dựa trên C khác có phải là điểm khởi đầu lý tưởng để tốt hơn xử lý danh sách. Năm ngoái, một thời gian ngắn tôi đã cố gắng thuyết phục anh ấy chuyển những tài năng đáng kể của mình sang cải thiện một ngôn ngữ đã có tiếng như C #, nhưng tôi sợ rằng không thành công! :)
Daniel Earwicker

6

Như những người khác đã nói, vectornội bộ sử dụng một mảng đối tượng liền kề. Các con trỏ vào mảng đó phải được coi là không hợp lệ bất cứ khi nào bất kỳ hàm thành viên không phải const nào được gọi là IIRC.

Tuy nhiên, vẫn có một ngoại lệ !!

vector<bool>có một triển khai chuyên biệt được thiết kế để tiết kiệm không gian, để mỗi bool chỉ sử dụng một bit. Mảng cơ bản không phải là một mảng liền kề của bool và mảng số học trên vector<bool>không hoạt động như mong vector<T>muốn.

(Tôi cho rằng điều này cũng có thể đúng với bất kỳ chuyên môn hóa nào của vectơ, vì chúng ta luôn có thể triển khai một chuyên ngành mới. Tuy nhiên, std::vector<bool>là chuyên môn hóa tiêu chuẩn duy nhất, sai lầm, dựa trên đó số học con trỏ đơn giản sẽ không hoạt động.)


Người dùng không được phép chuyên biệt hóa std::vectorvà tất cả các vectơ khác được yêu cầu sử dụng bộ nhớ liền kề. Do đó, std::vector<bool>(may mắn thay) là vectơ tiêu chuẩn duy nhất kỳ lạ. (Tôi mạnh mẽ của dư luận rằng chuyên môn hóa này cần được phản đối và thay thế bằng một ví dụ std::dynamic_bitsetvới nhiều tính năng tương tự Nó không phải là một cấu trúc dữ liệu xấu, nó chỉ là không phải là một vectơ..)
Arne Vogel

3

Tôi tìm thấy chủ đề này vì tôi có một trường hợp sử dụng trong đó các vectơ sử dụng bộ nhớ liền kề là một lợi thế.

Tôi đang học cách sử dụng các đối tượng bộ đệm đỉnh trong OpenGL. Tôi đã tạo một lớp wrapper để chứa logic bộ đệm, vì vậy tất cả những gì tôi cần làm là chuyển một mảng float và một vài giá trị cấu hình để tạo bộ đệm. Tôi muốn có thể tạo bộ đệm từ một hàm dựa trên đầu vào của người dùng, vì vậy độ dài không được biết tại thời điểm biên dịch. Làm điều gì đó như thế này sẽ là giải pháp dễ dàng nhất:

void generate(std::vector<float> v)
{
  float f = generate_next_float();
  v.push_back(f);
}

Bây giờ tôi có thể chuyển float của vector dưới dạng một mảng cho các hàm liên quan đến bộ đệm của OpenGL. Điều này cũng loại bỏ sự cần thiết của sizeof để xác định độ dài của mảng.

Điều này tốt hơn nhiều so với việc phân bổ một mảng lớn để lưu trữ các phao và hy vọng tôi đã làm cho nó đủ lớn hoặc tạo mảng động của riêng tôi với bộ nhớ liền kề.


2
chức năng này không có ý nghĩa gì đối với tôi. ý của bạn là chuyển một tham chiếu hay một con trỏ tới vhơn là vchính nó? bởi vì việc truyền vmột mình sẽ tạo ra một bản sao được tạo bên trong hàm, bản sao này sẽ không còn tồn tại sau khi hàm kết thúc. Vì vậy, bạn đang đẩy một cái gì đó lên vector chỉ để xóa vector khi hàm kết thúc.
johnbakers

1

cplusplus.com:

Vùng chứa vectơ được thực hiện dưới dạng mảng động; Cũng giống như các mảng thông thường, vùng chứa vectơ có các phần tử của chúng được lưu trữ ở các vị trí lưu trữ liền kề, có nghĩa là các phần tử của chúng có thể được truy cập không chỉ bằng cách sử dụng trình vòng lặp mà còn sử dụng hiệu số trên các con trỏ thông thường đến các phần tử.


1

Có, các phần tử của vectơ std :: được đảm bảo là liền kề.


Đúng. Tôi đoán tôi sử dụng quá nhiều trong số đó :)
Benoît
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.