Разыменование указателя на произвольный адрес дает ошибку сегментации - PullRequest
1 голос
/ 01 октября 2019

Я написал простой C-код для указателей. Насколько я понимаю, Pointer - это переменная, которая содержит адрес другой переменной. Например:

 int x = 25; // address - 1024
 int *ptr = &x;
 printf("%d", *ptr); // *ptr will give value at address of x i.e, 25 at 1024 address.

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

#include "stdio.h"
int main()
{
    int *ptr = 25;
    printf("%d", *ptr);
    return 0;
}

Что в этом плохого? Почему переменная-указатель не может вернуть значение по адресу 25? Разве я не могу прочитать байты по этому адресу?

Ответы [ 3 ]

5 голосов
/ 01 октября 2019

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

Раздел 6.5.3.2p4стандарта C в отношении оператора косвенности * говорится следующее:

Унарный оператор * обозначает косвенность. Если операнд указывает на функцию, результатом является обозначение функции;если он указывает на объект, результатом является lvalue, обозначающее объект. Если операнд имеет тип «указатель на тип», результат имеет тип «тип». Если указателю было присвоено недопустимое значение, поведение унарного оператора * не определено.

Как упомянуто в отрывке выше, стандарт C допускает толькодля указателей, указывающих на известные объекты или на динамически выделяемую память (или NULL), а не на произвольные области памяти. Некоторые реализации могут допускать это в определенных ситуациях, но не в целом.

2 голосов
/ 01 октября 2019

Хотя поведение вашей программы не определено в соответствии со стандартом C, ваш код на самом деле правильный в том смысле, что он выполняет именно то, что вы намереваетесь. Он пытается прочитать с адреса памяти 25 и напечатать значение по этому адресу.

Однако в большинстве современных операционных систем, таких как Windows и Linux, программы используют виртуальную память , а не физическую. Память. Поэтому вы, скорее всего, пытаетесь получить доступ к адресу виртуальной памяти, который не сопоставлен с адресом физической памяти. Доступ к несопоставленной ячейке памяти недопустим и вызывает ошибку сегментации.

Поскольку адрес памяти 0 (который обычно записывается в C как 'NULL') обычно резервируется для указания неверного адреса памяти, большинство современных операционных системникогда не отображайте первые несколько килобайт адресов виртуальной памяти на физическую память. Таким образом, ошибка сегментации будет возникать, когда разыменовывается недопустимый указатель NULL (что хорошо, потому что это облегчает обнаружение ошибок).

По этой причине вы можете быть достаточно уверены, что также адрес 25(который очень близок к адресу 0) никогда не сопоставляется с физической памятью и поэтому вызывает ошибку сегментации, если вы пытаетесь получить доступ к этому адресу.

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

Если вы хотите исследовать виртуальное адресное пространство вашего процесса, чтобы найти адреса памяти, которые вы можете прочитать без возникновения ошибки сегментации, вы можетеиспользуйте соответствующий API, предоставляемый вашей операционной системой. В Windows вы можете использовать функцию VirtualQuery () . В Linux вы можете прочитать псевдофайловую систему / proc / self / maps . Сам стандарт C / C ++ не предоставляет никакого способа определения макета адресного пространства виртуальной памяти, поскольку это зависит от операционной системы.

Если вы хотите изучить макет адреса виртуальной памяти других запущенных процессов,затем вы можете использовать функцию VirtualQueryEx () в Windows и читать / proc / [pid] / maps в Linux. Однако, поскольку другие процессы имеют отдельное адресное пространство виртуальной памяти, вы не можете напрямую обращаться к их памяти, но должны использовать функции ReadProcessMemory и WriteProcessMemory в Windows и использовать / proc / [pid] / mem в Linux.

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

Однако, какпрограммист, вы обычно не хотите исследовать адресное пространство виртуальной памяти. Вместо этого вы обычно работаете с памятью, которая была назначена вашей программе операционной системой. Если вы хотите, чтобы операционная система давала вам немного памяти, с которой вы можете читать и записывать по желанию (т.е. без ошибок сегментации), то вы можете просто объявить большой массив символов (байтов) какглобальная переменная, например "char buffer [1024];". Будьте осторожны с объявлением больших массивов в качестве локальных переменных, так как это может вызвать превышение стека . В качестве альтернативы вы можете запросить у операционной системы динамически распределенную память, например, с помощью функции malloc ().

2 голосов
/ 01 октября 2019

Следует учитывать все предупреждения, которые выдает компилятор.

Это утверждение

int *ptr = 25;

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

printf("%d", *ptr);

делается попытка получить доступ к памяти по адресу 25, который не принадлежит вашей программе.

То, что вы имеете в виду, это следующее

#include "stdio.h"

int main( void )
{
    int x = 25;

    int *ptr = &x;

    printf("%d", *ptr);

    return 0;
}

или

#include "stdio.h"
#include <stdlib.h>


int main( void )
{
    int *ptr = malloc( sizeof( int ) );

    *ptr = 25;

    printf("%d", *ptr);

    free( ptr );

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