2013-12-17 26 views
5

Tôi biết rằng không thể sử dụng memcmp() để so sánh các cấu trúc chưa được memset() đến 0 vì đệm không được khởi tạo. Tuy nhiên, trong chương trình của tôi, tôi có một cấu trúc với một vài loại khác nhau khi bắt đầu, sau đó vài chục cùng loại cho đến khi kết thúc cấu trúc. Suy nghĩ của tôi là so sánh thủ công một vài loại đầu tiên, sau đó sử dụng memcmp() trên khối bộ nhớ tiếp giáp còn lại của cùng một thành viên đã nhập.So sánh cấu trúc trong C bằng cách sử dụng memcmp() và số học con trỏ

Câu hỏi của tôi là, tiêu chuẩn C đảm bảo về cấu trúc đệm là gì? Tôi có thể đạt được điều này một cách đáng tin cậy trên bất kỳ hoặc tất cả các trình biên dịch không? Liệu các tiêu chuẩn C cho phép cấu trúc đệm được chèn vào giữa các thành viên cùng loại?

tôi đã thực hiện giải pháp đề xuất của tôi, và có vẻ như để làm việc chính xác như dự định với 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

Nhỏ: Vì so sánh con trỏ của bạn đang trả về 0 hoặc 1, gợi ý việc đảm bảo 'memcmp()' trả về 0 hoặc 1 với 'memcmp()! = 0'. – chux

+0

@chux Ý tưởng hay, cảm ơn đề xuất. – John

Trả lời

4

Không đảm bảo điều này trong tiêu chuẩn C. Từ quan điểm thực tế, nó là một phần của ABI cho mỗi lần thực hiện C hiện tại, và dường như không có mục đích thêm đệm (ví dụ như nó không thể được sử dụng để kiểm tra tràn bộ đệm, vì chương trình phù hợp được phép ghi vào padding). Nhưng nghiêm túc nói nó không phải là "di động".

0

Đáng buồn thay, không có tiêu chuẩn C (mà tôi đã từng nghe nói về) cho phép bạn kiểm soát cấu trúc đệm. Có một thực tế là phân bổ tự động được khởi tạo như thế này

struct something val = { 0 }; 

sẽ gây ra tất cả các thành viên trong val được khởi tạo để 0. Nhưng đệm ở giữa là để thực hiện.

Có phần mở rộng trình biên dịch bạn có thể sử dụng như GCC's __attribute__((packed)) để loại bỏ hầu hết nếu không phải tất cả cấu trúc đệm, nhưng ngoài việc bạn có thể bị thua lỗ. Tôi cũng biết rằng nếu không có tối ưu hóa lớn tại chỗ, hầu hết các trình biên dịch sẽ không bận tâm để thêm cấu trúc đệm trong hầu hết các trường hợp, mà sẽ giải thích tại sao điều này hoạt động theo GCC.

Điều đó nói rằng, nếu các thành viên cấu trúc của bạn gây ra các vấn đề liên kết kỳ lạ như thế này

struct something { char onebyte; int fourbyte; }; 

họ sẽ gây ra trình biên dịch để thêm đệm sau khi onebyte thành viên để đáp ứng các yêu cầu liên kết của các thành viên fourbyte.

+1

Điều này: 'struct something val = {0};' khởi tạo thành viên đầu tiên 0 và sau đó mặc định khởi tạo các thành viên còn lại (có thể với 0 nếu đó là mặc định của họ). 'struct something val = {};' mặc định inializes tất cả các thành viên đó là chung chung hơn vì mục đầu tiên có thể hoặc không thể là một thành viên không thể thiếu. –

+0

@JerryJeremiah đúng, nhưng điều này có ý tưởng tốt hơn. – randomusername

+0

Khi điều tra với 'gdb' tôi thấy rằng có 7 byte đệm được thêm vào sau' char a', làm cho toàn bộ cấu trúc 32 byte trên hệ thống của tôi (thay vì 25, là trường hợp với '__attribute __ ((__ packed __))') . Khi sử dụng một 'memcmp()' đơn giản trên toàn bộ cấu trúc, tất nhiên chúng không bằng nhau. – John

Các vấn đề liên quan