เหตุใดโค้ดนี้จึง segfault บนสถาปัตยกรรม 64 บิต แต่ทำงานได้ดีบน 32 บิต


112

ฉันเจอปริศนา C ต่อไปนี้:

ถาม: เหตุใดโปรแกรมต่อไปนี้จึง segfault บน IA-64 แต่ทำงานได้ดีบน IA-32

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

ฉันรู้ว่าขนาดของintบนเครื่อง 64 บิตอาจไม่เหมือนกับขนาดของตัวชี้ ( intอาจเป็น 32 บิตและตัวชี้อาจเป็น 64 บิต) แต่ฉันไม่แน่ใจว่าสิ่งนี้เกี่ยวข้องกับโปรแกรมข้างต้นอย่างไร ความคิดใด ๆ ?


50
เป็นอะไรที่งี่เง่าเหมือนstdlib.hไม่รวม?
user786653

3
รหัสนี้ทำงานได้ดีบนเครื่อง 64 บิตของฉัน แม้จะรวบรวมโดยไม่มีคำเตือนหากคุณ#include stdlib.h(สำหรับ malloc)
mpenkov

1
โธ่! @ user786653 ตอกบิตสำคัญ ด้วย#include <stdlib.h>การค้นหาที่สมบูรณ์แบบ แต่นั่นไม่ได้อยู่ในคำถาม

8
@delnan - มันไม่จำเป็นต้องทำงานแบบนั้น แต่มันอาจล้มเหลวอย่างถูกต้องตามกฎหมายบนแพลตฟอร์มที่sizeof(int) == sizeof(int*)ตัวอย่างเช่นหากพอยน์เตอร์ถูกส่งคืนแม้ว่าจะมีการลงทะเบียนอื่นไปยังints ในรูปแบบการโทรที่ใช้
Flexo

7
ในสภาพแวดล้อม C99 คอมไพเลอร์ควรแจ้งเตือนคุณอย่างน้อยเกี่ยวกับการประกาศโดยปริยายของmalloc(). GCC กล่าวว่าwarning: incompatible implicit declaration of built-in function 'malloc'เช่นกัน
Jonathan Leffler

คำตอบ:


130

โยนไปint*มาสก์ความจริงที่ว่าไม่มีการที่เหมาะสม#includeประเภทการกลับมาของจะถือว่าmalloc intIA-64 เกิดขึ้นsizeof(int) < sizeof(int*)ซึ่งทำให้ปัญหานี้ชัดเจน

(โปรดทราบด้วยว่าเนื่องจากพฤติกรรมที่ไม่ได้กำหนดไว้มันยังคงล้มเหลวแม้บนแพลตฟอร์มที่sizeof(int)==sizeof(int*)ถือเป็นจริงเช่นหากหลักการเรียกใช้การลงทะเบียนที่แตกต่างกันสำหรับการส่งคืนพอยน์เตอร์มากกว่าจำนวนเต็ม)

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 เนื่องจาก an intและ a void*มีขนาดเท่ากันและส่งคืนในลักษณะเดียวกัน ใน IA64 คุณได้รับค่าที่ไม่ถูกต้อง
Steve Jessop

33

เป็นไปได้มากว่าคุณไม่ได้รวมไฟล์ส่วนหัวไว้สำหรับmallocและในขณะที่คอมไพเลอร์มักจะเตือนคุณถึงเรื่องนี้ แต่การที่คุณส่งค่าส่งคืนอย่างชัดเจนหมายความว่าคุณกำลังบอกว่าคุณรู้ว่าคุณกำลังทำอะไรอยู่

นั่นหมายความว่าคอมไพลเลอร์คาดว่าintจะส่งคืนจากmallocที่คอมไพเลอร์ส่งไปยังพอยน์เตอร์ หากมีขนาดแตกต่างกันนั่นจะทำให้คุณเศร้าโศก

นี่คือเหตุผลที่คุณไม่เคยส่งmallocผลตอบแทนเป็น C สิ่งvoid*ที่ส่งคืนจะถูกแปลงโดยปริยายเป็นตัวชี้ประเภทที่ถูกต้อง (เว้นแต่คุณจะไม่ได้รวมส่วนหัวไว้ในกรณีนี้อาจจะเตือนคุณถึง Int- ที่อาจไม่ปลอดภัย การแปลงเป็นตัวชี้)


ขอโทษที่ฟังดูไร้เดียงสา แต่ฉันมักจะคิดว่า malloc ส่งคืนตัวชี้โมฆะซึ่งสามารถส่งไปยังประเภทที่เหมาะสมได้ ฉันไม่ใช่โปรแกรมเมอร์ C ดังนั้นจึงขอขอบคุณรายละเอียดเพิ่มเติมเล็กน้อย
7

5
@ user7: หากไม่มี #include <stdlib.h> คอมไพลเลอร์ C จะถือว่าค่าส่งคืนของ malloc เป็น int
sashang

4
@ user7: ตัวชี้โมฆะสามารถส่งได้ แต่ไม่จำเป็นต้องใช้ใน C เนื่องจากvoid *สามารถแปลงเป็นชนิดตัวชี้อื่น ๆ ได้โดยปริยาย int *p = malloc(sizeof(int))ทำงานได้หากต้นแบบที่เหมาะสมอยู่ในขอบเขตและล้มเหลวหากไม่เป็นเช่นนั้น (เพราะจะถือว่าผลลัพธ์เป็นint) sizeof(int) != sizeof(void *)พร้อมกับโยนทั้งจะรวบรวมและหลังจะส่งผลให้เกิดข้อผิดพลาดเมื่อ

2
@ user7 แต่ถ้าคุณไม่รวมstdlib.hคอมไพลเลอร์จะไม่ทราบmallocและไม่มีประเภทการส่งคืน ดังนั้นจึงถือว่าintเป็นค่าเริ่มต้น
Christian Rau

10

นี่คือเหตุผลที่คุณไม่เคยรวบรวมโดยไม่มีคำเตือนเกี่ยวกับต้นแบบที่หายไป

นี่คือเหตุผลที่คุณไม่เคยส่งผลตอบแทน malloc ใน C

จำเป็นต้องใช้นักแสดงเพื่อความเข้ากันได้ของ C ++ มีเหตุผลเพียงเล็กน้อย (อ่าน: ไม่มีเหตุผลที่นี่) ที่จะละเว้น

ไม่จำเป็นต้องใช้ความเข้ากันได้กับ C ++ เสมอไปและในบางกรณีก็ทำไม่ได้เลย แต่ในกรณีส่วนใหญ่ทำได้ง่ายมาก


22
เหตุใดฉันจึงต้องสนใจว่ารหัส C ของฉัน "เข้ากันได้" กับ C ++ บนโลกนี้ได้อย่างไร ฉันไม่สนใจว่ามันเข้ากันได้กับ perl หรือ java หรือ Eiffel หรือ ...
Stephen Canon

4
หากคุณรับประกันว่าใครบางคนที่อยู่ในบรรทัดจะไม่มองไปที่รหัส C ของคุณและไปได้เฮ้ฉันจะรวบรวมด้วยคอมไพเลอร์ C ++ เพราะมันน่าจะใช้ได้!
Steven Lu

3
สาเหตุรหัส C ที่สุดที่สามารถนิดทำ c ++ ที่เข้ากันได้
ซอกแซก
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.