Bối cảnh
Chúng tôi đang chuyển mã C ban đầu được biên dịch bằng trình biên dịch C 8 bit cho vi điều khiển PIC. Một thành ngữ phổ biến đã được sử dụng để ngăn các biến toàn cục không dấu (ví dụ: bộ đếm lỗi) quay trở về 0 là như sau:
if(~counter) counter++;
Toán tử bitwise ở đây đảo ngược tất cả các bit và câu lệnh chỉ đúng nếu counter
nhỏ hơn giá trị tối đa. Điều quan trọng, điều này hoạt động bất kể kích thước biến.
Vấn đề
Chúng tôi hiện đang nhắm mục tiêu bộ xử lý ARM 32 bit bằng GCC. Chúng tôi đã nhận thấy rằng cùng một mã tạo ra kết quả khác nhau. Theo như chúng tôi có thể nói, có vẻ như hoạt động bổ sung bitwise trả về một giá trị có kích thước khác với mong đợi của chúng tôi. Để tái tạo điều này, chúng tôi biên dịch, trong GCC:
uint8_t i = 0;
int sz;
sz = sizeof(i);
printf("Size of variable: %d\n", sz); // Size of variable: 1
sz = sizeof(~i);
printf("Size of result: %d\n", sz); // Size of result: 4
Trong dòng đầu ra đầu tiên, chúng ta nhận được những gì chúng ta mong đợi: i
là 1 byte. Tuy nhiên, phần bù bit của i
thực tế là bốn byte gây ra vấn đề vì so sánh với điều này bây giờ sẽ không cho kết quả như mong đợi. Ví dụ: nếu làm (nơi i
được khởi tạo đúng cách uint8_t
):
if(~i) i++;
Chúng ta sẽ thấy i
"bao quanh" từ 0xFF trở lại 0x00. Hành vi này khác với GCC so với khi nó được sử dụng để hoạt động như chúng tôi dự định trong trình biên dịch trước đó và vi điều khiển PIC 8 bit.
Chúng tôi biết rằng chúng tôi có thể giải quyết vấn đề này bằng cách sử dụng như vậy:
if((uint8_t)~i) i++;
Hoặc bằng cách
if(i < 0xFF) i++;
Tuy nhiên, trong cả hai cách giải quyết này, kích thước của biến phải được biết và dễ bị lỗi đối với nhà phát triển phần mềm. Những loại kiểm tra giới hạn trên xảy ra trong suốt cơ sở mã. Có rất nhiều kích cỡ của các biến (ví dụ., uint16_t
Và unsigned char
vv) và thay đổi này trong một codebase khác làm việc không phải là điều chúng ta mong muốn.
Câu hỏi
Sự hiểu biết của chúng ta về vấn đề có đúng không, và có các tùy chọn có sẵn để giải quyết vấn đề này không yêu cầu truy cập lại từng trường hợp mà chúng ta đã sử dụng thành ngữ này không? Giả định của chúng tôi có đúng không, rằng một hoạt động như bổ sung bitwise sẽ trả về một kết quả có cùng kích thước với toán hạng? Có vẻ như điều này sẽ phá vỡ, tùy thuộc vào kiến trúc bộ xử lý. Tôi cảm thấy như mình đang uống thuốc điên và C nên dễ mang theo hơn một chút so với thứ này. Một lần nữa, sự hiểu biết của chúng tôi về điều này có thể sai.
Nhìn bề ngoài, điều này có vẻ không phải là một vấn đề lớn nhưng thành ngữ hoạt động trước đây này được sử dụng ở hàng trăm địa điểm và chúng tôi rất muốn hiểu điều này trước khi tiến hành những thay đổi đắt giá.
Lưu ý: Có một câu hỏi trùng lặp có vẻ giống nhau nhưng không chính xác ở đây: Thao tác bit trên char cho kết quả 32 bit
Tôi đã không thấy mấu chốt thực sự của vấn đề được thảo luận ở đó, cụ thể là, kích thước kết quả của phần bổ sung bit bit khác với những gì được đưa vào toán tử.