2012-05-08 16 views
11

Học một cách khó khăn, tôi đã cố gắng dịch chuyển một số long longuint64_t đến hơn 32 bit trên máy x86 kết quả là 0. Tôi mơ hồ nhớ đã đọc một nơi nào đó hơn trên một nhà khai thác máy 32 bit chỉ hoạt động trên 32 bit đầu tiên nhưng không thể thu hồi nguồn. Tôi muốn biết là nếu chuyển hơn 32 bit của một số nguyên uint64_t trên máy x86 là một hành vi không xác định?Đang dịch chuyển hơn 32 bit của số nguyên uint64_t trên máy x86 Hành vi không xác định?

+3

Không nên. Bạn đang sử dụng trình biên dịch nào? –

+0

Bạn có nhớ mình đã cố gắng thay đổi bao nhiêu bit không? – RedX

+2

Hãy làm rõ ý của bạn bằng "m/c"? –

Trả lời

20

Tiêu chuẩn nói (6.5.7 trong n1570):

3 Các chương trình khuyến mãi số nguyên được thực hiện trên mỗi toán hạng. Loại kết quả là của toán hạng bên trái được quảng bá. Nếu giá trị của toán hạng bên phải là âm hoặc lớn hơn hoặc bằng chiều rộng của toán hạng trái được thăng hạng, hành vi không được xác định.

4 Kết quả của E1 < < E2 là các vị trí bit E2 dịch chuyển sang trái E1; các bit trống được fi lled với số không. Nếu E1 có loại không dấu, giá trị của kết quả là E1 × 2 E2, giảm modulo một giá trị lớn hơn giá trị lớn nhất thể hiện trong loại kết quả. Nếu E1 có giá trị loại có ký hiệu và giá trị không âm, và E1 × 2 E2 là thể hiện trong loại kết quả, thì đó là giá trị kết quả; nếu không, hành vi không được xác định.

5 Kết quả của E1 >> E2 là vị trí bit E2 được dịch chuyển sang phải E1. Nếu E1 có loại chưa ký hoặc nếu E1 có loại đã ký và giá trị không âm, thì giá trị của kết quả là phần tách rời của thương số E1/2 E2. Nếu E1 có loại đã ký và giá trị âm, thì giá trị kết quả là sẽ được thực hiện.

Chuyển một khoảng cách nhỏ hơn 64 bit được xác định hoàn toàn theo tiêu chuẩn.

long long phải ít nhất 64 bit, việc chuyển đổi long long giá trị nhỏ hơn 64 bit được xác định theo tiêu chuẩn cho giá trị không âm, nếu kết quả không tràn.

Lưu ý, tuy nhiên, nếu bạn viết một chữ phù hợp với 32 bit, ví dụ: uint64_t s = 1 << 32 được phỏng đoán bởi @drhirsch, bạn không thực sự chuyển giá trị 64 bit nhưng giá trị 32 bit. Đó là hành vi không xác định. Kết quả phổ biến nhất là thay đổi theo shift_distance % 32 hoặc 0, tùy thuộc vào phần cứng.

+0

+1. Đây là cách nó nên được. Trình biên dịch tuân thủ phải tuân thủ tiêu chuẩn C. – ArjunShankar

+1

@drhirsch chỉ ra vấn đề có thể là gì: một cái gì đó giống như 'uint64_t x = 1 << 33' – bames53

1

Dịch chuyển bằng một số bao gồm từ 0 đến tiền thân của chiều rộng của loại không gây ra hành vi chưa xác định, nhưng việc dịch chuyển trái số âm sẽ thực hiện. Bạn sẽ làm điều đó?

Mặt khác, phải dịch chuyển số âm được xác định thực hiện và hầu hết các trình biên dịch, khi các kiểu ký hiệu dịch chuyển phải, truyền bá bit dấu.

+0

Hầu hết các trình biên dịch thực hiện logic (chèn 0) phải thay đổi trên 'unsigned' và số học (bit chèn dấu) thay đổi ngay trên các biến' signed'. Ít nhất là bất kỳ trình biên dịch nào tôi đã từng sử dụng. – hirschhornsalz

+0

Việc dịch chuyển trái số âm không phải là hành vi không xác định; nó được thực hiện. Trong thực tế, nếu bộ xử lý có một lệnh mà sẽ ký mở rộng khi chuyển sang trái, tôi mong đợi trình biên dịch sẽ sử dụng nó; "thực hiện xác định" là để hỗ trợ bộ vi xử lý mà không có một hướng dẫn. –

+0

@JamesKanze C99 6.5.7: 4 "nếu không, hành vi không được xác định". Nếu bạn đang tìm kiếm một máy phân tích tĩnh sẽ (tùy ý) cảnh báo bạn nếu bạn chuyển sang trái một số âm, hãy xem liên kết trong tiểu sử của tôi. –

3

Tiêu chuẩn C yêu cầu ca làm việc chính xác. Một trình biên dịch lỗi đặc biệt có thể có lỗi mà bạn mô tả, nhưng đó là hành vi lỗi.

Đây là một chương trình thử nghiệm:

#include <stdio.h> 
#include <inttypes.h> 

int main(void) 
{ 
    uint64_t x = 1; 
    for (int i = 0; i < 64; i++) 
     printf("%2d: 0x%.16" PRIX64 "\n", i, (x << i)); 
    return 0; 
} 

Đây là đầu ra trên một máy i686 chạy RHEL 5 với GCC 4.1.2, và cũng trên x86/64 máy (cũng chạy RHEL 5 và GCC 4.1. 2) và trên máy Mac x86/64 (chạy Mac OS X 10.7.3 với GCC 4.7.0). Vì đó là kết quả mong đợi, tôi kết luận rằng không có vấn đề cần thiết trên máy 32 bit, và GCC ít nhất đã không trưng bày bất kỳ lỗi nào như vậy kể từ GCC 4.1.2 (và có thể chưa bao giờ có lỗi như vậy).

0: 0x0000000000000001 
1: 0x0000000000000002 
2: 0x0000000000000004 
3: 0x0000000000000008 
4: 0x0000000000000010 
5: 0x0000000000000020 
6: 0x0000000000000040 
7: 0x0000000000000080 
8: 0x0000000000000100 
9: 0x0000000000000200 
10: 0x0000000000000400 
11: 0x0000000000000800 
12: 0x0000000000001000 
13: 0x0000000000002000 
14: 0x0000000000004000 
15: 0x0000000000008000 
16: 0x0000000000010000 
17: 0x0000000000020000 
18: 0x0000000000040000 
19: 0x0000000000080000 
20: 0x0000000000100000 
21: 0x0000000000200000 
22: 0x0000000000400000 
23: 0x0000000000800000 
24: 0x0000000001000000 
25: 0x0000000002000000 
26: 0x0000000004000000 
27: 0x0000000008000000 
28: 0x0000000010000000 
29: 0x0000000020000000 
30: 0x0000000040000000 
31: 0x0000000080000000 
32: 0x0000000100000000 
33: 0x0000000200000000 
34: 0x0000000400000000 
35: 0x0000000800000000 
36: 0x0000001000000000 
37: 0x0000002000000000 
38: 0x0000004000000000 
39: 0x0000008000000000 
40: 0x0000010000000000 
41: 0x0000020000000000 
42: 0x0000040000000000 
43: 0x0000080000000000 
44: 0x0000100000000000 
45: 0x0000200000000000 
46: 0x0000400000000000 
47: 0x0000800000000000 
48: 0x0001000000000000 
49: 0x0002000000000000 
50: 0x0004000000000000 
51: 0x0008000000000000 
52: 0x0010000000000000 
53: 0x0020000000000000 
54: 0x0040000000000000 
55: 0x0080000000000000 
56: 0x0100000000000000 
57: 0x0200000000000000 
58: 0x0400000000000000 
59: 0x0800000000000000 
60: 0x1000000000000000 
61: 0x2000000000000000 
62: 0x4000000000000000 
63: 0x8000000000000000 
1

Không sao đâu.

ISO 9899: 2011 các nhà khai thác dịch chuyển 6.5.7 Bitwise

Nếu giá trị của toán hạng bên phải là tiêu cực hay là lớn hơn hoặc bằng với độ rộng của các toán hạng trái thăng chức, các hành vi không xác định.

Đó không phải là trường hợp ở đây, vì vậy tất cả đều ổn và được xác định rõ.

4

Daniel Fischer's answer trả lời câu hỏi về đặc tả ngôn ngữ C. Đối với những gì thực sự xảy ra trên máy x86 khi bạn phát hành thay đổi theo số lượng biến, hãy tham khảo Intel Software Developer Manual Tập 2B, tr. 4-506:

Số đếm được che thành 5 bit (hoặc 6 bit nếu ở chế độ 64 bit và REX.W được sử dụng). Phạm vi đếm được giới hạn ở 0 đến 31 (hoặc 63 nếu chế độ 64 bit và REX.W được sử dụng).

Vì vậy, nếu bạn cố gắng dịch chuyển số tiền lớn hơn 31 hoặc 63 (đối với giá trị 32 và 64 bit), phần cứng sẽ chỉ sử dụng 5 hoặc 6 bit dưới cùng của số tiền thay đổi. Vì vậy, mã này:

uint32_t RightShift(uint32_t value, uint32_t count) 
{ 
    return value >> count; 
} 

Sẽ cho kết quả RightShift(2, 33) == 1 trên x86 và x86-64. Nó vẫn còn hành vi không xác định theo tiêu chuẩn C, nhưng trên x86, nếu trình biên dịch biên dịch xuống hướng dẫn sar, nó sẽ có hành vi được xác định trên kiến ​​trúc đó. Nhưng bạn vẫn nên tránh viết loại mã này phụ thuộc vào các quirks kiến ​​trúc cụ thể.

+0

Nhưng vì hành vi không xác định, trình biên dịch có thể đã quyết định rằng sự dịch chuyển thực sự KHÔNG THỂ xảy ra, và như vậy, thậm chí được phát ra. Vì vậy, nó không thực sự có ý nghĩa để nhìn xa hơn vào những gì lắp ráp sẽ làm. – hmijail