2015-08-12 14 views
12

Tôi đang sử dụng uint16_t làm bộ đếm chuỗi trong giao thức mạng. Bộ đếm này thường kết thúc tốt đẹp như mong đợi. Khi người nhận nhận được một gói, nó sẽ kiểm tra bộ đếm này so với lần truy cập gần đây nhất để xem liệu đó là một gói mới hay một gói tin không đúng thứ tự.Có phát hiện thấy sự bao bọc chưa ký thông qua truyền tới hành vi chưa được xác định đã ký không?

Cần tính đến các yếu tố cần thiết khi so sánh số thứ tự. Vì vậy, ví dụ: nếu số thứ tự cuối cùng là 0x4000 thì số thứ tự từ 0x4001 đến 0xBFFF là mới hơn và số thứ tự từ 0xC000 đến 0xFFFF và từ 0x0000 đến 0x3FFF nhỏ hơn.

Cách tôi đang làm điều này là như sau:

uint16_t last; 
uint16_t current; 
... 
// read in values for last and current 
... 
if ((int16_t)(current - last) > 0) { 
    printf("current is newer\n"); 
} else { 
    printf("current is older (or same)\n"); 
} 

Bằng cách trừ hai và xử lý kết quả như một int16_t, tôi có thể dễ dàng nhận thấy đó là lớn hơn và bao nhiêu. Vì vậy, ví dụ nếu số thứ tự hiện tại ít nhất 5 ít hơn số cuối cùng, tức là ((int16_t)(current - last) < -5), tôi có thể giả định điều này không phải do sắp xếp lại gói dữ liệu bình thường và thả gói.

Tôi nhận thấy rằng bao bọc đã ký là không xác định, tuy nhiên trong trường hợp này tôi đang xử lý một giá trị chưa ký như đã ký vì lợi ích của việc so sánh. Điều này gọi hành vi không xác định, và nếu như vậy những gì sẽ là một cách tốt hơn để làm loại so sánh?

+0

Miễn là 'INT_MAX' lớn hơn' UINT16_MAX' (đúng trên hầu hết các nền tảng 32 hoặc 64 bit) sẽ không có bất kỳ hành vi không xác định nào, vì số học được thực hiện trên loại được quảng cáo. – EOF

+0

@Quentin: Nhưng nếu 'INT_MAX' lớn hơn' INT16_MAX', toán hạng sẽ được thăng hạng. – EOF

+0

@EOF Và tôi đã trả lời một câu hỏi về điều đó hai ngày trước. Xấu hổ: < – Quentin

Trả lời

6

Hành vi của out-of-phạm vi chuyển đổi được thực hiện xác định.

Tại sao bạn không chỉ tránh vấn đề này hoàn toàn và viết:

if (current != last && current - last < 0x8000) 
    printf("current is newer\n"); 
else 
    printf("current is older (or same)\n"); 

Lưu ý: Câu trả lời này chỉ áp dụng cho câu hỏi cụ thể liên quan đến uint16_t. Đối với các loại khác, mã khác nhau sẽ được yêu cầu.

+0

Sự cố với cách tiếp cận của bạn là nó đòi hỏi hằng số ở phía bên tay phải phụ thuộc vào loại thực tế của biến chưa ký. Điều đó càng khó chịu hơn trong một mẫu và không phải lúc nào cũng có thể/thực tiễn trong một macro. – Mehrdad

+1

Trên các hệ thống trong đó 'int' là 16 bit,' current-last' sẽ được đánh giá là 'uint16_t'. Trên các hệ thống trong đó 'int' là 17 bit hoặc lớn hơn, không đếm padding,' current-last' sẽ được đánh giá là (đã ký) 'int'. Để cộng và trừ, tình huống có thể được khắc phục bằng cách đúc tổng hoặc sự khác biệt trở lại để nhập 'uint16_t'. Trong trường hợp hiếm hoi, người ta muốn tính toán 16 bit thấp hơn của sản phẩm của hai giá trị 16 bit có thể vượt quá 46,340, trước tiên một nhân sẽ là một trong các số bằng '1u'. Với 'uint16_t x = 65533;', giá trị của '(uint16_t) (1u * x * x)' sẽ là 9, kể từ ... – supercat

+1

... 65533u * 65533 == 4294574089u và (uint16_t) 4294574089u == 9, nhưng một nỗ lực để thực hiện các tính toán như '(uint16_t) (x * x)' hoặc 'x * x' khi x là 46341 hoặc lớn hơn có thể gây ra một số trình biên dịch 32-bit để ném tất cả các khái niệm về thời gian và quan hệ nhân quả ra ngoài cửa sổ . Như điên như nó có vẻ, một số nhà biên dịch biên dịch con số đó kể từ khi tiêu chuẩn cho phép trình biên dịch để biến 'if (x <65000) foo (x); x * = x; 'thành' foo (x); x * = x; ', và sau này là" hiệu quả hơn ", họ nên cố gắng làm cho trình biên dịch của họ làm điều đó.Nếu người ta muốn 'if (x <65000) foo (x); x * = x; 'người ta phải viết nó' if (x <65000) foo (x); x * = 1u * x; ' – supercat

-1

Phép trừ sẽ diễn ra bằng cách sử dụng loại được quảng cáo trừ khi int là 16 bit trong trường hợp đó hành vi được thực hiện được xác định.

Để được ở bên an toàn, bạn có thể tính toán độ lệch tuyệt đối sử dụng một ternary:

current > last ? current - last : last - current

+2

Tiêu chuẩn dự thảo C11: '7.20.1.1 Các kiểu số nguyên chiều rộng chính xác 1 Tên typedef intN_t chỉ định một kiểu số nguyên có dấu với chiều rộng N, không có bit đệm và biểu diễn bổ sung của hai. Vì vậy, int8_t biểu thị một kiểu số nguyên đã ký với chiều rộng chính xác là 8 bit. [...] ' – EOF

+0

' current - last' không bao giờ được undefined –

+0

@EOF: nhận ra rằng, đã chỉnh sửa bánh của tôi, làm mất hiệu lực nhận xét của bạn, nhưng vẫn có thể sai về 16 bit int – Bathsheba

0

Bạn có thể truyền uint16_t s đến int32_t và thực hiện phép trừ.

if (((int32_t) current - (int32_t) last) > 0) { 
    printf("current is newer\n"); 
} else { 
    printf("current is older (or same)\n"); 
} 

Lưu ý: khi đúc để int16_t, uint16_t s lớn hơn 32767 sẽ hiển thị như tiêu cực (trong ký số nguyên bit quan trọng nhất thiết lập để 1 có nghĩa là một tiêu cực, trong các loại unsigned nó chỉ là một chút thường xuyên).

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