Сохраняются ли элементы std::vector отдельно в памяти?

Вопрос

Я попытался напечатать количество байтов и адрес памяти элементов, которые находятся в векторе. В результате я вижу адрес памяти и номер байта. Элементы вектора хранятся отдельно? и если да, то почему мой код выдает, что вектор всего 24 байта?

Что я пробовал

#include <iostream>
#include <vector>

int main(){
    std::vector<const char *> colour = {"Microsoft", "Apple", "DELL", "Accer", "Lenovo", "hp"};
    std::cout << "VectorSize : " << sizeof(colour) << "\n" << std::endl;
    for(int i = 0; i < colour.size(); i++){
        std::cout << colour[i] << " is " << sizeof(colour[i]) << " byte at " << &colour[i] << "." << std::endl;
    }
}

Результат:

VectorSize : 24

Microsoft is 8 byte at 0x27b5a7c6220.
Apple is 8 byte at 0x27b5a7c6228.
DELL is 8 byte at 0x27b5a7c6230.
Accer is 8 byte at 0x27b5a7c6238.
Lenovo is 8 byte at 0x27b5a7c6240.

Дополнительное примечание рассмотрите возможность использования std::vector<std::string_view> (чтобы избежать использования необработанных указателей const char* в вашем коде.

Pepijn Kramer 31.07.2023 06:09

«Элементы вектора хранятся отдельно?» Да. "...и если да, то почему мой код выдает мне, что вектор всего 24 байта?" Потому что элементы хранятся отдельно.

user207421 31.07.2023 07:40

По теме: sizeof Vector равен 24?

JaMiT 31.07.2023 08:32
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
90
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Данные в векторе выделяются динамически и хранятся в другой ячейке памяти, они не являются частью вектора, и вектор имеет только указатель на эту память. Выделенная память является непрерывной, поэтому все выделенные байты располагаются рядом друг с другом в памяти.

В вашем случае размер класса std::vector составляет 24 байта, размер дополнительной выделенной памяти где-то еще в памяти составляет 40 байт. Таким образом, всего в памяти вашего компьютера будет выделено 64 байта.

Но расходует ли мой вектор 24 байта или 40 байт?

Meniev 31.07.2023 06:01

С тем, что ты сказал.

Meniev 31.07.2023 06:02

Хорошо, спасибо за ваш ответ. Но я думаю, что это важный момент, который вы только что сказали (64 байта в памяти). Я рекомендую вам поставить его на ваш ответ.

Meniev 31.07.2023 06:09

Без проблем!

Meniev 31.07.2023 06:13

sizeof(...) измеряет размер объекта. Это не связано с количеством элементов, которые имеет std::vector. Напишите colour.size(), чтобы вычислить это. С вектором sizeof(std::vector) == 24 потому, что он содержит указатель на данные, текущее количество элементов и емкость выделенных данных, каждый из которых занимает 8 байтов в вашей архитектуре. В разных реализациях это может быть реализовано по-разному.

Да, как вы можете видеть, расположение указателя каждый раз увеличивается на 8, так как данные вектора хранятся непрерывно.

Сколько байтов использует вектор?

Векторы используют память в двух местах: 1. внутри самого векторного объекта и 2. в куче, где хранятся векторные элементы.

Разработчики компилятора имеют большую свободу в том, как они распределяют эту память. Однако обычно сам векторный объект содержит части, размер которых не меняется. Вот что я нашел в реализации Microsoft:

  1. Указатель на начало данных в куче, т. е. на первый элемент данных.

  2. Указатель на конец данных, т. е. один элемент после последнего элемента данных. Используя этот указатель, capacity() вычисляется как end - first.

  3. Указатель, обозначающий последний использованный элемент, т. е. указатель на элемент, следующий за последним использованным элементом данных. С этим указателем size() вычисляется как last - first.

Элементы данных хранятся в куче, потенциально с другой информацией, специфичной для реализации (или без нее).

Для данного вектора v, предполагая, что в куче ничего, кроме данных, не хранится, количество используемых байтов будет:

sizeof(v) + (v.data() == nullptr ? 0 : v.capacity() * sizeof(*v.data()))

Вектор в исходном вопросе представляет собой вектор указателей, в частности указателей на const char. Вектор инициализируется initializer_list с 6 string literals. Строковые литералы хранятся в статической памяти, а указатели на них — в куче. Предположительно, емкость была выделена на уровне 6, но я считаю, что эта деталь зависит от реализации. Размер каждого элемента равен размеру указателя данных, обычно это sizeof(std::size_t).

Вот короткая программа для подсчета общего количества используемых байтов.

#include <cstddef>
#include <iostream>
#include <vector>
int main()
{
    std::vector<const char*> v = { "Microsoft", "Apple", "DELL", "Accer", "Lenovo", "hp" };
    std::cout << std::boolalpha
        << "sizeof(v)           : " << sizeof(v)
        << "\nv.size()            : " << v.size()
        << "\nv.capactity()       : " << v.capacity()
        << "\nv.data() == nullptr : " << (v.data() == nullptr)
        << "\nv.data()            : " << v.data()
        << "\nsizeof(*v.data())   : " << sizeof(*v.data())
        << "\nsizeof(std::size_t) : " << sizeof(std::size_t)
        << "\nTotal bytes used    : " << sizeof(v) + (v.data() == nullptr ? 0 : v.capacity() * sizeof(*v.data()))
        << "\n\n"
        ;
    return 0;
}

Я запускал MSVC с таргетингом на x64, поэтому мои указатели были по 8 байт каждый. Очевидно, в векторном объекте хранится четвертый элемент, потому что sizeof(v) получилось 32, а не 24.

Прочитав исходный код Microsoft для заголовка <vector>, я смог найти три указателя, упомянутых выше. Они хранятся в объекте с именем _Mypair как _Mypair._Myval2. Однако я не смог найти определение для _Mypair._Myval1, которое, предположительно, является четвертым элементом.

sizeof(v)           : 32
v.size()            : 6
v.capactity()       : 6
v.data() == nullptr : false
v.data()            : 000002952A005030
sizeof(*v.data())   : 8
sizeof(std::size_t) : 8
Total bytes used    : 80

Когда я перекомпилировал, ориентируясь на x86, размер был уменьшен вдвое. Это имеет смысл, потому что размер std::size_t, используемый для указателей, был сокращен до 4 байтов.

sizeof(v)           : 16
v.size()            : 6
v.capactity()       : 6
v.data() == nullptr : false
v.data()            : 00AEC640
sizeof(*v.data())   : 4
sizeof(std::size_t) : 4
Total bytes used    : 40

Другие вопросы по теме