2013-12-17 1 views
5

초기화되지 않은 패딩 때문에 memset()이 아닌 구조체를 비교하는 데 memcmp()을 사용할 수 없다는 것을 알고 있습니다. 그러나, 내 프로그램에서 시작 부분에 몇 가지 다른 유형의 구조체가 있고, 구조체가 끝날 때까지 동일한 유형이 여러 개 있습니다. 내 생각은 처음 몇 가지 유형을 수동으로 비교 한 다음 동일한 유형의 구성원으로 남아있는 인접한 메모리 블록에 memcmp()을 사용하는 것이 었습니다.memcmp() 및 포인터 산술을 사용하여 C 구조체 비교

제 질문은 C 표준이 구조체 패딩을 어떻게 보장합니까? 일부 또는 모든 컴파일러에서이를 안정적으로 얻을 수 있습니까? C 표준은 동일한 유형 멤버간에 구조체 패딩을 삽입 할 수 있습니까?

gcc으로 의도 한대로 내 제안 된 솔루션을 구현하고 정확하게 작동하는 것 같다 :

슬프게도
#include <stdlib.h> 
#include <string.h> 
#include <stdio.h> 

struct foo 
{ 
    char a; 
    void *b; 
    int c; 
    int d; 
    int e; 
    int f; 
}; 

static void create_struct(struct foo *p) 
{ 
    p->a = 'a'; 
    p->b = NULL; 
    p->c = 1; 
    p->d = 2; 
    p->e = 3; 
    p->f = 4; 
} 

static int compare(struct foo *p1, struct foo *p2) 
{ 
    if (p1->a != p2->a) 
     return 1; 

    if (p1->b != p2->b) 
     return 1; 

    return 
     /* Note the typecasts to char * so we don't get a size in ints. */ 
     memcmp(
      /* A pointer to the start of the same type members. */ 
      &(p1->c), 
      &(p2->c), 
      /* A pointer to the start of the last element to be compared. */ 
      (char *)&(p2->f) 
      /* Plus its size to compare until the end of the last element. */ 
      +sizeof(p2->f) 
      /* Minus the first element, so only c..f are compared. */ 
      -(char *)&(p2->c) 
     ) != 0; 
} 

int main(int argc, char **argv) 
{ 
    struct foo *p1, *p2; 
    int ret; 

    /* The loop is to ensure there isn't a fluke with uninitialized padding 
    * being the same. 
    */ 
    do 
    { 
     p1 = malloc(sizeof(struct foo)); 
     p2 = malloc(sizeof(struct foo)); 

     create_struct(p1); 
     create_struct(p2); 

     ret = compare(p1, p2); 

     free(p1); 
     free(p2); 

     if (ret) 
      puts("no match"); 
     else 
      puts("match"); 
    } 
    while (!ret); 

    return 0; 
} 
+0

보조 : 포인터 비교가 0 또는 1을 반환하므로'memcmp()'는'memcmp()! = 0'을 사용하여 0 또는 1을 반환합니다. – chux

+0

@chux 좋은 아이디어, 제안에 감사드립니다. – John

답변

4

C의 보증은 없습니다. 실용적인 관점에서 볼 때 현재의 모든 C 구현에 대해 ABI의 일부로 사실이며 패딩을 추가하는 데는 아무런 목적이 없습니다 (예 : 버퍼 오버플로를 검사하는 데 사용할 수 없음). 심). 그러나 엄밀히 말하자면 "이식성"이 아닙니다.

0

, 당신은 구조를 제어 할 수 있습니다 (I 이제까지 들어 본 것을) 더 C 표준이 없다 심. 0로 초기화 할 val에서 모든 구성원의 원인이됩니다이

struct something val = { 0 }; 

처럼 자동 할당 초기화된다는 사실이있다. 그러나 그 사이의 패딩은 구현에 맡겨져 있습니다.

GCC의 __attribute__((packed))처럼 사용할 수있는 컴파일러 확장 기능이있어 대부분의 구조체 패딩을 제외하고는 대부분 없애 버릴 수 있지만 그 외에도 손실이 발생할 수 있습니다.

중요한 최적화가 없으면 대부분의 컴파일러가 대부분의 경우 구조 패딩을 추가하지 않으므로 GCC에서 이것이 작동하는 이유를 알 수 있습니다. 당신의 구조 회원이

struct something { char onebyte; int fourbyte; }; 

같은 이상한 정렬 문제가 발생할 경우가 컴파일러가 fourbyte 멤버의 정렬 요구 사항을 충족하기 위해 onebyte 멤버 후에 패딩을 추가하게됩니다 말했다

.

+1

: struct struct val = {0};은 첫 번째 멤버를 0으로 초기화 한 다음 나머지 멤버를 초기화합니다 (기본값 인 경우 0으로 설정 가능). 'struct something val = {};'은 기본 항목이 필수 멤버 일 수도 있고 그렇지 않을 수도 있기 때문에 기본 설정 인 모든 멤버를 기본값으로 초기화합니다. –

+0

@JerryJeremiah 사실,하지만 이것은 더 좋은 아이디어를 얻습니다. – randomusername

+0

'gdb'를 조사했을 때'char a' 다음에 7 개의 패딩 바이트가 추가 된 것을 발견했습니다. 시스템에서 전체 구조체를 32 바이트로 만들었습니다 (25 대신에 __attribute __ (__ packed __))). . struct 전체에 대해 간단한'memcmp()'를 사용했을 때, 그들은 당연히 같지 않았습니다. – John