Tại sao không trộn các giá trị đã ký và Unsigned trong C / C ++?
Hầu hết các C / C ++ lập trình viên đã được cho biết để tránh pha trộn các giá trị đã ký kết và unsigned trong các biểu thức. Tuy nhiên - ít nhất là trong một phần bởi vì chúng ta thường làm theo lời khuyên này - nhiều người trong chúng ta không hoàn toàn trên đầu trang của các vấn đề tiềm ẩn. Chương trình này minh họa những gì có thể đi sai: #include
int main (void)
{
dài a = -1;
unsigned b = 1;
printf ("% d n", a> b);
return 0;
}
Đây là những gì xảy ra trên một x86-64 Linux hộp: [Regehr @ Gamow ~] $ gcc -o compare.c so sánh [Regehr @ Gamow ~] $ ./compare 0 [Regehr @ Gamow ~] $ gcc -o -m32 compare.c so sánh [Regehr @ Gamow ~] $ ./compare 1 Trong khác lời nói, sự bất bình đẳng là sai trên x64 và đúng sự thật trên x86. Nếu điều này không cung cấp cho bạn ít nhất một thời gian ngắn của "WTF?", Sau đó bạn đang làm tốt hơn rất nhiều so với tôi đã lần đầu tiên tôi nhìn thấy một cái gì đó như thế này xảy ra. Vấn đề cơ bản là một tính năng tương tác. Tính năng đầu tiên là chiến lược xúc tiến số nguyên C, mà bảo tồn các giá trị nhưng thường không giữ gìn signedness. Thông thường, các đối số cho bất kỳ toán tử số học được thăng int ký - hoặc một loại ký lớn hơn, nếu cần thiết, để làm cho các toán hạng có cùng kích thước. Tuy nhiên, nếu loại chứa các giá trị không biểu diễn trong thúc đẩy loại ký, loại thăng tiến là thay vì unsigned. Ví dụ, unsigned char và unsigned ngắn đều có thể được thăng int bởi vì tất cả các giá trị của họ có thể được đại diện trong một int. Mặt khác, unsigned int không có thể được thúc đẩy để int vì (ints giả định là 32 bit) giá trị như 2147483648 không phải là biểu diễn. Tính năng thứ hai là phương pháp C để lựa chọn phiên bản của một nhà điều hành để sử dụng. Mặc dù lớn hơn điều hành trong C luôn luôn trông giống như ">", đằng sau hậu trường có khá một vài nhà khai thác khác nhau: số nguyên ký kết>, số nguyên không dấu>, ký dài>, unsigned dài>, vv Nếu một trong hai toán hạng để "> ". là unsigned, sau đó một sự so sánh không dấu được sử dụng, nếu không so sánh được ký Bây giờ, quay trở lại ví dụ: trên một nền tảng 64-bit, b được thúc đẩy để ký kết lâu dài và ký kết ">" được sử dụng. Trên một nền tảng 32-bit, bởi vì "int" và "dài" có cùng kích thước, b vẫn unsigned, buộc các dấu ">" để được sử dụng. Điều này giải thích sự đảo chiều của cảm giác so sánh. Đăng ký các vấn đề có thể lẻn vào mã trong hai cách bổ sung. Đầu tiên, nó dễ dàng để quên rằng các hằng ký theo mặc định, ngay cả khi chúng được sử dụng trong một bối cảnh mà có vẻ như nó phải là unsigned. Thứ hai, kết quả của một toán tử so sánh luôn luôn là một int:. Một loại ký Một khi chúng ta đang nhận thức được những vấn đề này, nó không khó để suy nghĩ thông qua một câu đố như trên. Mặt khác, nó có thể rất khó khăn để gỡ lỗi các loại vấn đề trong một mảnh lớn của phần mềm, đặc biệt là kể từ khi vấn đề dấu hiệu có lẽ không phải là mục đầu tiên trong danh sách những nguyên nhân nghi ngờ gốc cho một lỗi. Khi viết phần mềm mới, đó là chắc chắn thận trọng để bật cảnh báo trình biên dịch về vấn đề dấu. Thật không may, GCC 4.4 không cảnh báo về các chương trình trên ngay cả khi cung cấp tùy chọn Wall. Các tùy chọn -Wsign-so sánh không đưa ra cảnh báo, nhưng chỉ khi tạo mã 32-bit. Khi tạo mã 64-bit, không có cảnh báo kể từ b được thăng một loại ký trước khi được tiếp xúc với ">" nhà điều hành. Vì vậy, nếu chúng ta đang phát triển chủ yếu trên nền tảng 64-bit, vấn đề có thể vẫn còn tiềm ẩn trong một thời gian. Chỉ cần để làm cho mọi việc thêm khó hiểu, một lần tôi theo dõi xuống một vấn đề mà các phiên bản của GCC đã được phát hành như là một phần của Ubuntu Hardy cho x86 miscompiled một chức năng rất tương tự như trên. Phải mất chương trình này và biên dịch nó để trở về 1: int foo (void) { ký char a = 1; unsigned char b = -1; trở lại a> b; } Ở đây cả hai giá trị cần được thúc đẩy để int ký và sau đó so sánh là (1> 255). Rõ ràng, trong trường hợp này là các trình biên dịch lỗi. Phiên bản cơ sở của GCC, 4.2.2, không có lỗi này. Tuy nhiên, người dân Ubuntu áp dụng khoảng 5 MB của bản vá lỗi cho trình biên dịch này trước khi đóng gói nó lên và bằng cách nào đó đã phá vỡ nó. Một vài năm trước, tôi thấy lỗi tương tự trong CIL và trong phiên bản đầu tiên của Clang. Rõ ràng, ngay cả các nhà phát triển trình biên dịch không được miễn dịch để ký / unsigned nhầm lẫn.
đang được dịch, vui lòng đợi..
