C에서 평등에 대한 구조체를 어떻게 비교합니까?


답변:


196

C는이를 위해 언어 기능을 제공하지 않습니다.이를 직접 수행하고 각 구조 구성원을 구성원별로 비교해야합니다.


19
2 구조체 변수가 calloc으로 초기화되거나 memset에 의해 0으로 설정된 경우 2 구조체를 memcmp와 비교할 수 있고 구조체 쓰레기에 대해 걱정할 필요가 없으므로 시간을 벌 수 있습니다.
MOHAMED

21
@MOHAMED와 부동 소수점 필드 비교 0.0, -0.0 NaN는 문제가 memcmp()있습니다. 이진 표현이 다른 포인터는 동일한 위치 (예 : DOS : seg : offset)를 가리킬 수 있으므로 동일합니다. 일부 시스템에는 동일하게 비교되는 여러 개의 널 포인터가 있습니다. int중복 인코딩이있는 -0 및 부동 소수점 유형의 경우 모호한 경우 와 동일합니다 . (Intel long double, decimal64 등)이 문제는 calloc()사용되거나 패딩되지 않습니다.
chux-복직 모니카

2
에 @chux 어떤 내가 아는 현대 32 비트 또는 64 비트 시스템, 유일한 문제는 부동 소수점과 함께합니다.
Demi

2
==나와 같은 구조로 작업하지 않는지 궁금한 경우 stackoverflow.com/questions/46995631/…
stefanct

4
@ 데미 : 오늘. C 프로그래머를위한 10 번째 계명은 '여러분은“모든 세상이 VAX”라고 주장하는 사악한 이단을 앞장서 서 포기하고 포기하십시오. 이것을 "세계의 모든 PC"로 바꾸는 것은 개선되지 않습니다.
Martin Bonner는 Monica

110

을 사용 memcmp(&a, &b, sizeof(struct foo))하고 싶을 수도 있지만 모든 상황에서 작동하지 않을 수 있습니다. 컴파일러는 정렬 버퍼 공간을 구조에 추가 할 수 있으며 버퍼 공간에있는 메모리 위치에서 발견 된 값은 특정 값으로 보장되지 않습니다.

그러나 구조를 사용하기 전에 calloc또는 memset전체 크기를 사용하는 경우 구조 와 포인터를 비교할 때 얕은 비교를 수행 할 수 있습니다 (구조에 포인터가 포함 된 경우 포인터가 가리키는 주소가 동일한 경우에만 일치 함).memcmp


19
닫기는 "거의 모든"컴파일러에서 작동하지만 그다지 좋지는 않습니다. C90에서 6.2.1.6.4를 확인하십시오. "동일한 객체 표현을 가진 두 개의 값 (NaN 이외의 값)은 동일하게 비교되지만 동일한 값을 비교하는 값은 다른 객체 표현을 가질 수 있습니다."
Steve Jessop

22
"BOOL"필드를 고려하십시오. 동등성의 관점에서, 0이 아닌 BOOL은 0이 아닌 모든 BOOL 값과 같습니다. 따라서 1과 2는 모두 참이어서 동일 할 수 있지만 memcmp는 실패합니다.
ajs410

4
@JSalazar 아마도 당신을 위해 더 쉬울 것이지만, 컴파일러와 CPU를 위해 훨씬 더 어려워서 훨씬 느리다. 왜 컴파일러가 처음에 패딩을 추가한다고 생각합니까? 확실히 아무것도 메모리를 낭비하지 마십시오;)
Mecki

4
@Demetri : 예를 들어, float 값의 양수와 음수의 0은 IEEE float 구현에서 동일하지만 객체 표현이 동일하지 않습니다. 따라서 실제로 "거의 모든 컴파일러"에서 작동한다고 말해서는 안되며, 음수 0을 저장할 수있는 구현에서는 실패합니다. 나는 의견을 말할 때 재미있는 정수 표현을 생각하고 있었을 것입니다.
Steve Jessop

4
@Demetri : 그러나 많은 사람들이 float을 포함하고 있으며, 질문자는 "float를 포함하지 않는 구조체를 어떻게 비교합니까?"가 아니라 "struct를 어떻게 비교합니까?" 이 대답은 memcmp메모리가 먼저 지워졌다면 얕은 비교를 할 수 있다고 말합니다 . 작동에 가깝지만 올바르지 않습니다. 종종 질문은 "평등"을 정의하지 않으므로 "객체 표현의 바이트 단위 평등"을 의미 memcmp하는 경우 메모리가 지워 졌는지 여부에 관계없이 정확하게 수행됩니다.
Steve Jessop

22

많이한다면 두 구조를 비교하는 함수를 작성하는 것이 좋습니다. 이렇게하면 구조를 변경 한 경우 한 곳에서 비교 만 변경하면됩니다.

방법에 관해서는 .... 모든 요소를 ​​개별적으로 비교해야합니다.


1
한 번만 사용하더라도 별도의 함수를 작성합니다.
Sam

18

구조체의 필드 사이에 잠재적 인 임의의 패딩 문자로 인해 구조체의 동등성을 비교하기 위해 memcmp를 사용할 수 없습니다.

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

위와 같은 구조체에서는 위의 내용이 실패합니다.

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

안전을 위해 멤버 별 비교를 사용해야합니다.


25
더블 후 패딩 될 가능성이 적습니다. 문자는 더블 직후에 완벽하게 적절하게 정렬됩니다.
Jonathan Leffler

7

@Greg는 일반적인 경우 명시적인 비교 함수를 작성해야합니다.

다음과 같은 memcmp경우 에 사용할 수 있습니다.

  • 구조체에는 가능한 부동 소수점 필드가 없습니다 NaN.
  • 구조체에 패딩이 포함되어 있지 않거나 ( -Wpaddedclang을 사용 하여이를 확인) 구조체를 초기화 할 memset때 명시 적으로 초기화합니다.
  • BOOL고유하지만 동등한 값을 갖는 멤버 유형 (예 : Windows )이 없습니다.

임베디드 시스템을 프로그래밍하거나 (또는 ​​그에 사용될 라이브러리를 작성하지 않는 한) C 표준의 일부 경우에 대해 걱정하지 않을 것입니다. 근거리 포인터와 원거리 포인터 구별은 32 비트 또는 64 비트 장치에 존재하지 않습니다. 내가 아는 내장형 시스템에는 여러 개의 NULL포인터가 없습니다.

다른 옵션은 등식 함수를 자동 생성하는 것입니다. 구조체 정의를 간단한 방법으로 배치하면 간단한 텍스트 처리를 사용하여 간단한 구조체 정의를 처리 할 수 ​​있습니다. 일반적인 경우에는 libclang을 사용할 수 있습니다. Clang과 동일한 프런트 엔드를 사용하기 때문에 모든 코너 경우를 올바르게 처리합니다 (버그 제외).

그런 코드 생성 라이브러리를 보지 못했습니다. 그러나 비교적 단순 해 보입니다.

그러나 이와 같이 생성 된 등식 함수가 종종 응용 프로그램 레벨에서 잘못된 일을하는 경우도 있습니다. 예를 들어, UNICODE_STRINGWindows에서 두 개의 구조체를 얕게 또는 깊게 비교 해야 합니까?


2
struct memset등으로 구조체를 명시 적으로 초기화 한다고해서 struct 요소에 추가로 쓴 후 패딩 비트의 값을 보장 할 수는 없습니다. stackoverflow.com/q/52684192/689161
gengkev

4

모든 멤버를 한 번에 초기화하지 않는 한 패딩에 대해 걱정하지 않고 정적이 아닌 구조에서 memcmp ()를 사용할 수 있습니다. 이것은 C90에 의해 정의됩니다.

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


1
{0, }패딩 바이트도 0으로 지정하는 것이 실제로 지정 되었습니까?
Alnitak

GCC는 위의 링크에서 설명한 것처럼 부분적으로 초기화 된 구조체의 패딩 바이트를 0으로 만들고, stackoverflow.com/questions/13056364/… 는 C11이 해당 동작을 지정한다는 내용을 자세히 설명합니다.
pixelbeat

1
멤버에게 할당 할 때 모든 패딩이 결정되지 않기 때문에 일반적으로별로 유용하지 않습니다
MM

2

묻는 질문에 따라 다릅니다.

  1. 이 두 구조체가 같은 객체입니까?
  2. 그들은 같은 가치가 있습니까?

이들이 동일한 객체인지 확인하려면 두 구조체의 포인터가 동일한 지 비교하십시오. 값이 같은지 일반적으로 찾으려면 깊은 비교를해야합니다. 여기에는 모든 멤버를 비교하는 것이 포함됩니다. 멤버가 다른 구조체에 대한 포인터 인 경우 해당 구조체에도 재귀가 필요합니다.

구조체에 포인터가없는 특수한 경우 memcmp를 수행하여 데이터의 의미를 알 필요없이 각각에 포함 된 데이터를 비트 단위로 비교할 수 있습니다.

각 멤버에 대해 '같음'이 무엇을 의미하는지 알아야합니다. int에게는 분명하지만 부동 소수점 값 또는 사용자 정의 형식에 대해서는 더 미묘합니다.


2

memcmp구조를 memcmp비교 하지 않고 이진을 비교하므로 구조체에 항상 가비지가 있으므로 비교에서 항상 False로 나타납니다.

요소별로 안전을 비교하면 실패하지 않습니다.


1
2 개의 구조체 변수가 calloc으로 초기화되었거나 memset에 의해 0으로 설정된 경우 2 개의 구조체를 memcmp와 비교할 수 있고 구조체 가비지에 대해 걱정할 필요가 없으므로 시간을 벌 수 있습니다.
MOHAMED

1

구조체에 프리미티브 만 포함되거나 엄격한 평등에 관심이 있다면 다음과 같이 할 수 있습니다.

int my_struct_cmp (const struct my_struct * lhs, const struct my_struct * rhs)
{
    memcmp (lhs, rsh, sizeof (struct my_struct))를 반환;
}

그러나 구조체에 다른 구조체 또는 공용체에 대한 포인터가 포함 된 경우 프리미티브를 올바르게 비교하고 다른 구조와 비교하여 적절하게 비교하는 함수를 작성해야합니다.

그러나 ADT 초기화의 일부로 memset (& a, sizeof (struct my_struct), 1)을 사용하여 구조의 메모리 범위를 0으로 설정해야합니다.


-1

2 구조체 변수가 calloc으로 초기화되거나 memset에 의해 0으로 설정된 경우 2 구조체를 memcmp와 비교할 수 있고 구조체 가비지에 대해 걱정할 필요가 없으므로 시간을 벌 수 있습니다


-2

이 호환 예제는 Microsoft Visual Studio의 #pragma pack 컴파일러 확장을 사용하여 구조 멤버를 최대한 단단히 패킹합니다.

#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.