Указатели на статически выделенные объекты

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

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

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

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

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

6
задан John Kugelman supports Monica 13 July 2010 в 12:40
поделиться

8 ответов

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

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

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

7
ответ дан 8 December 2019 в 13:43
поделиться

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

1
ответ дан 8 December 2019 в 13:43
поделиться

Вероятно, он никогда не умрет, потому что pinf укажит на что-то на стеке.

Стеки не часто сжимаются.

Измените его, и вам в значительной степени будет гарантирована перезапись.

2
ответ дан 8 December 2019 в 13:43
поделиться

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

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

1
ответ дан 8 December 2019 в 13:43
поделиться

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

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

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

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

1
ответ дан 8 December 2019 в 13:43
поделиться

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

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

0
ответ дан 8 December 2019 в 13:43
поделиться

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

3
ответ дан 8 December 2019 в 13:43
поделиться

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

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

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

2
ответ дан 8 December 2019 в 13:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: