Чому цей код є стандартним для 64-розрядної архітектури, але добре працює у 32-розрядному?


112

Я натрапив на таку загадку C:

Питання: Чому на IA-64 наведена нижче програма, але вона працює чудово на IA-32?

  int main()
  {
      int* p;
      p = (int*)malloc(sizeof(int));
      *p = 10;
      return 0;
  }

Я знаю, що розмір int64-бітної машини може бути не такий, як розмір вказівника ( intможе бути 32 біта, а вказівник може бути 64 біт). Але я не впевнений, як це стосується вищезгаданої програми. Якісь ідеї?


50
Це щось нерозумне, stdlib.hне включене?
користувач786653

3
Цей код працює на моїй 64-бітної машині. Він навіть компілюється без попереджень, якщо ви #include stdlib.h(для malloc)
mpenkov

1
D'oh! @ user786653 прибив важливий біт. З #include <stdlib.h>, це ідеально знайти, але це не в питанні.

8
@delnan - він не повинен працювати так, хоча він може законно вийти з ладу на платформі, де sizeof(int) == sizeof(int*), наприклад, покажчики повернулися, хоча інший реєстр, який використовується intв конвенції виклику, що використовується.
Flexo

7
У середовищі C99 компілятор повинен попередити вас про приховану заяву malloc(). GCC каже: warning: incompatible implicit declaration of built-in function 'malloc'теж.
Джонатан Леффлер

Відповіді:


130

У ролях int*маскується той факт, що без належного #includeтипу повернення mallocпередбачається без int. IA-64, можливо, sizeof(int) < sizeof(int*)робить цю проблему очевидною.

(Зауважте також, що через невизначену поведінку він все ще може вийти з ладу навіть на платформі, де sizeof(int)==sizeof(int*)справедливо, наприклад, якщо конвенція виклику використовувала різні регістри для повернення покажчиків, ніж цілі числа)

У FAQ. comp.lang.c є запис, в якому mallocйдеться про те, чому передача повернення з ніколи не потрібна і може бути поганою .


5
без належного #include, чому тип повернення malloc вважається int?
користувач7,

11
@WTP - це хороший привід завжди використовувати newв C ++ і завжди компілювати C за допомогою компілятора C, а не компілятора C ++.
Flexo

6
@ user7 - це правила. Будь-який тип повернення вважається таким, intякщо він не відомий
Flexo

2
@vlad - краща ідея завжди оголошувати функції, а не покладатися на неявні декларації саме з цієї причини. (І не кинути повернення з malloc)
Flexo

16
@ user7: "у нас є вказівник p (розміром 64), який вказує на 32 біти пам'яті" - неправильно. Адреса блоку, виділеного malloc, повертається відповідно до конвенції виклику для void*. Але код виклику вважає, що функція повертається int(оскільки ви вирішили не говорити про це інакше), тому намагається прочитати значення повернення згідно з умовами виклику для int. Отже p, не обов'язково вказувати на виділену пам'ять. Так сталося, що я працював для IA32, тому що intа та a void*мають однаковий розмір і повертаються однаково. У IA64 ви отримуєте неправильне значення.
Стів Джессоп

33

Швидше за все, тому що ви не включаєте файл заголовка для, mallocі, хоча компілятор зазвичай попереджає вас про це, той факт, що ви явно викидаєте повернене значення, означає, що ви говорите йому, що ви знаєте, що ви робите.

Це означає, що компілятор очікує intповернення, з mallocякого потім переходить до покажчика. Якщо вони різного розміру, це викличе вам горе.

Ось чому ви ніколи не кидаєте mallocповернення у C. Те, void*що воно повертається, буде неявно перетворене на покажчик правильного типу (якщо ви не включили заголовок, і в цьому випадку воно, ймовірно, попередило б вас про потенційно небезпечну цілість - перетворення вказівника).


Вибачте за звучання наївного, але я завжди вважав, що malloc повертає недійсний покажчик, який можна передати на відповідний тип. Я не програміст на С, і тому я буду вдячний трохи детальніше.
користувач7

5
@ user7: без #include <stdlib.h> компілятор C передбачає, що повернене значення malloc - це int.
сашанг

4
@ user7: Недійсний покажчик може бути переданий , але він не потрібен у C, оскільки void *його можна неявно перетворити на будь-який інший тип вказівника. int *p = malloc(sizeof(int))працює, якщо належний прототип є в області застосування, і не вдається, якщо його немає (тому що вважається, що результат є int). З амплуа обидва будуть компілюватися, а останній призведе до помилок, коли sizeof(int) != sizeof(void *).

2
@ user7 Але якщо ви не включите stdlib.h, компілятор не знає mallocі не повертає його тип. Тож воно просто береться intза замовчуванням.
Крістіан Рау

10

Ось чому ви ніколи не збираєте без попереджень про відсутні прототипи.

Ось чому ви ніколи не відкидаєте віддачу малок в С.

Гравець необхідний для сумісності з C ++. Причин мало (читайте: тут немає причин) пропускати це.

Сумісність з C ++ потрібна не завжди, а в кількох випадках взагалі не можлива, але в більшості випадків це дуже легко досягти.


22
Чому на землі я б не переймався, якщо мій код C "сумісний" із C ++? Мені байдуже, чи сумісний він з perl, або java, або Ейфелем, або ...
Stephen Canon

4
Якщо ви гарантуєте, що хтось внизу лінії не буде дивитись на ваш код C і піти, ей, я збираюся скласти його з компілятором C ++, тому що це має працювати!
Стівен Лу

3
Ось чому більшість C-кодів може бути тривіально сумісними C ++.
curiousguy
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.