использовать нераспределенную память без ошибок? - PullRequest
8 голосов
/ 30 ноября 2010

Почему это работает?

#include <iostream>
using namespace std;

int main() {
    float* tab[3];

    int i = 0;
    while(i < 3) {
        tab[i] = new float[3-i];
        i++;
    }

    cout << tab[2][7] << endl;
    tab[2][7] = 6.87;
    cout << tab[2][7] << endl;

    i = 0;
    while(i < 3)
        delete[] tab[i];
}

, а эта не работает?

#include <iostream>
using namespace std;

int main() {
    float* tab = new float[3];

    cout << tab[7] << endl;
    tab[7] = 6.87;
    cout << tab[7] << endl;

    delete[] tab;
}

Я пробовал обе программы на Win XP с MS VS 2008, обе скомпилированы без ошибок ипервый работал без ошибок.Во втором появилось окно с сообщением об ошибке, однако я не могу вспомнить его и не могу воспроизвести (в настоящее время нет доступа к Windows).

Я пробовал их также в Linux (Kubuntu 10.10 с предварительно скомпилированным пакетом ядра).версия 2.6.35.23.25) с g ++ и оба компилируются и запускаются без ошибок.

Почему?Разве не должно быть всплывающих окон с чем-то вроде «Неправильный доступ к нераспределенной памяти»?

Я знаю, что это должно (и, к счастью, делает) компилироваться без ошибок, но я думал, что это не должно работать безих ... А почему второй пример делает ошибки на Windows, а не на Linux?

Ответы [ 5 ]

9 голосов
/ 30 ноября 2010

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

Программа может сразу аварийно завершить работу, она может работать некоторое время, а затем завершаться сбоем позже, она может даже работать идеально.

Доступ к памяти, которой вы не владеете, всегда является ошибкой программирования. Не думайте о появлении правильной операции как о «иногда она работает», думайте о ней как «мне действительно не повезло, и моя ошибка не появляется быстро».

7 голосов
/ 30 ноября 2010

Хотя другие ответы, за исключением ответов Марка, не являются неправильными, они также не совсем верны. То, что вы делаете, получая доступ к данным после окончания того, что вы явно выделяли в своей программе, вызывает «неопределенное поведение». Может делать все что угодно, в том числе и «работать».

Ответа Стива не было, когда я начал писать это.

5 голосов
/ 30 ноября 2010

Оба они осуществляют доступ к массиву вне пределов - у вас есть массив из 3 указателей с плавающей точкой, и вы получаете доступ к восьмому массиву. Это обязательно приведет к краху.

Однако, в отличие от Java или некоторых других управляемых языков, нет явной проверки границ для каждого доступа к массиву (так как затраты на производительность это непомерно высоки). Таким образом, единственная проверка границ у вас есть ваш MMU. Если вы в конечном итоге получите доступ к памяти, которая не принадлежит вашему приложению, у вас произойдет сбой. Если вы нажмете на память, которая не выделена, но все же является частью вашего процесса (например, это может быть защитное слово), это произойдет незаметно. Это отличный рецепт для очень трудно выявляемых ошибок.

Проверка границ является ключевым. Делай это, когда можешь.

1 голос
/ 30 ноября 2010

В первом примере вкладка [2] имеет значение, которое указывает на действительную память.вкладка [2] +7 не выделена, но может быть.Нет ошибки сегмента.

Во второй вкладке [7] нет значения ... это случайные биты (возможно, нули, или 0xDEADBEEF, или просто любое значение, которое было там последним).Это почти наверняка не указывает на память, которая является допустимой для доступа этого приложения.Таким образом: бум.

0 голосов
/ 30 ноября 2010

Защита доступа к памяти не очень тонкая.Когда вы выделяете некоторую память, вы получаете целую страницу памяти, выделенной для вашей программы.Когда вы пытаетесь получить доступ к этой дополнительной памяти, это, скорее всего, будет успешным, но вы также можете использовать и другую память, выделенную вашей программе.

Вот почему переполнение буфера работает как атака.Во многих случаях предсказуемо, для чего используется эта дополнительная память после использования вашего массива.Если я смогу контролировать то, что вы положили туда, я могу перезаписать данные, которые вы не хотите, чтобы я перезаписывал.Если я могу перезаписать ваш стек вызовов, то я могу выполнить любой код, который я хочу, в контексте вашего процесса.Если это служба, работающая от имени администратора, то у меня есть локальное повышение привилегий.Если это какая-то служба, ориентированная на Интернет, то у меня атака на удаленное выполнение.

Лучше всего работать с более надежными структурами, такими как std :: vector, если у вас нет особой цели использования массивов.,(И даже тогда вы могли бы уйти с векторами ).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...