2011-06-26 43 views
7

Giả sử tôi có một tập tin mã nguồn mẫu, test.c, mà tôi đang biên soạn như vậy:C khắc phục những hạn chế răng cưa (? Đoàn)

$ gcc -Wall -03

thử nghiệm. c trông giống như thế này ..

/// CMP128(x, y) 
// 
// arguments 
// x - any pointer to an 128-bit int 
// y - any pointer to an 128-bit int 
// 
// returns -1, 0, or 1 if x is less than, equal to, or greater than y 
// 
#define CMP128(x, y) // magic goes here 

// example usages 

uint8_t A[16]; 
uint16_t B[8]; 
uint32_t C[4]; 
uint64_t D[2]; 
struct in6_addr E; 
uint8_t* F; 

// use CMP128 on any combination of pointers to 128-bit ints, i.e. 

CMP128(A, B); 
CMP128(&C[0], &D[0]); 
CMP128(&E, F); 

// and so on 

chúng ta hãy cũng nói rằng tôi chấp nhận những hạn chế rằng nếu bạn vượt qua trong hai con trỏ chồng chéo, bạn sẽ có được tái xác định sults.

Tôi đã thử một cái gì đó như thế này (tưởng tượng các macro được định dạng đúng với dòng mới xuyệc ngược thoát vào cuối mỗi dòng)

#define CMP128(x, y) ({ 
    uint64_t* a = (void*)x; 
    uint64_t* b = (void*)y; 

    // compare a[0] with b[0], a[1] with b[1] 
}) 

nhưng khi tôi dereference một trong vĩ mô (a [ 0] < b [0]) Tôi nhận được thông báo "quy định hội nghị chấm dứt vi phạm nghiêm ngặt các quy tắc bí danh" từ gcc

Tôi đã nghĩ rằng bạn phải sử dụng các công đoàn để tham khảo một nơi duy nhất trong bộ nhớ theo hai cách khác nhau, vì vậy tiếp theo tôi đã thử một cái gì đó như

#define CMP128(x, y) ({ 
    union { 
     typeof(x) a; 
     typeof(y) b; 
     uint64_t* c; 
    } d = { .a = (x) } 
     , e = { .b = (y) }; 

    // compare d.c[0] with e.c[0], etc 
}) 

Ngoại trừ việc tôi nhận được lỗi chính xác giống như trình biên dịch về quy tắc bí danh nghiêm ngặt.

Vì vậy: có cách nào để thực hiện việc này mà không vi phạm nghiêm ngặt bí danh không, thực tế là COPYING bộ nhớ?

(may_alias đếm doesnt, nó chỉ cho phép bạn bỏ qua các quy tắc nghiêm ngặt-aliasing)

EDIT: sử dụng memcmp để làm điều này. Tôi đã bắt kịp các quy tắc bí danh và không nghĩ về nó.

+1

Để sử dụng công đoàn theo cách phù hợp tiêu chuẩn, bạn chỉ được phép đọc từ thành viên mà bạn đã viết lần cuối. –

+8

@Kerrek: không đúng - C99 cho phép loại thông qua các công đoàn, một chú thích đề cập rõ ràng điều này đã được thêm vào với TC3; tuy nhiên, mã của Todd vẫn không chính xác ... – Christoph

+0

@Christoph: C1x thậm chí còn tốt hơn, phụ lục J (UB) là cố định, cho phép đọc các byte tương ứng với thành viên viết cuối cùng (rõ ràng là mục đích trong C99, nhưng dường như phụ lục J bị bỏ qua sau đó). – ninjalj

Trả lời

5

Trình biên dịch chính xác như quy tắc bí danh được xác định bởi cái gọi là 'loại hiệu quả' của đối tượng (tức là vị trí bộ nhớ) mà bạn đang truy cập, bất kể ma thuật con trỏ. Trong trường hợp này, việc đánh dấu các con trỏ với một liên kết không khác với một phép diễn rõ ràng - bằng cách sử dụng phép toán thực sự thích hợp hơn vì tiêu chuẩn không đảm bảo rằng các kiểu con trỏ arbitary có các biểu diễn tương ứng, nghĩa là bạn không cần thiết tùy thuộc vào việc thực hiện hành vi.

Nếu bạn muốn tuân thủ tiêu chuẩn, bạn cần phải sao chép dữ liệu sang các biến mới hoặc sử dụng công đoàn trong khi khai báo các biến gốc.

Nếu số nguyên 128 bit của bạn là số lớn hoặc cuối nhỏ (tức là không kết hợp), bạn cũng có thể sử dụng memcmp() (trực tiếp hoặc sau khi phủ nhận giá trị trả về) hoặc tự so sánh từng byte : truy cập thông qua con trỏ của loại ký tự là một ngoại lệ đối với quy tắc bí danh.

+1

Tôi nên nghĩ đến memcmp. Đó là cơ bản chính xác những gì tôi muốn làm. –

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