2011-10-30 42 views
5

Câu hỏi này lần đầu tiên được lấy cảm hứng từ (bất ngờ) kết quả của mã này:c tiêu chuẩn và bitshifts

uint16_t t16 = 0; 
uint8_t  t8 = 0x80; 
uint8_t t8_res; 

t16 = (t8 << 1); 
t8_res = (t8 << 1); 

printf("t16: %x\n", t16); // Expect 0, get 0x100 
printf(" t8: %x\n", t8_res); // Expect 0, get 0 

Nhưng nó quay ra này có ý nghĩa:

6.5.7 Bitwise nhà khai thác dịch chuyển

chế

Mỗi phòng trong số toán hạng phải có số nguyên kiểu

Như vậy dòng ban đầu bối rối tương đương với:

t16 = (uint16_t) (((int) t8) << 1); 

Một chút IMHO phi trực quan, nhưng ít nhất cũng xác định.

Ok, tuyệt vời, nhưng sau đó chúng tôi làm:

{ 
uint64_t t64 = 1; 
t64 <<= 31; 
printf("t64: %lx\n", t64); // Expect 0x80000000, get 0x80000000 
t64 <<= 31; 
printf("t64: %lx\n", t64); // Expect 0x0, get 0x4000000000000000 
} 

// chỉnh sửa: sau khi tranh luận nghĩa đen tương tự như trên, sau đây sẽ tương đương:

t64 = (uint64_t) (((int) t64) << 31); 

// do đó sự nhầm lẫn của tôi/expectation [end_edit]

Bây giờ, chúng tôi nhận được kết quả trực quan, nhưng không phải những gì sẽ được bắt nguồn từ việc đọc (theo nghĩa đen) của tôi về tiêu chuẩn. Khi nào thì "xúc tiến loại tự động tiếp theo" này diễn ra như thế nào? Hoặc là có một giới hạn ở những nơi khác mà một loại không bao giờ có thể được giáng chức (mà sẽ có ý nghĩa?), Trong trường hợp đó, làm thế nào để các quy tắc khuyến mãi áp dụng cho:

uint32_t << uint64_t 

Kể từ khi tiêu chuẩn không nói cả hai đối số được thăng chức int; cả hai đối số có nên được quảng cáo cùng loại ở đây không?

// chỉnh sửa:

Cụ thể hơn, những gì nên kết quả của:

uint32_t t32 = 1; 
uint64_t t64_one = 1; 
uint64_t t64_res; 

t64_res = t32 << t64_one; 

// end chỉnh sửa

Câu trả lời cho câu hỏi trên đã được giải quyết khi chúng ta nhận ra rằng spec không yêu cầu quảng bá cho int cụ thể, thay vì một số integer type, mà uint64_t đủ điều kiện là.

// CLARIFICATION EDIT:

Ok, nhưng bây giờ tôi lại bị nhầm lẫn. Cụ thể, nếu uint8_t là một loại số nguyên thì tại sao nó được thăng cấp lên int? Nó dường như không có liên quan đến hằng int 1, như tập thể dục sau minh họa:

{ 
uint16_t t16 = 0; 
uint8_t t8 = 0x80; 
uint8_t t8_one = 1; 
uint8_t t8_res; 

t16 = (t8 << t8_one); 
t8_res = (t8 << t8_one); 

printf("t16: %x\n", t16); 
printf(" t8: %x\n", t8_res); 
} 

t16: 100 
t8: 0 

Tại sao (t8 < < t8_one) biểu hiện được phát huy nếu uint8_t là một kiểu số nguyên?

-

Để tham khảo, tôi đang làm việc từ ISO/IEC 9899: TC9, WG14/N1124 ngày 06 tháng 5, 2005. Nếu đó là lỗi thời và ai cũng có thể cung cấp một liên kết đến một bản sao gần đây , điều đó cũng được đánh giá cao.

+3

Tôi hoàn toàn không hiểu tại sao bạn nói '// Mong đợi 0x0' sau lần dịch thứ hai bên trái 31 bit ... –

+0

Tôi bối rối như Greg, nhưng sẽ mong đợi các loại sẽ không bao giờ tự động hạ cấp (thu hẹp), chỉ được quảng cáo (mở rộng). –

+0

Khi đọc tiêu chuẩn, t64 = (uint64_t) (((int) t64) << 31) là một dòng tương đương, trong trường hợp đó bit thiết lập sẽ bị mất để cắt ngắn int. – Pat

Trả lời

5

Ràng buộc trong §6.5.7 rằng "Mỗi toán hạng phải có loại số nguyên." là một ràng buộc có nghĩa là bạn không thể sử dụng toán tử dịch chuyển bit trên các loại không phải số nguyên như giá trị dấu phẩy động hoặc con trỏ. Nó không gây ra hiệu ứng bạn đang chú ý.

Phần rằng không nguyên nhân có hiệu lực là ở đoạn tiếp theo:

  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à kết quả của toán hạng bên trái được quảng bá.

Các chương trình khuyến mãi nguyên được mô tả trong §6.3.1.1:

  2.Sau đây có thể được sử dụng trong một biểu hiện bất cứ nơi nào một int hoặc unsigned int có thể được sử dụng:

  • Một đối tượng hoặc biểu hiện với một kiểu số nguyên có số nguyên chuyển đổi cấp bậc thấp hơn hoặc tương đương với cấp bậc intunsigned int.
  • Một trường bit loại _Bool, int, signed int hoặc unsigned int.

Nếu một int thể đại diện cho tất cả các giá trị của các loại gốc, giá trị được chuyển đổi thành một int; nếu không, nó được chuyển đổi thành một số unsigned int. Đây được gọi là các quảng cáo số nguyên . Tất cả các loại khác là không thay đổi theo các chương trình khuyến mãi số nguyên.

uint8_t có một cấp bậc thấp hơn so với int, vì vậy giá trị được chuyển đổi thành một int (kể từ khi chúng ta biết rằng một int phải có khả năng đại diện cho tất cả các giá trị của uint8_t, được đưa ra các yêu cầu về phạm vi của hai loại).

Quy tắc xếp hạng rất phức tạp, nhưng chúng đảm bảo rằng loại có xếp hạng cao hơn không thể có độ chính xác thấp hơn. Điều này có nghĩa là, các loại đó không thể được "giáng cấp" thành loại có độ chính xác thấp hơn bằng các quảng cáo số nguyên (có thể là uint64_t để được thăng cấp thành int hoặc unsigned int, nhưng chỉ nếu phạm vi loại này ít nhất của uint64_t).

Trong trường hợp uint32_t << uint64_t, quy tắc bắt đầu là "Loại kết quả là của toán hạng trái được quảng cáo". Vì vậy, chúng tôi có một vài khả năng:

  • Nếu int ít nhất là 33 bit, sau đó uint32_t sẽ được thăng int và kết quả sẽ là int;
  • Nếu int nhỏ hơn 33 bit và unsigned int ít nhất 32 bit, sau đó uint32_t sẽ được thăng cấp thành unsigned int và kết quả sẽ là unsigned int;
  • Nếu unsigned int nhỏ hơn 32 bit thì uint32_t sẽ không thay đổi và kết quả sẽ là uint32_t.

Mở máy tính để bàn và máy chủ triển khai phổ biến hiện nay, intunsigned int thường 32 bit, và do đó khả năng thứ hai sẽ xảy ra (uint32_t được thăng unsigned int). Trong quá khứ, nó phổ biến cho int/unsigned int là 16 bit và khả năng thứ ba sẽ xảy ra (uint32_t chưa được xác định trước).

Kết quả của ví dụ của bạn:

uint32_t t32 = 1; 
uint64_t t64_one = 1; 
uint64_t t64_res; 

t64_res = t32 << t64_one; 

Sẽ là giá trị 2 lưu trữ vào t64_res.Lưu ý rằng mặc dù điều này là không bị ảnh hưởng bởi thực tế là kết quả của biểu thức không phải là uint64_t - và ví dụ về một biểu thức sẽ bị ảnh hưởng là:

uint32_t t32 = 0xFF000; 
uint64_t t64_shift = 16; 
uint64_t t64_res; 

t64_res = t32 << t64_shift; 

kết quả ở đây là 0xf0000000.

Lưu ý rằng mặc dù các chi tiết khá phức tạp, bạn có thể đun sôi tất cả xuống đến một quy tắc khá đơn giản mà bạn nên lưu ý:

Trong C, số học không bao giờ được thực hiện trong các loại hẹp hơn int/ unsigned int.

+0

Ah .... rằng bit cuối cùng trong §6.3.1.1 giây 2 thực sự niêm phong nó cho tôi. Cũng như quy tắc tóm tắt cuối cùng của bạn. Mô hình tinh thần của tôi của C đã được nhiều hơn nữa của 'thúc đẩy theo yêu cầu' (do đó các câu hỏi uint8_t tacked vào cuối). Câu trả lời này làm cho rất nhiều ý nghĩa, cảm ơn bạn đã dành thời gian của bạn. -pat – Pat

7

Tôi nghĩ rằng nguồn gốc của sự nhầm lẫn của bạn có thể là hai câu sau đây là không tương đương:

  • Mỗi phòng trong số toán hạng có trách nhiệm loại nguyên
  • Mỗi phòng trong số toán hạng có trách nhiệm int loại

uint64_t là một loại số nguyên.

+2

Thật vậy, đây là nguồn gốc của sự nhầm lẫn của OP. –

+0

Rực rỡ! Cảm ơn bạn. – Pat

+1

@Pat: Lưu ý rằng 'uint8_t' cũng là một kiểu số nguyên, vì vậy quy tắc này không chịu trách nhiệm về hành vi mà bạn thấy trong ví dụ đầu tiên của mình. Vì [Jens Gustedt] (http://stackoverflow.com/questions/7947982/c-standard-and-bitshifts/7948145#7948145) nói rằng, đó là "chuyển đổi số học thông thường" gây ra các giá trị có loại hẹp hơn 'int' được quảng cáo (cho 'int' hoặc' unsigned'). – caf

3

Bạn đã tìm thấy quy tắc sai trong tiêu chuẩn: (Có liên quan là một cái gì đó như "loại chương trình khuyến mãi kiểu số nguyên thông thường áp dụng" .Đây là điều bạn đánh giá cho ví dụ đầu tiên. hơn int nó được thăng intuint64_t không có một cấp bậc nhỏ hơn int hay unsigned vì vậy không xúc tiến được thực hiện và các nhà điều hành << được áp dụng cho các biến uint64_t

Edit:.. Tất cả các loại số nguyên nhỏ hơn int được quảng bá cho số học. Đây chỉ là một thực tế của cuộc sống :) Có hay không uint32_t được quảng bá phụ thuộc vào nền tảng, bởi vì nó có thể có cùng thứ hạng hoặc cao hơn int (không được thăng hạng) hoặc xếp hạng nhỏ hơn (được quảng bá).

Liên quan đến toán tử << loại toán hạng bên phải không thực sự quan trọng, số đếm cho số bit là số còn lại (với các quy tắc trên). Quan trọng hơn đối với cái đúng là giá trị của nó. Nó không âm hoặc vượt quá chiều rộng của toán hạng trái (được thăng hạng).

+0

Đã chỉnh sửa câu hỏi chính vì ngắt dòng không được phép trong nhận xét, vui lòng xem? – Pat

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