«Средство анализа адресов: использование стека после области действия» при попытке доступа к элементу вектора указателей

#c #address-sanitizer

Вопрос:

Почему следующий код

 #include lt;iostreamgt; #include lt;vectorgt;  typedef struct Number {  int number = 15; } Number;  int main() {  std::vectorlt;Number*gt; nums(5);  for (size_t i = 0; i lt; nums.size();   i) {  Number num;  nums[i] = amp;num;  }   std::cout lt;lt; nums[1]-gt;number lt;lt; "n";   return 0; }  

триггер «AddressSanitizer: использование стека после области действия», но когда я комментирую строку 15: std::cout lt;lt; nums[5]-gt;number lt;lt; "n"; он хорошо компилируется? Как это исправить?

Команда компиляции: clang main.cpp -fsanitize=address,undefined -fno-sanitize-recover=all -std=c 17 -O2 -Wall -Werror -Wsign-compare -g -o debug_solution amp;amp; ./debug_solution

Комментарии:

1. Возьмите свой учебник по C и узнайте об new операторе. И примерно std::shared_ptr , пока ты этим занимаешься.

2. Вы сохраняете в векторе адрес num : каков срок службы num ?

3. Не связано: В C вы определяете структуры как struct Number { ... };

4. Примечание: Если вместо этого вы распечатаете адреса , в которых они хранятся nums , велика вероятность того, что вы обнаружите, что все они одинаковы. Должен дать вам несколько подсказок о том, что здесь пошло не так.

5. Спасибо всем! Я на пути к тому, чтобы стать лордом c .

Ответ №1:

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

 std::vectorlt;Number*gt; nums(5); for (size_t i = 0; i lt; nums.size();   i) {  Number num;  nums[i] = amp;num;  // num goes out of scope here and num[i] has a dangling pointer  // to an invalid object }  std::cout lt;lt; nums[1]-gt;number lt;lt; "n";  

В этом игрушечном примере я вообще не вижу причин использовать указатели, и исправление состояло бы в том, чтобы просто использовать std::vectorlt;Numbergt; :

 std::vectorlt;Numbergt; nums(5);  // per comment by @user4581301 // this loop is not really needed, as the constuctor will // default-construct the elements for you for (size_t i = 0; i lt; nums.size();   i) {  Number num;  nums[i] = num; }  std::cout lt;lt; nums[1].number lt;lt; "n";  

Если вам действительно нужно динамическое выделение памяти в куче, вам следует рассмотреть возможность использования std::vectorlt;std::unique_ptrlt;Numbergt;gt; или std::vectorlt;std::shared_ptrlt;Numbergt;gt; в зависимости от того, нужно ли совместно использовать объект между несколькими компонентами.

Комментарии:

1. В случае без указателей вообще нет необходимости в цикле. std::vectorlt;Numbergt; nums(5); созданы все необходимые значения по умолчанию Number .

2. Очень верно, добавлено примечание.

3. Большое спасибо! На самом деле это просто игрушечный пример, истинный код с той же проблемой составляет около 300 строк (идеальная реализация хэша).