2011-12-07 27 views
12

Tôi biết, việc dịch chuyển đúng kiểu ký hiệu phủ định phụ thuộc vào việc triển khai, nhưng điều gì sẽ xảy ra nếu tôi thực hiện một thay đổi trái? Ví dụ:Các số nguyên âm được dịch chuyển trái và phải có được xác định là hành vi không?

int i = -1; 
i << 1; 

Điều này có được xác định rõ không?

Tôi nghĩ rằng tiêu chuẩn không nói về giá trị âm với loại ký

nếu E1 có một loại ký và giá trị không âm, và E1 × 2 E2 là biểu diễn trong các loại quả , thì đó là giá trị kết quả; nếu không, hành vi không xác định.

Nó chỉ làm rõ rằng nếu kết quả không thể biểu hiện trong loại đã ký thì hành vi không xác định.

+8

Phần nào không rõ ràng? * Nếu * E1 có giá trị không âm và ... * nếu không * hành vi không xác định. E1 có giá trị âm, do đó hành vi không xác định. Đúng là đôi khi người tiêu chuẩn có thể làm với một số dấu ngoặc phụ để làm cho nó hoàn toàn rõ ràng "khác" là gì, nhưng ở đây có nghĩa là "trong mọi tình huống chưa được mô tả". Nó giúp khi giải thích những điều này để nhớ rằng trong * bất kỳ * tình huống mà tiêu chuẩn không mô tả hành vi, sau đó hành vi là không xác định. Vì vậy, trong thực tế rằng "nếu không" là chính thức dự phòng. –

+0

@SteveJessop: Tôi nghĩ __otherwise__ có nghĩa là nếu giá trị không thể biểu diễn thì hành vi không xác định. Tôi không chắc chắn nếu điều này bao gồm E1 với giá trị âm. – user103214

+1

@Norman: Đúng vậy, hãy xem câu trả lời của R.Marthinho, có một dấu chấm phẩy, một người seperator rõ ràng giữa điều kiện và kết quả cuối cùng. – Xeo

Trả lời

24

Bạn không đọc câu đó một cách chính xác. Tiêu chuẩn xác định nó nếu: toán hạng bên trái có một loại đã ký giá trị không âm kết quả là đại diện (và trước đó trong cùng một đoạn xác định nó cho loại chưa ký). Trong tất cả các trường hợp khác (hãy chú ý đến số use of the semicolon trong câu đó), tức là, nếu bất kỳ điều kiện nào trong số này không được xác minh, hành vi này không được xác định.

+1

+1 để có giải thích tốt hơn. – Xeo

+1

+1 cho dấu chấm phẩy – Pubby

+0

Điều gì sẽ xảy ra nếu tôi có một số không âm và tôi phải dịch nó quá nhiều lần? 'int i = 1; i >> 24; 'có hợp lệ không? chỉ là hơi bối rối. – user103214

3

nếu không, hành vi không xác định.

này bao gồm các

nếu E1 có một loại ký và giá trị không âm

11

Khi các tiêu chuẩn C được mã hóa, các nền tảng khác nhau sẽ làm những việc khác nhau khi các số nguyên âm chuyển dịch trái. Trên một số người trong số họ, hành vi có thể kích hoạt bẫy thực hiện cụ thể mà hành vi của họ có thể nằm ngoài tầm kiểm soát của chương trình và có thể bao gồm việc thực thi mã ngẫu nhiên. Tuy nhiên, có thể các chương trình được viết cho các nền tảng như vậy có thể sử dụng các hành vi như vậy (một chương trình có thể chỉ định rằng người dùng sẽ phải làm gì đó để cấu hình trình xử lý bẫy của hệ thống trước khi chạy nó, nhưng sau đó chương trình có thể khai thác hành vi của các trình xử lý bẫy được cấu hình phù hợp). Các tác giả của tiêu chuẩn C không muốn nói rằng các trình biên dịch cho các máy nơi dịch chuyển trái của các số âm sẽ bẫy phải được sửa đổi để ngăn chặn bẫy như vậy (vì các chương trình có khả năng dựa vào nó), nhưng nếu trái- chuyển số âm được phép kích hoạt bẫy có thể gây ra bất kỳ hành vi tùy ý nào (bao gồm cả việc thực thi mã ngẫu nhiên) có nghĩa là việc dịch chuyển trái một số âm được phép làm bất cứ điều gì. Do đó hành vi không xác định. Trong thực tế, cho đến khoảng 5 năm trước, 99%% trình biên dịch được viết cho một máy sử dụng toán học bù hai (nghĩa là 99%% máy được tạo từ năm 1990) sẽ mang lại các hành vi sau đây cho x<<yx>>y, trong chừng mực mà sự phụ thuộc mã vào hành vi như vậy được coi là không di chuyển nhiều hơn mã được giả định là char là 8 bit. Tiêu chuẩn C không ủy thác hành vi như vậy, nhưng bất kỳ tác giả trình biên dịch nào muốn tương thích với một cơ sở rộng lớn của mã hiện có sẽ tuân theo nó.

  • nếu y là một loại ký, x << yx >> y được đánh giá là mặc dù y được đúc để unsigned.
  • nếu x là loại int, x<<y tương đương với (int)((unsigned)x << y).
  • nếu x là loại int và tích cực, x>>y tương đương với (unsigned)x >> y. Nếu x thuộc loại int và âm, x>>y tương đương với `~ (~ ((chưa ký) x) >> y).
  • Nếu x thuộc loại long, các quy tắc tương tự được áp dụng, nhưng với unsigned long thay vì unsigned.
  • nếu x là một loại N-bit và y lớn hơn N-1, sau đó x >> yx << y có thể tùy tiện mang lại không, hoặc có thể hành động như thể các toán hạng bên phải là y % N; chúng có thể yêu cầu thêm thời gian tỷ lệ thuận với y [lưu ý rằng trên máy 32 bit, nếu y là âm, có khả năng là thời gian dài, mặc dù tôi chỉ biết một máy thực tế chạy hơn 256 bước bổ sung ]. Các trình biên dịch không nhất quán trong sự lựa chọn của chúng, nhưng sẽ luôn trả về một trong các giá trị được chỉ định mà không có các tác dụng phụ khác.

Thật không may vì một lý do nào đó tôi không thể hiểu được, các nhà biên dịch đã quyết định thay vì cho phép lập trình viên chỉ ra những trình biên dịch giả định nào nên sử dụng để xóa mã chết, trình biên dịch sẽ không thể thực hiện bất kỳ thay đổi nào hành vi của họ không được bắt buộc bởi tiêu chuẩn C. đang do đó, được đưa ra như sau:

uint32_t shiftleft(uint32_t v, uint8_t n) 
{ 
    if (n >= 32) 
    v=0; 
    return v<<n; 
} 

một trình biên dịch có thể xác định rằng vì mã sẽ tham gia vào các hành vi undefined khi n là 32 hoặc lớn hơn, trình biên dịch có thể giả định rằng if sẽ không bao giờ trở thành sự thật, và có thể vì thế bỏ qua mã. Do đó, trừ khi hoặc cho đến khi một người nào đó đưa ra tiêu chuẩn cho C để khôi phục các hành vi kinh điển và cho phép các lập trình viên chỉ định những giả định nào đã loại bỏ mã chết, các cấu trúc như vậy không thể được đề xuất cho bất kỳ mã nào có thể được cung cấp cho trình biên dịch siêu hiện đại.

+1

Cảm ơn câu trả lời tuyệt vời này - ngay cả khi nó không được chấp nhận, nó rất hữu ích. –

+0

Một điều khác cần lưu ý là, không phải tất cả những người thay đổi thùng đều giống nhau. Tôi dường như nhớ lại ARM hành xử một chút khác với IA32 và các nền tảng khác, và lý do của nó không phải là một hoạt động ngầm ẩn chỉ sử dụng các bit thấp hơn của số lượng thay đổi. – jww

+0

@jww: Các nền tảng bao gồm hướng dẫn "shift-by-N" thường làm giảm giá trị thay đổi, nhưng trong nhiều trường hợp, giảm mod là bởi số tiền lớn hơn kích thước từ. Hơn nữa, các chip khác nhau trong một gia đình có thể hoạt động khác nhau. Hầu như tất cả các nhà chế biến, tuy nhiên, sẽ sử dụng một trong hai hành vi được liệt kê. – supercat

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