Может ли измениться адрес элемента контейнера?

Рейтинг: 2Ответов: 1Опубликовано: 31.12.2014

Например:

std::map<std::string,std::map<std::string,int>> массив;
std::map<std::string,int>* указатель = &массив["апельсин"];

Если никаких манипуляций с переменной '&массив'; не выполняется, но выполняются манипуляции с 'указатель'.

  1. Может ли измениться адрес, на который ссылается 'указатель'?
  2. Этот адрес будет всегда одним и тем же, даже если приложение будет выполнять многочисленные операции несколько дней? Система не делает никаких оптимизаций памяти с изменением адресов?
  3. Если map 'указатель' будет заполняться очень очень большими данными (гигабайты), адрес всё равно останется тем же?
  4. То есть будет ли этот 'указатель' валидным всегда, при любых настройках оптимизации компилятора, разных ОС?

Ответы

▲ 2Принят

Давайте для начала перепишем это по-нормальному.

typedef std::map<std::string,int> arr;
std::map<arr> array; // массив
arr* pointer = &array["orange"]; // указатель

Согласитесь, куда понятнее и красивее. Кстати, этот код будет компилироваться большинством с++ компиляторов, а Ваш - нет, так как не все компиляторы нормально переносят >> в объявлении. Нужно через пробел писать.

То есть просто получаем указатель на элемент. Тут нужно остановиться и сказать, что лучше использовать итераторы, так как они именно для этого и предназначены. Но на более низком уровне итераторы - это те же указатели, просто компилятор имеет больше информации.

Итератор на элемент map не изменяется, если не трогать сам map. Цитата с cplusplus.com/reference/map/map/operator%5B%5D/

The function accesses an element and returns a reference that can be used to modify its mapped value.

то есть, имея указатель на элемент map, можно модифицировать сам элемент

Concurrently accessing other elements is safe.

Более того, это не будет влиять на то, что другие функции модифицируют другие элементы этого map'а.

If the function inserts a new element, concurrently iterating ranges in the container is not safe.

Но если в исходный map добавить элемент, тут уже указатель может повредиться (а может и нет. В оригинале написано - небезопасно).

Пройдемся по ответам.

  1. В описаной ситуации не будет меняться. В целом переменная "указатель" не будет меняться, так как изменения любого из map на нее не влияют. Даже если map удалить. Указатель - это просто адрес в памяти. А что скрывается за указателем - это уже отдельная история.
  2. Указатель, как я сказал выше, буде точно один и тот же. А вот адрес элемента в map'е может поменяться, если модифицировать родительский map. Операционная система память конечно же оптимизирует, но делает это на уровне физических адресов. А те адреса, с которыми Вы работаете - это виртуальные. Они не изменяются в результате действий ОС. Это же не Java или .NET, но и там подобное сложно сделать - в Java, к примеру, нет указателей:). В .NET есть unsafe операции, там подобное, наверное, можно сделать, но unsafe используют те, кто понимает, что они делают. То есть, по-простому, физически, в микросхеме памяти, данные могут храниться в разных местах, даже на диск могут перемещаться (своппинг). Но операционная система делает это прозрачно для прикладного программиста. Если же опуститься на уровень драйверов, то там не все так просто.
  3. Да, адресс будет один и тот же, до тех пор, пока не модифицируется родительский map.
  4. А вот на этот вопрос, думаю, ответ никто не даст. При изменении настроек компилятора ничего не должно страшного случиться (если случиться, то это бага компилятора и стандартной библиотеки). С разными ОС - тут Вы конечно погорячились. visual studio пока компилирует только для одной ОС. (Да, я знаю, что можно прикрутить gcc, но это другая история.) Но, скорее всего, в обозримом будущем даже на разных ОС ситуция будет стабильной. Главное, чтобы компилятор придерживался стандарта и библиотека также. Вы вполне можете взять/написать стороннюю реализацию map, для которой все вышесказанное будет неверно.