สไตล์ sizeof: sizeof (ชนิด) หรือตัวแปร sizeof?


22

ฉันเห็นสองรูปแบบการใช้งานsizeofสำหรับการดำเนินการที่เกี่ยวข้องกับหน่วยความจำ (เช่นในmemsetหรือmalloc):

  • sizeof(type)และ
  • sizeof variable หรือ sizeof(variable)

คุณต้องการเลือกแบบใดหรือคุณใช้การผสมผสานระหว่างสองสไตล์และเมื่อใดที่คุณจะใช้แต่ละสไตล์ อะไรคือข้อดีข้อเสียของแต่ละสไตล์และเมื่อคุณใช้พวกเขา?

เป็นตัวอย่างฉันสามารถดูคู่ของสถานการณ์ต่อไปนี้ที่ลักษณะหนึ่งช่วยและอีกลักษณะหนึ่งไม่ได้:

เมื่อคุณทำให้ตัวชี้ไปทางผิด:

type *var;
...
memset(var, 0, sizeof var);    /* oops */

เมื่อประเภทเปลี่ยนแปลง:

new_type var;    /* changed from old_type to new_type */
...
memset(&var, 0, sizeof(old_type));    /* oops */

คำตอบ:


30

ผมประสงค์มากกว่าsizeof(variable) sizeof(type)พิจารณา:

int a1;
float a2;
memset(&a1,0,sizeof(a1));
memset(&a2,0,sizeof(a2));

เมื่อเทียบกับ

int a1;
float a2;
memset(&a1,0,sizeof(int));
memset(&a2,0,sizeof(float));

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


4
นอกจากนี้หากคุณเปลี่ยนประเภทของตัวแปรรหัสที่ถือว่าประเภทนั้นจะต้องเปลี่ยน ฉันคิดว่ามันจะดีกว่าที่จะใช้โค้ดที่ถูกต้องโดยใช้การพึ่งพาประเภทน้อยที่สุดเท่าที่จะทำได้ (ฉันได้รับอิทธิพลจากโครงการแปลงขนาดใหญ่ 16 บิตถึง 32 บิตในวันนั้น)
Gort the Robot

สิ่งนี้จะดีกว่าสำหรับการทำงานกับอาร์เรย์ C ที่ไม่ธรรมดาในการสร้าง typedef แต่ใช้สิ่งต่าง ๆ เช่นอาร์เรย์ถ่าน [MAX_SIZE];
mattnz

@ vy32: ผิด 1: "sizeof (อาร์เรย์) ไม่ส่งคืนขนาดของอาร์เรย์ ... ขนาดของตัวชี้ไปยังอาร์เรย์" - ถ้าarrayเป็นอาร์เรย์ C แน่นอนแล้วsizeof(array)ส่งกลับจำนวนไบต์ที่จำเป็นในการจัดเก็บองค์ประกอบอาร์เรย์ . ถ้าarrayเป็นตัวชี้ไปยังองค์ประกอบแรกของอาร์เรย์ C ที่ลดลงแล้วคำสั่งของคุณจะเก็บไว้ การผ่านอาร์เรย์ C เป็นอาร์กิวเมนต์ของฟังก์ชันทำให้ดูเหมือนตัวชี้ในฟังก์ชัน - ข้อมูลทั้งหมดที่พิสูจน์ว่าเป็นอาร์เรย์เช่นขนาดของมันหายไป นั่นเป็นเหตุผลที่มันผุ ผิด 2: "อาร์เรย์ไม่ได้มีอยู่จริงใน C; ... เพียงแค่ประโยคน้ำตาลสำหรับพอยน์เตอร์"
Johann Gerell

9

การตั้งค่า (เช่นเคย) คือการแสดงเจตนาของคุณโดยตรงเท่าที่จะทำได้

ความตั้งใจที่จะดำเนินการกับหน่วยความจำของตัวแปรที่มีอยู่หรือไม่? ถ้าเป็นเช่นนั้นให้ใช้sizeof(variable)เพราะสิ่งนี้แสดงให้เห็นอย่างใกล้ชิดที่สุดเท่าที่จะเป็นไปได้ว่ามันเป็นหน่วยความจำของตัวแปรที่คุณสนใจ

มีความตั้งใจที่จะทำการคำนวณบางอย่างกับชนิดเช่นเพื่อกำหนดจำนวนหน่วยความจำที่ควรจะจัดสรรสำหรับอินสแตนซ์ใหม่หรือไม่? sizeof(type)ถ้าเป็นเช่นนั้นแล้วใช้

นั่นคือฉันชอบ

struct foo *bar;
bar = (struct foo *) malloc(sizeof(struct foo));

เกิน

bar = (struct foo *) malloc(sizeof(*bar));

ในกรณีหลังดูเหมือนว่าคุณกำลังพยายามเข้าถึงตัวแปรที่ไม่มีอยู่

ในทางกลับกันฉันชอบ

char Buffer[256];
memset(Buffer, 0, sizeof(Buffer));

เกิน

char Buffer[256];
memset(Buffer, 0, 256 * sizeof(char));

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



ฉันมักจะทำ แต่นั่นเป็นเรื่องของรสนิยมส่วนตัว ฉันคิดว่าvoid *มันถูกใช้งานโดยอัตโนมัติกับตัวชี้ประเภทอื่น ๆ เพื่อให้เป็นความผิดพลาด
Aidan Cully

3

ผมจะชอบอย่างมากมายกว่าsizeof(type) sizeof variableแม้ว่ามันจะบังคับให้คุณต้องกังวลเพิ่มเติมเกี่ยวกับประเภทและทำการเปลี่ยนแปลงมากขึ้นก็จะช่วยให้คุณหลีกเลี่ยงความผิดพลาดของตัวชี้ร้ายซึ่งจัดอันดับในสาเหตุที่พบบ่อยที่สุดของข้อบกพร่องใน C

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

ข้อดีอย่างหนึ่งของการใช้sizeof variableสำหรับอาร์เรย์ แต่ในทางปฏิบัติฉันได้พบว่าการส่งผ่านอาร์เรย์เป็นคู่แรก / len นั้นเป็นเรื่องปกติมากขึ้น (ในกรณีที่ใช้ความยาว) บวกกับมันยังง่ายมากที่จะได้ขนาดผิดเนื่องจาก การสลายอาร์เรย์ / ตัวชี้ดังในตัวอย่างนี้:

ID_INLINE mat3_t::mat3_t( float src[ 3 ][ 3 ] ) {
  memcpy( mat, src, sizeof( src ) );    /* NOT the same as 3*3*sizeof(float) */
}

2
"การสแกนอย่างรวดเร็วเพื่อดูว่ามีการใช้ตัวแปรใดบ้าง .... มีโอกาสที่จะทำสิ่งนี้ผิด" - สำหรับฉันนี่เป็นอันตรายที่ใหญ่ที่สุดในการทำขนาดของชนิด (type) ซึ่งเป็นสาเหตุที่ฉันมักจะทำขนาดของตัวแปร เนื่องจากผลลัพธ์สุดท้ายของอันตรายนี้คือทุกอย่างอาจยังคงรวบรวมและแม้ว่าคุณทำผิดพลาดเพียงครั้งเดียวมันอาจทำให้คุณต้องใช้เวลานานกว่าหนึ่งปีหรือเกิดปัญหาแบบสุ่มและบัฟเฟอร์เสียหายก่อนที่คุณจะจับมัน และฉันไม่สามารถทำการเชื่อมต่อได้ (เห็นได้ชัดว่าหน้าต่างเวลาของฉันต่อคำถามอยู่ที่ประมาณ 30 วินาที) ระหว่างคำถามนี้กับ "ข้อผิดพลาดทางอ้อมตัวชี้"
DXM

@DXM ด้วยขนาดของตัวถูกดำเนินการถ้ามันเป็นค่าแล้วมันsizeof var; sizeof *varถ้ามันเป็นตัวชี้ว่ามันเป็น นี่เป็นสิ่งที่ผู้คนสามารถทำผิดได้และคอมไพเลอร์จะรับมันอย่างมีความสุขโดยไม่มีการร้องเรียน ฉันยืนยันว่าความผิดพลาดเหล่านี้ยากที่จะสังเกตเห็นและเป็นเรื่องธรรมดามากกว่าความผิดพลาดในsizeof(type)แบบฟอร์ม
congusbongus

1
ทั้งผู้ใช้ประเภท C และขนาดที่ถูกต้องของคุณแตกหักและจะไม่มีความน่าเชื่อถือไม่มีอะไรที่คุณทำในฐานะโปรแกรมเมอร์สามารถแก้ไขได้
mattnz

โพสต์ติดแท็กCรหัสการโพสต์ C มีข้อมูลมากขึ้นว่ารหัส c ++ mat3_t::mat3_t(...)เช่น
chux - Reinstate Monica

0

เป้าหมายคือการลบความซ้ำซ้อน หากคุณใช้ sizeof สำหรับการดำเนินการที่เกี่ยวข้องกับตัวแปรแน่นอนว่าคุณจะต้องสะท้อนขนาดนั้น ใช่คุณสามารถเลอะเทอะเหมือนในตัวอย่างแรก แต่การรักษาไม่ได้ใช้ประเภท แต่ * var ตรงกับเป้าหมายอย่างถูกต้อง

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

ดูมาโคร CLEARของฉันที่ใช้เฉพาะแทน memset เปล่า บันทึกตูดของฉันหลายครั้งในกรณีที่มีการพิมพ์ผิดทางหรือเมื่อเนื้อหา struct หยิบเวกเตอร์หรือสตริงขึ้นมา ...


แมโครเช่นว่านี้เป็นสิ่งที่ทำให้ของการเขียนโปรแกรม C Macro ชื่อที่ไม่ดี ......
mattnz

@ เคลือบเงา: โปรดแสดงทางเลือกที่ดีกว่า มันง่ายกว่าที่จะพูดเรื่องนามธรรมมากกว่าสร้างโปรแกรมที่ใช้งานได้จริง
Balog Pal

1
มาโครที่เชื่อมโยงคือ C ++ และโพสต์นี้ถูกติดแท็ก 'C' (เป็นภาษาอื่น) Ada จะเป็นคำแนะนำของฉัน
mattnz

@mattnz: และหัวข้อเดียวกันแสดงให้เห็นถึงวิธีการบังคับใช้ในวิธีที่คล้ายกันสำหรับ C. คุณดูเหมือนจะพลาดจุดที่ไม่ได้ใช้งานเนื้อหาจริง แต่การใช้งานที่ปลอดภัยกว่าเมื่อเทียบกับสิ่งดิบ Btw ก็อ่านได้เช่นกัน
Balog Pal

0

ตรรกะทางธุรกิจกำหนดทางเลือกของคุณ

  1. หากรหัสของคุณอ้างถึงตัวแปรเฉพาะและไม่มีตัวแปรนี้รหัสของคุณจะไม่มีเหตุผลเลือก sizeof(var)

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


-1

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

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