Дамп ядра на типе указателя-приведение (int к double) в C


Я наткнулся на этот фрагмент кода:

void incme(double *p)
{
    *p += 1;
}

int i = 1;
incme((double *)&i);    /* WRONG */

Когда я пытаюсь выполнить его,я получаю дамп ядра. Что не так с этим кодом? Мы не можем ввести приведение указателя int к типу double.

Спасибо.

6 2

6 ответов:

Вы не бросаете int в double, вы бросаете int * в double *. Это небезопасно, если sizeof(double) и sizeof(int) не одно и то же...

Даже если вы сопоставите размеры хранилища, что вы ожидаете получить на выходе? Типы с плавающей запятой и целые числа, как правило, не имеют каких-либо совместимых представлений.

(int) обычно выравнивается до 4 байт на 32-битном оборудовании, в то время как (double) обычно должен быть выровнен по 8 байтам. Если ваш i не находится на 8-байтовом выровненном адресе, вы можете ожидать SIGBUS; Кроме того, если он выделен в стеке, более крупный (double), вероятно, перезапишет фрейм вызова, ведущий к дампу ядра, когда incme() вернется, если предыдущий не убьет его.

Можно привести любой указатель из одного типа в любой другой тип, но это не делает его правильным. В этом случае, если sizeof (double)!= sizeof (int), то строка в "incme", скорее всего, будет записываться в память вне той, которая выделена целому числу "i". Тогда все ставки отменяются.

Существует разница между преобразованием типа и реинтерпретацией памяти .

Ваш код принудительно преобразует указатель типа int * В Тип double *. Это достигается с помощью явного приведения. Само по себе это действие не обязательно приводит к каким-либо проблемам. Итак, предположение, которое вы сделали в названии вашего вопроса, неверно. "Сброс ядра", который вы наблюдаете, не имеет ничего общего с самим броском.

Как только вы выполнили преобразование, вы перейдите к разыменованию результирующего указателя и получите доступ (измените) к памяти, на которую он указывает. Вот тогда-то и возникает настоящая проблема. Код выполняет реинтерпретацию данных : он пытается получить доступ к объекту типа int как к объекту типа double. Это всегда явно незаконно в языке Си. Сбой, который вы наблюдаете, вызван именно этой попыткой переосмысления.

Итак, краткий ответ на ваш вопрос таков: Вы можете определенно бросить указатель, который указывает на объект int типа double *. Но Вы не можете разыменовать результирующий указатель double * и получить доступ к памяти, как если бы она содержала объект double.

На большинстве платформ double составляет 8 байт, а int 4, поэтому никто не может привести int* к double* и использовать его

Один из способов сделать это:

int i = 1;
double d1 = (double)i;
double *d = &d1;
incme(d);