Я наткнулся на следующую загадку C:
В: Почему следующая программа имеет segfault на IA-64, но нормально работает на IA-32?
int main()
{
int* p;
p = (int*)malloc(sizeof(int));
*p = 10;
return 0;
}
Я знаю, что размер int
на 64-битной машине может отличаться от размера указателя ( int
может быть 32 бита, а указатель может быть 64 бита). Но я не уверен, как это связано с указанной выше программой. Любые идеи?
c
pointers
segmentation-fault
user7
источник
источник
stdlib.h
отсутствие включения?#include stdlib.h
(для malloc)#include <stdlib.h>
, что это прекрасно найти, но не в этом вопрос.sizeof(int) == sizeof(int*)
, например, указатели были возвращены через регистр, отличный отint
s в используемом соглашении о вызовах.malloc()
. GCC говорит:warning: incompatible implicit declaration of built-in function 'malloc'
тоже.Ответы:
Приведение к
int*
маскирует тот факт, что без надлежащего предполагается, что#include
тип возврата . IA-64 имеет, что делает эту проблему очевидной.malloc
int
sizeof(int) < sizeof(int*)
(Также обратите внимание, что из-за неопределенного поведения он все равно может выйти из строя даже на платформе, где
sizeof(int)==sizeof(int*)
выполняется, например, если соглашение о вызовах использовало разные регистры для возврата указателей, чем целые числа)В FAQ по comp.lang.c есть запись, в которой обсуждается, почему приведение возврата из
malloc
никогда не требуется и потенциально плохо .источник
new
в C ++ и всегда компилировать C с помощью компилятора C, а не компилятора C ++.int
если он неизвестенmalloc
)void*
. Но вызывающий код считает, что функция возвращаетint
(поскольку вы решили не сообщать об обратном), поэтому он пытается прочитать возвращаемое значение в соответствии с соглашением о вызовах дляint
. Следовательноp
, не обязательно указывает на выделенную память. Так получилось, что для IA32 это сработало, потому что anint
и avoid*
имеют одинаковый размер и возвращаются одинаково. На IA64 вы получаете неправильное значение.Скорее всего, потому что вы не включаете файл заголовка для
malloc
и, хотя компилятор обычно предупреждает вас об этом, тот факт, что вы явно приводите возвращаемое значение, означает, что вы говорите ему, что знаете, что делаете.Это означает, что компилятор ожидает, что
int
будет возвращен объект , изmalloc
которого он затем преобразуется в указатель. Если они разных размеров, это вас огорчит.Вот почему вы никогда не бросил
malloc
возвращение в С. ,void*
что он возвращает будет неявно преобразован в указатель правильного типа (если вы не включили заголовок в этом случае он , вероятно , был бы предупрежден вас о потенциально небезопасных int- преобразование в указатель).источник
void *
можно неявно преобразовать в любой другой тип указателя.int *p = malloc(sizeof(int))
работает, если правильный прототип находится в области видимости, и терпит неудачу, если нет (потому что тогда предполагается результатint
). С приведением оба будут компилироваться, и последнее приведет к ошибкам, когдаsizeof(int) != sizeof(void *)
.stdlib.h
, компилятор не знает, какmalloc
и его возвращаемый тип. Так что это просто предполагаетсяint
по умолчанию.Вот почему вы никогда не компилируете без предупреждения об отсутствии прототипов.
Приведение необходимо для совместимости с C ++. Есть небольшая причина (читай: здесь нет причин) пропустить ее.
Совместимость с C ++ не всегда нужна, а в некоторых случаях вообще невозможна, но в большинстве случаев это очень легко достигается.
источник