คุณเปรียบเทียบโครงสร้างเพื่อความเท่าเทียมกันใน C อย่างไร


216

คุณเปรียบเทียบสองอินสแตนซ์ของ structs เพื่อความเท่าเทียมในมาตรฐาน C ได้อย่างไร

คำตอบ:


196

C ไม่มีสิ่งอำนวยความสะดวกด้านภาษาในการทำสิ่งนี้ - คุณต้องทำด้วยตัวเองและเปรียบเทียบโครงสร้างสมาชิกแต่ละรายโดยสมาชิก


19
หากตัวแปร 2 โครงสร้างถูกกำหนดค่าเริ่มต้นด้วย calloc หรือตั้งค่าเป็น 0 โดย memset คุณจึงสามารถเปรียบเทียบ 2 โครงสร้างของคุณกับ memcmp และไม่ต้องกังวลกับขยะโครงสร้างและสิ่งนี้จะช่วยให้คุณได้รับเวลา
MOHAMED

21
@MOHAMED เปรียบเทียบเขตลอยจุดที่มีปัญหากับ0.0, -0.0 NaN memcmp()ตัวชี้ที่แตกต่างกันในการเป็นตัวแทนไบนารีอาจชี้ไปที่ตำแหน่งเดียวกัน (เช่น DOS: seg: offset) และอื่น ๆ ที่เท่ากัน บางระบบมีพอยน์เตอร์พอยน์เตอร์หลายตัวที่เปรียบเทียบกัน เหมือนกันสำหรับคลุมเครือintกับ -0 และชนิดจุดลอยที่มีการเข้ารหัสซ้ำซ้อน (Intel ยาวสองเท่า, ทศนิยม 64, ฯลฯ ) ปัญหาเหล่านี้ไม่ได้calloc()ใช้หรือแตก
chux - Reinstate Monica

2
@ chux สำหรับระบบ 32 หรือ 64- บิตที่ทันสมัยทุกอย่างที่ฉันรู้ปัญหาเดียวคือจุดลอย
Demi

2
ในกรณีที่คุณสงสัยว่าทำไม==ไม่ทำงานกับโครงสร้าง (เช่นฉัน) โปรดดูstackoverflow.com/questions/46995631/ …
stefanct

4
@Demi: วันนี้ พระบัญญัติข้อที่ 10 สำหรับโปรแกรมเมอร์ C คือ 'ท่านจะต้องสละสิทธิ์และสละความชั่วช้าซึ่งอ้างว่า“ โลกทั้งโลกคือ VAX” ... ' การแทนที่ด้วย "พีซีทั้งหมดของโลก" ไม่ใช่การปรับปรุง
Martin Bonner สนับสนุน Monica

110

คุณอาจถูกล่อลวงให้ใช้memcmp(&a, &b, sizeof(struct foo))แต่มันอาจไม่ทำงานในทุกสถานการณ์ คอมไพเลอร์อาจเพิ่มพื้นที่บัฟเฟอร์การจัดตำแหน่งให้กับโครงสร้างและค่าที่พบในตำแหน่งหน่วยความจำที่วางอยู่ในพื้นที่บัฟเฟอร์ไม่รับประกันว่าจะเป็นค่าใด ๆ

แต่ถ้าคุณใช้callocหรือmemsetขนาดเต็มของโครงสร้างก่อนที่จะใช้พวกเขาคุณสามารถทำการเปรียบเทียบแบบตื้นด้วยmemcmp(ถ้าโครงสร้างของคุณมีตัวชี้มันจะจับคู่เฉพาะถ้าที่อยู่ที่ตัวชี้จะชี้ไปที่เดียวกัน)


19
ปิดเพราะมันทำงานกับคอมไพเลอร์ "เกือบทั้งหมด" แต่ไม่มาก ตรวจสอบ 6.2.1.6.4 ใน C90: "สองค่า (นอกเหนือจาก NaNs) ที่มีการแสดงวัตถุเดียวกันเปรียบเทียบเท่ากัน แต่ค่าที่เปรียบเทียบเท่ากันอาจมีการนำเสนอวัตถุที่แตกต่างกัน"
Steve Jessop

22
พิจารณาฟิลด์ "BOOL" ในแง่ของความเท่าเทียมกัน BOOL ที่ไม่เป็นศูนย์ใด ๆ จะเท่ากับค่า BOOL ที่ไม่เป็นศูนย์ทุกตัว ดังนั้นในขณะที่ 1 และ 2 อาจเป็น TRUE และเท่ากัน memcmp จะล้มเหลว
ajs410

4
@JSalazar ง่ายขึ้นสำหรับคุณ แต่อาจจะยากกว่าสำหรับคอมไพเลอร์และซีพียูและทำให้ช้าลงมากเช่นกัน ทำไมคุณคิดว่าคอมไพเลอร์เพิ่มการเติมในตอนแรก แน่นอนว่าจะไม่เสียความทรงจำอะไรเลย)
Mecki

4
@Demetri: ตัวอย่างเช่นค่าทศนิยมบวกและลบศูนย์เปรียบเทียบเท่ากับในการดำเนินการลอย IEEE ใด ๆ แต่พวกเขาไม่ได้เป็นตัวแทนวัตถุเดียวกัน ดังนั้นจริง ๆ แล้วฉันไม่ควรพูดว่ามันใช้งานได้กับ "คอมไพเลอร์เกือบทั้งหมด" มันจะล้มเหลวในการติดตั้งใด ๆ ที่ช่วยให้คุณเก็บศูนย์ลบได้ ฉันอาจจะคิดว่าการเป็นตัวแทนจำนวนเต็มตลกในเวลาที่ฉันแสดงความคิดเห็น
Steve Jessop

4
@Demetri: แต่หลายคนมีจำนวนลอยตัวและผู้ถามถามว่า "คุณเปรียบเทียบโครงสร้างอย่างไร" ไม่ใช่ "คุณเปรียบเทียบโครงสร้างอย่างไรที่ไม่มีลอย" คำตอบนี้บอกว่าคุณสามารถทำการเปรียบเทียบแบบตื้นกับmemcmpโดยที่ล้างหน่วยความจำก่อน ซึ่งอยู่ใกล้กับการทำงาน แต่ไม่ถูกต้อง Ofc คำถามยังไม่ได้กำหนด "ความเท่าเทียมกัน" ดังนั้นถ้าคุณใช้มันเพื่อหมายถึง "ความเท่าเทียมกันของการแทนค่าวัตถุที่เป็นไบต์" แล้วmemcmpทำสิ่งนั้นอย่างแน่นอน (ไม่ว่าจะล้างหน่วยความจำหรือไม่
Steve Jessop

22

ถ้าคุณทำมันมากฉันขอแนะนำให้เขียนฟังก์ชั่นที่เปรียบเทียบทั้งสองโครงสร้าง ด้วยวิธีนี้หากคุณเปลี่ยนโครงสร้างคุณจะต้องเปลี่ยนการเปรียบเทียบในที่เดียว

สำหรับวิธีที่จะทำ .... คุณต้องเปรียบเทียบทุกองค์ประกอบแยกกัน


1
ฉันจะเขียนฟังก์ชั่นแยกต่างหากแม้ว่าฉันจะใช้เพียงครั้งเดียว
Sam

18

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

  // bad
  memcmp(&struct1, &struct2, sizeof(struct1));

ข้างต้นจะล้มเหลวสำหรับ struct เช่นนี้

typedef struct Foo {
  char a;
  /* padding */
  double d;
  /* padding */
  char e;
  /* padding */
  int f;
} Foo ;

คุณต้องใช้การเปรียบเทียบสมาชิกที่ชาญฉลาดเพื่อความปลอดภัย


25
ไม่น่าจะแพ็ดดิ้งหลังจากดับเบิล ถ่านจะได้รับการจัดตำแหน่งอย่างเพียงพออย่างสมบูรณ์แบบทันทีหลังจากที่ดับเบิล
Jonathan Leffler

7

@Greg ถูกต้องที่หนึ่งจะต้องเขียนฟังก์ชั่นการเปรียบเทียบที่ชัดเจนในกรณีทั่วไป

มันเป็นไปได้ที่จะใช้memcmpถ้า:

  • structs ไม่มีเขตข้อมูลทศนิยมที่อาจเป็นไปNaNได้
  • structs ไม่มีช่องว่างภายใน (ใช้-Wpaddedกับ clang เพื่อตรวจสอบสิ่งนี้) หรือ structs ถูกเตรียมใช้งานอย่างชัดเจนด้วยmemsetเมื่อเริ่มต้น
  • ไม่มีประเภทสมาชิก (เช่น Windows BOOL) ที่มีค่าแตกต่างกัน แต่เทียบเท่า

หากคุณไม่ได้เขียนโปรแกรมสำหรับระบบฝังตัว (หรือเขียนไลบรารีที่อาจใช้กับพวกเขา) ฉันจะไม่กังวลเกี่ยวกับตัวพิมพ์มุมบางตัวในมาตรฐาน C ความแตกต่างของตัวชี้ใกล้กับไกลไม่มีอยู่ในอุปกรณ์ 32- บิตหรือ 64- บิต ไม่มีระบบที่ไม่ฝังที่ฉันรู้จักมีพNULLอยน์เตอร์หลายตัว

อีกทางเลือกหนึ่งคือการสร้างฟังก์ชั่นความเท่าเทียมกันโดยอัตโนมัติ หากคุณวางคำจำกัดความของโครงสร้างในวิธีที่ง่ายมันเป็นไปได้ที่จะใช้การประมวลผลข้อความอย่างง่ายในการจัดการคำจำกัดความของโครงสร้างที่เรียบง่าย คุณสามารถใช้ libclang สำหรับกรณีทั่วไป - เนื่องจากใช้ส่วนหน้าเดียวกับ Clang จึงจัดการกรณีมุมทั้งหมดได้อย่างถูกต้อง (ยกเว้นข้อบกพร่อง)

ฉันไม่เห็นไลบรารี่การสร้างรหัสดังกล่าว อย่างไรก็ตามมันดูค่อนข้างง่าย

อย่างไรก็ตามเป็นกรณีที่ฟังก์ชันความเสมอภาคที่สร้างขึ้นเช่นนี้มักจะทำสิ่งผิดพลาดในระดับแอปพลิเคชัน ตัวอย่างเช่นควรเปรียบเทียบสองUNICODE_STRINGโครงสร้างใน Windows อย่างตื้นหรือลึก?


2
การกำหนดค่าเริ่มต้นอย่างชัดเจนด้วย structs memsetฯลฯ ไม่รับประกันค่าของบิต
แพ็ดดิ้ง

4

หมายเหตุคุณสามารถใช้ memcmp () กับ stuctures ที่ไม่คงที่โดยไม่ต้องกังวลเกี่ยวกับการขยายหากคุณไม่ได้กำหนดค่าเริ่มต้นสมาชิกทั้งหมด (พร้อมกัน) สิ่งนี้ถูกกำหนดโดย C90:

http://www.pixelbeat.org/programming/gcc/auto_init.html


1
มีการระบุจริงหรือไม่ว่า{0, }จะเป็นศูนย์ไบต์ใด ๆ ?
Alnitak

GCC อย่างน้อยเป็นศูนย์ padding bytes สำหรับโครงสร้างเริ่มต้นบางส่วนตามที่แสดงไว้ที่ลิงค์ด้านบนและstackoverflow.com/questions/13056364/…รายละเอียดที่ C11 ระบุพฤติกรรมนั้น
pixelbeat

1
ไม่เป็นประโยชน์อย่างมากโดยทั่วไปเนื่องจากการแพ็ดดิ้งทั้งหมดไม่แน่นอนเมื่อกำหนดให้กับสมาชิกใด ๆ
MM

2

ขึ้นอยู่กับว่าคำถามที่คุณถามคือ:

  1. โครงสร้างทั้งสองนี้เป็นวัตถุเดียวกันหรือไม่
  2. พวกเขามีค่าเท่ากันหรือไม่

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

ในกรณีพิเศษที่ structs ไม่มีพอยน์เตอร์คุณสามารถทำ memcmp เพื่อทำการเปรียบเทียบข้อมูลที่มีอยู่ในแต่ละบิตโดยไม่ต้องรู้ว่าข้อมูลหมายถึงอะไร

ตรวจสอบให้แน่ใจว่าคุณรู้ว่า 'เท่ากับ' หมายความว่าอย่างไรสำหรับสมาชิกแต่ละคน - มันชัดเจนสำหรับ ints แต่จะละเอียดกว่าเมื่อพูดถึงค่าทศนิยมหรือประเภทที่ผู้ใช้กำหนด


2

memcmp ไม่ได้เปรียบเทียบโครงสร้าง memcmpเปรียบเทียบไบนารีและมีขยะในโครงสร้างเสมอดังนั้นมันจะออกมาเป็นเท็จในการเปรียบเทียบเสมอ

เปรียบเทียบองค์ประกอบตามองค์ประกอบที่ปลอดภัยและจะไม่ล้มเหลว


1
หากตัวแปร 2 โครงสร้างถูกกำหนดค่าเริ่มต้นด้วย calloc หรือตั้งค่าเป็น 0 โดย memset คุณจึงสามารถเปรียบเทียบ 2 โครงสร้างของคุณกับ memcmp และไม่ต้องกังวลเกี่ยวกับขยะโครงสร้างและสิ่งนี้จะช่วยให้คุณได้รับเวลา
MOHAMED

1

หาก structs มีเพียงข้อมูลพื้นฐานเท่านั้นหรือหากคุณสนใจในความเท่าเทียมที่เข้มงวดคุณสามารถทำสิ่งนี้ได้:

int my_struct_cmp (const struct my_struct * lhs, const struct my_struct * rhs)
{
    ส่งคืน memcmp (lhs, rsh, sizeof (struct my_struct));
}

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

อย่างไรก็ตามโปรดทราบว่าคุณควรใช้ memset (& a, sizeof (struct my_struct), 1) เพื่อไม่ให้ช่วงของหน่วยความจำของโครงสร้างเป็นศูนย์ซึ่งเป็นส่วนหนึ่งของการเริ่มต้น ADT ของคุณ


-1

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


-2

ตัวอย่างที่สอดคล้องกับนี้ใช้ส่วนขยายคอมไพเลอร์แพ็ค #pragma จาก Microsoft Visual Studio เพื่อให้แน่ใจว่าสมาชิกโครงสร้างจะถูกบีบอัดให้แน่นที่สุด:

#include <string.h>

#pragma pack(push, 1)
struct s {
  char c;
  int i;
  char buffer[13];
};
#pragma pack(pop)

void compare(const struct s *left, const struct s *right) { 
  if (0 == memcmp(left, right, sizeof(struct s))) {
    /* ... */
  }
}

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