2012-01-11 37 views
15

Tôi đang cố sửa hai cảnh báo khi biên dịch một chương trình cụ thể bằng GCC. Những cảnh báo là:Khắc phục cho con trỏ loại bỏ cuộc hội nghị dereferencing sẽ phá vỡ bí danh nghiêm ngặt

cảnh báo: dereferencing type-punned con trỏ sẽ phá vỡ quy tắc nghiêm ngặt -aliasing [-Wstrict-aliasing]

và hai thủ phạm là:

unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf)); 

*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset); 

incoming_bufoutgoing_buf được định nghĩa như sau:

char     incoming_buf[LIBIRC_DCC_BUFFER_SIZE]; 

char     outgoing_buf[LIBIRC_DCC_BUFFER_SIZE]; 

Điều này có vẻ hơi khác so với các ví dụ khác về cảnh báo mà tôi đã được kiểm tra. Tôi muốn khắc phục sự cố thay vì tắt kiểm tra bí danh nghiêm ngặt.

Đã có nhiều đề xuất để sử dụng liên minh - điều gì có thể là một liên minh phù hợp cho trường hợp này?

+1

Thú vị ... bí danh nghiêm ngặt không được áp dụng cho 'char *'. Hay tôi đang thiếu một cái gì đó? – Mysticial

+3

@Mysticial Vâng, những gì bạn đang thiếu là không có sự vi phạm răng cưa khi một đối tượng kiểu 'T1' được truy cập với một giá trị của kiểu' T2' và 'T2' là' char', nhưng khi 'T1' là' char 'và' T2' không phải là một biến thể được ký/unsigned của 'char', có một sự vi phạm răng cưa. – ouah

+0

@ouah Bạn nên làm cho câu trả lời đó. – Mysticial

Trả lời

26

Trước hết, hãy xem xét lý do tại sao bạn nhận được cảnh báo vi phạm bí danh.

quy tắc Aliasing chỉ đơn giản nói rằng bạn chỉ có thể truy cập vào một đối tượng thông qua kiểu riêng của mình, ký/unsigned loại biến thể của nó, hoặc thông qua một loại nhân vật (char, signed char, unsigned char).

C cho biết vi phạm quy tắc bí danh gọi hành vi không xác định (vì vậy đừng!).

Trong dòng này của chương trình của bạn:

unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf)); 

mặc dù các yếu tố của mảng incoming_buf là loại char, bạn đang truy cập chúng như unsigned int. Thật vậy, kết quả của toán tử dereference trong biểu thức *((unsigned int*)dcc->incoming_buf) là loại unsigned int.

Điều này vi phạm quy tắc bí danh vì bạn chỉ có quyền truy cập các thành phần của incoming_buf mảng qua (xem tóm tắt quy tắc ở trên!) char, signed char hoặc unsigned char.

Chú ý bạn có chính xác cùng một vấn đề răng cưa ở thủ phạm thứ hai của bạn:

*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset); 

Bạn truy cập vào char yếu tố của outgoing_buf qua unsigned int, vì vậy nó là một vi phạm răng cưa.

giải pháp đề xuất

Để khắc phục vấn đề của bạn, bạn có thể thử để có các yếu tố của mảng của bạn được xác định trực tiếp trong các loại hình bạn muốn truy cập:

unsigned int incoming_buf[LIBIRC_DCC_BUFFER_SIZE/sizeof (unsigned int)]; 
unsigned int outgoing_buf[LIBIRC_DCC_BUFFER_SIZE/sizeof (unsigned int)]; 

(Bằng cách chiều rộng của unsigned int được thực hiện xác định, vì vậy bạn nên cân nhắc sử dụng uint32_t nếu chương trình của bạn giả định unsigned int là 32-bit).

Bằng cách này bạn có thể lưu trữ unsigned int đối tượng trong mảng của bạn mà không vi phạm các quy tắc răng cưa bằng cách truy cập phần tử thông qua các loại char, như thế này:

*((char *) outgoing_buf) = expr_of_type_char; 

hoặc

char_lvalue = *((char *) incoming_buf); 

CHỈNH SỬA:

Tôi đã hoàn toàn làm lại câu trả lời của mình, đặc biệt tôi giải thích lý do tại sao chương trình nhận được cảnh báo bí danh từ trình biên dịch.

+0

Điều đó dường như đã hoạt động. Tôi đã thực hiện các thay đổi đối với các mảng mà bạn đã đề xuất và thay đổi các tham chiếu khác thành incoming_buf ở nơi khác. Bây giờ các cảnh báo đã được sửa. Cảm ơn nhiều. – BlankFrank

+0

@BlankFrank bạn được chào đón! – ouah

11

Để khắc phục sự cố, không chơi chữ và bí danh! Các chỉ "đúng" cách để đọc một loại T là phải phân bổ một loại T và cư đại diện của mình nếu cần thiết:

uint32_t n; 
memcpy(&n, dcc->incoming_buf, 4); 

Tóm lại: Nếu bạn muốn một số nguyên, bạn cần phải thực hiện một số nguyên. Không có cách nào để lừa dối xung quanh điều đó theo một cách được gọi bằng ngôn ngữ.

Việc chuyển con trỏ chỉ mà bạn được phép (đối với mục đích của I/O, nói chung) là để điều trị địa chỉ của một biến hiện loại T như một char*, hay đúng hơn, là con trỏ đến phần tử đầu tiên của một loạt các ký tự có kích thước sizeof(T).

-2

Con trỏ truyền tới chưa được ký và sau đó quay lại con trỏ.

unsigned int received_size = ntohl (* ((unsigned *) ((unsigned) dcc-> incoming_buf)));

2
union 
{ 
    const unsigned int * int_val_p; 
    const char* buf; 
} xyz; 

xyz.buf = dcc->incoming_buf; 
unsigned int received_size = ntohl(*(xyz.int_val_p)); 

Giản giải thích 1. C++ tiêu chuẩn quốc gia là bạn nên cố gắng sắp xếp dữ liệu cho mình, g ++ đi một dặm thêm để tạo ra những cảnh báo về đề tài này. 2. bạn chỉ nên thử nó nếu bạn hoàn toàn hiểu sự liên kết dữ liệu trên kiến ​​trúc/hệ thống của bạn và bên trong mã của bạn (ví dụ mã trên là một điều chắc chắn trên Intel 32/64; liên kết 1; Win/Linux/Bsd/Mac) 3. lý do thực tế duy nhất để sử dụng mã ở trên là tránh cảnh báo trình biên dịch, KHI NÀO và NẾU bạn biết bạn đang làm gì