Malloc vs new - padding ที่แตกต่างกัน


110

ฉันกำลังตรวจสอบรหัส C ++ ของผู้อื่นสำหรับโครงการของเราที่ใช้ MPI สำหรับการประมวลผลประสิทธิภาพสูง (10 ^ 5 - 10 ^ 6 คอร์) รหัสนี้มีวัตถุประสงค์เพื่อให้สามารถสื่อสารระหว่าง (อาจ) เครื่องจักรที่แตกต่างกันในสถาปัตยกรรมที่แตกต่างกัน เขาเขียนความคิดเห็นที่กล่าวถึงบางสิ่งตามบรรทัดของ:

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

สิ่งนี้ไม่เข้ากับสิ่งที่ฉันรู้จากคำถามมาตรฐานnewเทียบกับmallocคำถาม

อะไรคือความแตกต่างระหว่าง new / delete และ malloc / free? บอกใบ้ถึงแนวคิดที่ว่าคอมไพเลอร์สามารถคำนวณขนาดของวัตถุได้แตกต่างกัน (แต่เหตุใดจึงแตกต่างจากการใช้sizeof)

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

malloc เข้าใจการจัดตำแหน่งอย่างไร บอกว่าหน่วยความจำได้รับการรับรองว่าจัดวางอย่างถูกต้องnewหรือmallocเป็นสิ่งที่ฉันเคยคิดไว้ก่อนหน้านี้

ฉันเดาว่าเขาวินิจฉัยจุดบกพร่องของตัวเองผิดพลาดในอดีตและอนุมานได้ว่าnewและmallocให้จำนวนช่องว่างที่แตกต่างกันซึ่งฉันคิดว่าอาจไม่เป็นความจริง แต่ฉันไม่พบคำตอบใน Google หรือคำถามก่อนหน้านี้

ช่วยฉันด้วย StackOverflow คุณเป็นความหวังเดียวของฉัน!


33
+1 สำหรับการวิจัยหัวข้อ SO ต่างๆเพียงอย่างเดียว!
iammilind

7
+1 ได้อย่างง่ายดายหนึ่งในงานวิจัย "ช่วยตัวเองก่อนฉันถามคนอื่น" ที่ดีที่สุดที่ฉันเคยเห็นใน SO ในระยะเวลาอันยาวนาน หวังว่าฉันจะโหวตเพิ่มอีกสักสองสามครั้ง
WhozCraig

1
รหัสการถ่ายโอนถือว่าข้อมูลถูกจัดแนวในลักษณะเฉพาะเช่นว่าข้อมูลเริ่มต้นที่ขอบเขตแปดไบต์หรือไม่ สิ่งนี้อาจแตกต่างกันระหว่างmallocและnewเนื่องจากnewในบางสภาพแวดล้อมจัดสรรบล็อกจะเพิ่มข้อมูลบางส่วนไปยังจุดเริ่มต้นและส่งกลับตัวชี้ไปยังตำแหน่งหลังจากข้อมูลนี้ (ฉันเห็นด้วยกับคนอื่น ๆ ในบล็อกข้อมูลmallocและnewต้องใช้ช่องว่างภายในแบบเดียวกัน)
Lindydancer

1
ว้าวฉันไม่ได้คาดหวังว่าคำถามนี้จะเป็นที่นิยมขนาดนี้! @ Lindydancer ฉันไม่คิดว่าขอบเขต 8 ไบต์จะถูกสมมติขึ้น จุดที่น่าสนใจแม้ว่า
hcarver

1
เหตุผลหนึ่งที่ต้องใช้วิธีการจัดสรรแบบหนึ่งกับอีกวิธีหนึ่งคือเมื่อ "ใครบางคน" กำลังทำการปลดปล่อยออบเจ็กต์ หาก "คนอื่น" นี้ลบวัตถุโดยใช้ฟรีคุณต้องจัดสรรโดยใช้ malloc (ปัญหาแผ่นรองคือปลาเฮอริ่งแดง)
Lindydancer

คำตอบ:


25

IIRC มีจุดที่จู้จี้จุกจิกอย่างหนึ่ง mallocรับประกันว่าจะส่งคืนที่อยู่ที่สอดคล้องกับประเภทมาตรฐานใด ๆ ::operator new(n)รับประกันว่าจะส่งคืนเฉพาะที่อยู่ที่จัดตำแหน่งสำหรับประเภทมาตรฐานใด ๆ ที่มีขนาดไม่เกิน nและหากTไม่ใช่ประเภทอักขระnew T[n]จะต้องส่งคืนที่อยู่ที่จัดตำแหน่งให้Tเท่านั้น

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

ไม่มีผลต่อช่องว่างภายในวัตถุซึ่งจำเป็นต้องมีเค้าโครงเดียวกันทุกประการไม่ว่าคุณจะจัดสรรหน่วยความจำอย่างไรก็ตาม ดังนั้นจึงยากที่จะดูว่าความแตกต่างอาจส่งผลให้เกิดข้อผิดพลาดในการถ่ายโอนข้อมูลได้อย่างไร

มีสัญญาณว่าผู้เขียนความคิดเห็นนั้นคิดอย่างไรเกี่ยวกับวัตถุในสแต็กหรือในรูปทรงกลมไม่ว่าในความคิดของเขาจะ "บุนวมเหมือน Malloc" หรือ "เบาะเหมือนใหม่" นั่นอาจให้เบาะแสว่าแนวคิดนี้มาจากไหน

บางทีเขาอาจจะสับสน แต่บางทีรหัสที่เขาพูดถึงเป็นมากกว่าความแตกต่างตรงระหว่างVSmalloc(sizeof(Foo) * n) new Foo[n]อาจจะชอบมากกว่า:

malloc((sizeof(int) + sizeof(char)) * n);

เทียบกับ

struct Foo { int a; char b; }
new Foo[n];

นั่นคือบางทีเขาอาจจะพูดว่า "ฉันใช้ malloc" แต่หมายถึง "ฉันแพ็คข้อมูลลงในตำแหน่งที่ไม่ตรงแนวด้วยตนเองแทนที่จะใช้โครงสร้าง" ที่จริงแล้วmallocไม่จำเป็นต้องใช้ในการแพ็คโครงสร้างด้วยตนเอง แต่ไม่ทราบว่าเป็นระดับความสับสนน้อยกว่า จำเป็นต้องกำหนดโครงร่างข้อมูลที่ส่งผ่านสาย การใช้งานที่แตกต่างกันจะแผ่นข้อมูลที่แตกต่างกันเมื่อstructถูกนำมาใช้


ขอบคุณสำหรับประเด็นเกี่ยวกับการจัดตำแหน่ง ข้อมูลที่เป็นปัญหาคืออาร์เรย์ char ดังนั้นฉันจึงสงสัยว่ามันไม่ใช่สิ่งที่จัดตำแหน่งที่นี่หรือสิ่งที่มีโครงสร้าง - แม้ว่านั่นจะเป็นความคิดแรกของฉันด้วยก็ตาม
hcarver

5
@Hbcdev: charอาร์เรย์อย่างดีไม่เคยมีเบาะเลยดังนั้นฉันจะใช้คำว่า "สับสน" เป็นคำอธิบาย
Steve Jessop

5

เพื่อนร่วมงานของคุณอาจมีnew[]/delete[]คุกกี้วิเศษอยู่ในใจ (นี่คือข้อมูลที่การนำไปใช้งานใช้เมื่อลบอาร์เรย์) อย่างไรก็ตามสิ่งนี้จะไม่มีปัญหาหากมีการใช้การจัดสรรที่เริ่มต้นที่ที่อยู่ที่ส่งกลับnew[]มา (ตรงข้ามกับผู้จัดสรร)

การบรรจุดูมีความเป็นไปได้มากขึ้น การเปลี่ยนแปลงใน ABI อาจส่งผลให้เกิดจำนวนไบต์ต่อท้ายที่แตกต่างกันเพิ่มที่ส่วนท้ายของโครงสร้าง (ซึ่งได้รับอิทธิพลจากการจัดตำแหน่งและพิจารณาอาร์เรย์ด้วย) ด้วย malloc สามารถระบุตำแหน่งของโครงสร้างและทำให้พกพาไปยัง ABI ต่างประเทศได้ง่ายขึ้น โดยปกติรูปแบบเหล่านี้ป้องกันได้โดยการระบุการจัดตำแหน่งและการบรรจุโครงสร้างการถ่ายโอน


2
นี่คือสิ่งแรกที่ฉันคิดว่าปัญหา "โครงสร้างมากกว่าผลรวมของส่วนต่างๆ" บางทีนี่อาจเป็นที่มาของความคิดของเขา
hcarver

3

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


3

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


ก่อนหน้านี้ฉันคิดว่าคุณสามารถพิจารณาnewเป็นกระดาษห่อหุ้มที่ดีmallocแต่ดูเหมือนว่าจากคำตอบอื่น ๆ ที่ไม่เป็นความจริง ฉันทามติดูเหมือนว่าช่องว่างภายในควรจะเหมือนกันด้วย; ฉันคิดว่าปัญหาในการถ่ายโอนข้อมูลระหว่างแพลตฟอร์มจะเกิดขึ้นก็ต่อเมื่อกลไกการถ่ายโอนของคุณมีข้อบกพร่อง :)
hcarver

0

เมื่อฉันต้องการที่จะควบคุมรูปแบบของโครงสร้างข้อมูลเก่าธรรมดาของฉันด้วย MS Visual #pragma pack(1)คอมไพเลอร์ใช้ฉัน ผมคิดว่าคำสั่งดังกล่าวได้รับการสนับสนุน precompiler สำหรับคอมไพเลอร์ส่วนใหญ่อย่างเช่นGCC

สิ่งนี้มีผลมาจากการจัดแนวเขตข้อมูลทั้งหมดของโครงสร้างหนึ่งไว้ด้านหลังอีกด้านหนึ่งโดยไม่มีช่องว่าง

หากแพลตฟอร์มในอีกด้านหนึ่งทำเช่นเดียวกัน (กล่าวคือรวบรวมโครงสร้างการแลกเปลี่ยนข้อมูลด้วยช่องว่างภายในเป็น 1) ข้อมูลที่ดึงมาจากทั้งสองด้านก็พอดีกัน ดังนั้นฉันจึงไม่ต้องเล่นกับ malloc ใน C ++

ที่แย่ที่สุดฉันคิดว่าตัวดำเนินการใหม่จะทำงานหนักเกินไปเพื่อที่จะดำเนินการบางอย่างที่ยุ่งยากแทนที่จะใช้ malloc โดยตรงใน C ++


มีสถานการณ์ใดบ้างที่คุณต้องการควบคุมเค้าโครงโครงสร้างข้อมูล แค่สงสัย.
hcarver

และมีใครรู้จักคอมไพเลอร์ที่รองรับpragma packหรือคล้ายกันบ้าง? ฉันตระหนักดีว่ามันจะไม่เป็นส่วนหนึ่งของมาตรฐาน
hcarver

gcc รองรับเช่น ฉันต้องการสิ่งนั้นในสถานการณ์ใด: การแบ่งปันข้อมูลไบนารีระหว่างรูปแบบจานสองแบบที่แตกต่างกัน: การแชร์สตรีมไบนารีระหว่าง windows และ palmOS ระหว่าง windows และ linux ลิงก์เกี่ยวกับ gcc: gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html
Stephane Rolland

0

นี่คือการคาดเดาของฉันว่าสิ่งนี้มาจากไหน ดังที่คุณได้กล่าวไปแล้วปัญหาเกิดจากการส่งข้อมูลผ่าน MPI

โดยส่วนตัวแล้วสำหรับโครงสร้างข้อมูลที่ซับซ้อนของฉันที่ฉันต้องการส่ง / รับผ่าน MPI ฉันมักจะใช้วิธีการทำให้เป็นอนุกรม / การดีซีเรียลไลเซชันที่แพ็ค / แกะสิ่งทั้งหมดลงใน / จากอาร์เรย์ของอักขระ ตอนนี้เนื่องจากการขยายเรารู้ว่าขนาดของโครงสร้างนั้นอาจใหญ่กว่าขนาดของสมาชิกดังนั้นจึงต้องคำนวณขนาดที่ไม่ได้ใส่ของโครงสร้างข้อมูลเพื่อให้เราทราบว่ามีการส่ง / รับจำนวนไบต์

ตัวอย่างเช่นหากคุณต้องการส่ง / รับstd::vector<Foo> Aผ่าน MPI ด้วยเทคนิคดังกล่าวการสมมติว่าขนาดของอาร์เรย์ของอักขระที่เป็นผลลัพธ์นั้นผิดA.size()*sizeof(Foo)โดยทั่วไป กล่าวอีกนัยหนึ่งคือแต่ละคลาสที่ใช้เมธอด serialize / deserialize ควรใช้วิธีที่รายงานขนาดของอาร์เรย์ (หรือดีกว่า แต่เก็บอาร์เรย์ไว้ในคอนเทนเนอร์) นี่อาจเป็นสาเหตุของข้อบกพร่อง อย่างไรก็ตามไม่ทางใดก็ทางหนึ่งที่ไม่มีส่วนเกี่ยวข้องกับnewvs mallocตามที่ระบุไว้ในเธรดนี้


การคัดลอกไปยังอาร์เรย์ถ่านอาจเป็นปัญหาได้ - อาจเป็นไปได้ว่าคอร์บางตัวของคุณอยู่บนสถาปัตยกรรมที่มีผู้ใช้น้อยและบางส่วน (อาจไม่น่าจะเป็นไปได้ แต่ก็เป็นไปได้) คุณต้องเข้ารหัส XDR หรืออะไรบางอย่าง แต่คุณสามารถใช้ประเภทข้อมูล MPI ที่ผู้ใช้กำหนดได้ พวกเขาคำนึงถึงช่องว่างภายในอย่างง่ายดาย แต่ฉันเห็นสิ่งที่คุณพูดเกี่ยวกับสาเหตุของความเข้าใจผิดที่เป็นไปได้นั่นคือสิ่งที่ฉันเรียกว่าปัญหา "โครงสร้างมากกว่าผลรวมของส่วนต่างๆ"
hcarver

ใช่การกำหนดชนิดข้อมูล MPI เป็นอีกวิธีหนึ่ง / ถูกต้องในการดำเนินการนี้ จุดที่ดีเกี่ยวกับความอดทน แม้ว่าฉันจะสงสัยจริงๆว่ามันจะเกิดขึ้นบนคลัสเตอร์จริง อย่างไรก็ตามฉันคิดว่าถ้าพวกเขาทำตามกลยุทธ์เดียวกันสิ่งนี้อาจนำไปสู่จุดบกพร่อง ...
mmirzadeh

0

ใน c ++: newคำสำคัญใช้เพื่อจัดสรรไบต์ของหน่วยความจำโดยเฉพาะเกี่ยวกับโครงสร้างข้อมูลบางส่วน ตัวอย่างเช่นคุณได้กำหนดคลาสหรือโครงสร้างบางอย่างและคุณต้องการจัดสรรหน่วยความจำสำหรับอ็อบเจ็กต์

myclass *my = new myclass();

หรือ

int *i = new int(2);

แต่ในทุกกรณีคุณต้องมีประเภทข้อมูลที่กำหนดไว้ (class, struct, union, int, char ฯลฯ ... ) และเฉพาะไบต์ของหน่วยความจำเท่านั้นที่จะได้รับการจัดสรรซึ่งจำเป็นสำหรับอ็อบเจ็กต์ / ตัวแปร (กล่าวคือทวีคูณของประเภทข้อมูลนั้น)

แต่ในกรณีของเมธอด malloc () คุณสามารถจัดสรรหน่วยความจำไบต์ใดก็ได้และคุณไม่จำเป็นต้องระบุประเภทข้อมูลตลอดเวลา ที่นี่คุณสามารถสังเกตได้จาก malloc ():

void *v = malloc(23);

หรือ

void *x = malloc(sizeof(int) * 23);

หรือ

char *c = (char*)malloc(sizeof(char)*35);

-1

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


1
ฉันคิดว่าคุณควรพยายามโต้แย้งคำตอบของคุณให้มากขึ้น
Carlo

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