Подтвердить что ты не робот

Что это значит?: * (int32 *) 0 = 0;

В следующем фрагменте кода, что означает *(int32 *) 0 = 0;?

void
function (void)
{
  ...

  for (;;)
     *(int32 *) 0 = 0;     /* What does this line do? */
}

Несколько примечаний:

  • Код кажется недоступным, поскольку перед этим конкретным фрагментом кода есть инструкция exit.
  • int32 is typedef 'ed, но вам не следует беспокоиться об этом.
  • Этот фрагмент кода выполняется из языка исполнения в компиляторе, для всех, кого это интересует.
4b9b3361

Ответ 1

Код выполняет следующие действия:

   for (;;) // while(true)
     *(int32 *) 0 = 0; // Treat 0 as an address, de-reference the 0 address and try and store 0 into it.

Это должно быть segfault, null-указатель de-reference.

ИЗМЕНИТЬ

Скомпилирован и запущен для получения дополнительной информации:

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

int main(void){
  *(int32_t *) 0 = 0;
  printf("done\n");
  return 0;
}

gcc -g null.c; ./a.out

Program received signal SIGSEGV, Segmentation fault.
0x00000000004004cd in main () at null.c:7
7         *(int32_t *) 0 = 0;

Ответ 2

Поскольку OP указывает, что код был написан опытными инженерами-компиляторами, возможно, это и есть цель кода:

  • *(int32 *) 0 = 0; распознается этой конкретной реализацией C как код, который вызывает поведение, не определенное стандартом C и известное этой реализации, как незаконное.
  • for (;;) дополнительно указывает, что этот код никогда не выходил.
  • Инженеры-компиляторы знают, что оптимизатор распознает этот код и выводит, что он может быть "оптимизирован", потому что любой программе, которая достигает этого кода, разрешено иметь какое-либо поведение, поэтому оптимизатор может выбрать, чтобы дать ему поведение как если код никогда не был достигнут. 1

Такое рассуждение возможно только, если у вас есть конкретные знания о внутренней работе реализации C. Это то, что инженер-компилятор может включать в специальные заголовки для реализации C, возможно, чтобы отметить, что определенный код (например, код после вызова abort) никогда не достигается. Он никогда не должен использоваться в обычном программировании.


1 Например, рассмотрим этот код:

if (a)
    for (;;)
        *(int 32 *) 0 = 0;
else
    foo();

Компилятор может распознать, что в предложении then разрешено иметь какое-либо поведение. Поэтому компилятор может свободно выбирать, какое поведение он имеет. Для простоты он выбирает, что он имеет такое же поведение, как foo();. Затем код становится:

if (a)
    foo();
else
    foo();

и может быть дополнительно упрощена до:

foo();

Ответ 3

На самом деле этот код seg-faulting не объясняет, почему он существует =)

Я думаю, что из-за времени выполнения некоторых MCU.. и потому он существует, потому что, если выполнение программы достигнет этой точки, такая инструкция либо инициирует программное обеспечение reset для MCU, поэтому программа будет перезапущена (что является обычной практикой во встроенной разработке) ИЛИ, если MCU настроен с помощью аппаратного сторожевого таймера, принудительно перезагрузите MCU из-за аппаратного сторожевого таймера и бесконечного цикла.

Основная цель таких построений - вызвать прерывание, которое может обрабатываться либо ОС, либо аппаратным обеспечением для инициирования определенных действий.

Зная, что его x86 будет зависеть от режима CPU... в реальном режиме ничего действительно не произойдет мгновенно, если нет сторожевого пса, по адресу 0 есть адрес обработчика 'divide by 0', поэтому, если он старой MS-DOS или встроенной среды выполнения x86, он изменит адрес обработчика "Разделить на 0" на 0, поэтому, как только это произойдет, и это прерывание не замаскировано, CPU перейдет в положение 0: 0 и, вероятно, просто перезапустится, потому что незаконной инструкции.. если она защищена или VM x86-код, то это способ уведомлять ОС или любого другого руководителя о том, что есть проблема во время выполнения, а программное обеспечение должно быть "убито" извне.

Ответ 5

Это бесконечный цикл поведения undefined (разыменование нулевого указателя). Вероятно, он сбой с segfault на * n * x или Нарушение доступа в Windows.

Ответ 6

Комментарий Майка довольно корректен: он сохраняет нулевое значение VALUE в ADDRESS 0.

Что будет сбоем на большинстве машин.

Ответ 7

Оригинальный IBM PC сохранил таблицу векторов прерываний в самой нижней 1 KiB памяти. Следовательно, на самом деле запись 32-битного значения в адрес 0 на такой архитектуре приведет к перезаписи адреса для INT 00h. INT 00h выглядит не используемым на ПК.

В основном что-либо современное (что означает, что в x86/x86-64 parlace все работает в защищенном или длинном режиме), он вызывает ошибку сегментации, если вы не находитесь в кольце 0 (режим ядра), потому что вы выходите за пределы своего процесса ' разрешенный диапазон разнесения адреса.

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

Ответ 8

Может быть, компилятор не знает, что exit() не возвращается, но он знает, что эта конструкция не возвращается.