Указатели на статически размещенные объекты - PullRequest
6 голосов
/ 13 июля 2010

Я пытаюсь понять, как работают указатели на статически размещенные объекты и где они могут работать неправильно.

Я написал этот код:

int* pinf = NULL;
for (int i = 0; i<1;i++) {
    int inf = 4;
    pinf = &inf;
}

cout<<"inf"<< (*pinf)<<endl;

Я был удивлен, что это сработало, потому чтоЯ думал, что inf исчезнет, ​​когда программа покинет блок, а указатель укажет на то, что больше не существует.Я ожидал ошибку сегментации при попытке доступа к pinf.На каком этапе программы inf умрет?

Ответы [ 8 ]

7 голосов
/ 13 июля 2010

Ваше понимание верно.inf исчезает, когда вы покидаете область действия цикла, и, таким образом, доступ к *pinf приводит к неопределенному поведению.Неопределенное поведение означает, что компилятор и / или программа могут делать все, что угодно, что может привести к сбою или, в данном случае, просто пускаться в пух.Даже если это выходит за рамки pinf, он по-прежнему указывает на используемую область памяти в стеке.Что касается времени выполнения, то с адресом стека все в порядке, и компилятор не потрудился вставить код, чтобы убедиться, что у вас нет доступа к местам за пределами стека.Это было бы слишком дорого для языка, разработанного для скорости.

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

3 голосов
/ 13 июля 2010

Вы используете так называемый Висячий указатель .Это приведет к неопределенному поведению по стандарту C ++.

2 голосов
/ 13 июля 2010

Если вы спрашиваете об этом:

int main() {
  int* pinf = NULL;
  for (int i = 0; i<1;i++){
    int inf = 4;
    pinf = &inf;
  }
  cout<<"inf"<< (*pinf)<<endl;
}

Тогда у вас есть неопределенное поведение. Автоматически размещенный (не статичный) объект inf вышел из области видимости и был условно уничтожен при доступе к нему через указатель. В этом случае может произойти что угодно, в том числе и «работать».

2 голосов
/ 13 июля 2010

Вероятно, он никогда не умрет, потому что pinf будет указывать на что-то в стеке.

Стеки не часто сокращаются.

Изменитьэто, и вы в значительной степени гарантированно перезаписать все же.

1 голос
/ 13 июля 2010

Память может или не может все еще содержать 4, когда она попадает в вашу строку cout.Это может содержать 4 строго случайно.:)

Перво-наперво: ваша операционная система может обнаруживать, что доступ к памяти сбился только с границ страницы .Итак, если у вас 4к или 8к или 16к или больше.(Проверьте /proc/self/maps в системе Linux однажды, чтобы увидеть структуру памяти процесса; любые адреса в перечисленных диапазонах разрешены, любые вне указанных диапазонов недопустимы. Каждая современная ОС на CPU с защищенной памятью будет поддерживатьаналогичный механизм, поэтому он будет поучителен, даже если вы просто не заинтересованы в Linux. Я просто знаю, что это легко для Linux.) Итак, ОС не может помочь вам, когда ваши данные настолько малы.

Кроме того, ваш int inf = 4; вполне может быть сохранен в сегментах .rodata, .data или .text вашей программы.Статические переменные могут быть вставлены в любой из этих разделов (я понятия не имею, как решает компилятор / компоновщик; я считаю это волшебством), и поэтому они будут действительны в течение всей продолжительности программы.Проверьте size /bin/sh в следующий раз, когда вы находитесь в системе Unix, чтобы понять, сколько данных попадает в какие разделы.(И проверьте readelf(1) слишком много информации. objdump(1), если вы используете более старые системы.)

Если вы измените inf = 4 на inf = i, то хранилище будет выделено настек, и у вас гораздо больше шансов быстро его перезаписать.

1 голос
/ 13 июля 2010

Поведение не определено, но на практике «уничтожение» int - это не что иное, поэтому большинство компиляторов оставляют число в стеке до тех пор, пока что-то еще не придет, чтобы повторно использовать этот конкретный слот.

Некоторые компиляторы могут устанавливать для int значение 0xDEADBEEF (или некоторый такой мусор), когда оно выходит из области видимости в режиме отладки, но это не приведет к сбою cout << ...; он просто напечатает бессмысленное значение.

1 голос
/ 13 июля 2010

Вы не обязательно получите SIGSEGV (ошибка сегментации). inf память, вероятно, выделена в стеке. И область стековой памяти, вероятно, все еще выделена вашему процессу в этот момент, поэтому, вероятно, вы не получаете ошибку сегмента.

0 голосов
/ 13 июля 2010

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

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

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