Ниже я готовлю небольшой фрагмент кода, который представляет мой реальный код с ошибками.
byte count = 100;
byte* pointer = &count;
(*(ushort*)pointer) = (ushort)byte.MaxValue;
Я нашел ошибку в третьей строке и исправил ее. *pointer = byte.MaxValue;
Но я точно не понимаю, почему код ошибки иногда запускается без ошибки, а иногда завершается с ошибкой:
malloc.c:2379: sysmalloc: Assertion `(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.
Я вижу, что инструкция (*(ushort*)pointer) = (ushort)byte.MaxValue;
записывает два байта в память, и вторая ячейка памяти здесь может использоваться для других переменных текущего процесса или других процессов.
Почему иногда ошибку не выдает?
Может быть, он работает нормально, если второй байт «свободен»?
Или, может быть, он выдает только в том случае, если второй байт принадлежит другому процессу и относится к другому адресному пространству?
(*(ushort*)pointer) = (ushort)byte.MaxValue;
пытается записать 2 байта с помощью указателя, который на самом деле указывает на 1 байт.
Результатом является неопределенное поведение.
Пример здесь для неопределенного поведения (в разделе 23.5 Преобразования указателей, 23.5.1 Общие сведения) демонстрирует аналогичный случай:
unsafe static void M()
{
char c = 'A';
char* pc = &c;
void* pv = pc;
int* pi = (int*)pv; // pretend a 16-bit char is a 32-bit int
int i = *pi; // read 32-bit int; undefined
*pi = 123456; // write 32-bit int; undefined
}
В случаях неопределенного поведения [почти] может случиться что угодно, включая (но не обязательно) получение ошибки. Может показаться, что программа работает так, как ожидалось, но на это не стоит рассчитывать.
Касательно:
Или, может быть, он выдает, только если второй байт принадлежит другому процессу и это относится к другому адресному пространству?
Каждый процесс имеет свое собственное адресное пространство (в Windows см.: Виртуальное адресное пространство), и такой процесс не может получить доступ к адресному пространству другого процесса.
Так что это не имеет отношения к вашему случаю (в некоторых случаях существуют специальные API Win32 для доступа к другому адресному пространству - например, WriteProcessMemory, но это не то, что вы можете тривиально сделать, как в опубликованном вами коде).
небольшое уточнение. Разве в неуправляемом коде невозможно получить доступ к ячейке памяти адресного пространства другого процесса? Я не могу присвоить указателю произвольный адрес (который будет «внешним» адресом) и прочитать/записать из него значение?
@vitm, да, таким образом невозможно. ОС позволяет вам получить доступ только к текущему адресному пространству памяти процесса (в некоторых случаях существуют специальные API Win32 для доступа к другому адресному пространству, но это не то, что вы можете сделать тривиально).
Добавил дополнительную информацию об этом в свой ответ.
@wohlstad, а технически, если я укажу из кода на ячейку случайной памяти другого процесса, мой процесс выйдет из строя? А из примера кода этот второй байт может относиться к другому адресному пространству?
Нет, вы просто не можете этого сделать технически. Любой адрес, который вы можете иметь в указателе, всегда находится в вашем адресном пространстве. ОС сохраняет полное разделение между адресными пространствами. Адрес X в одном процессе полностью отличается от того же адреса X в другом (даже если X — тот же номер).
@vitm Поиск термина «виртуальная память», который вы описываете, совершенно невозможен в обычном коде пользовательского режима (не в ядре/драйверах)
@vitm это поможет вам начать работу, если вы используете Windows: Виртуальное адресное пространство (управление памятью.
Зачем вы вообще возитесь с необработанными указателями, каков ваш вариант использования?