2011-09-25 36 views
7

Tôi đã đọc trong tiêu chuẩn C99 về các chuyển đổi số học thông thường.C chuyển đổi số học thông thường

Nếu cả hai toán hạng đều có cùng loại, thì không cần chuyển đổi thêm nữa là .

Nếu không, nếu cả hai toán hạng đã ký các kiểu nguyên hoặc cả hai đều có loại unsigned integer, các toán hạng với loại ít nguyên rank chuyển đổi được chuyển đổi sang kiểu của toán hạng với hơn xếp hạng.

Ngược lại, nếu các toán hạng có kiểu dữ liệu integer unsigned có rank lớn hơn hoặc bằng cấp bậc của các loại toán hạng khác, sau đó các toán hạng với kiểu số nguyên ký được chuyển đổi sang các loại của toán hạng với loại số nguyên không dấu.

Ngược lại, nếu kiểu của toán hạng với kiểu số nguyên ký thể đại diện cho tất cả các giá trị của các loại toán hạng với unsigned loại số nguyên, sau đó các toán hạng với kiểu dữ liệu integer unsigned được chuyển để loại toán hạng với kiểu số nguyên đã ký.

Nếu không, cả hai toán hạng sẽ được chuyển thành loại số nguyên không dấu tương ứng với loại toán hạng có loại số nguyên đã ký.

Vì vậy, chúng ta hãy nói rằng tôi có đoạn mã sau:

#include <stdio.h> 

int main() 
{ 
    unsigned int a = 10; 
    signed int b = -5; 

    printf("%d\n", a + b); /* 5 */ 
    printf("%u\n", a + b); /* 5 */ 
    return 0; 
} 

Tôi nghĩ đoạn in đậm áp dụng (từ unsigned intsigned int có cùng ngạch Tại sao không b chuyển đổi sang unsigned Hoặc có lẽ nó.? được chuyển thành unsigned nhưng có cái gì đó tôi không hiểu?

Cảm ơn bạn đã dành thời gian :-)

+0

Điều gì khiến bạn nghĩ rằng 'b' không được chuyển thành 'unsigned'? –

+0

@Charles Bailey Tôi đoán tôi đã ngu ngốc mong đợi kết quả khác nhau cho printfs: -? – user963368

+0

+5 là +5, bất kể int được ký hay chưa ký. –

Trả lời

5

0x0000000a cộng 0xfffffffb sẽ luôn là 0x00000005 bất kể bạn đang xử lý các loại đã ký hoặc chưa ký, miễn là chỉ sử dụng 32 bit.

+0

Vì vậy, bạn có nghĩa là nó tràn và được giảm modulo '2^32'? – user963368

+2

Nó tràn, và kết quả là nó xuất hiện như thể mô-đun đã được áp dụng. –

+0

Thực tế đối với tiêu chuẩn C tràn với ** số nguyên ** đã ký là hành vi không xác định (thậm chí nếu ngày nay tôi thực sự mong đợi mọi phần cứng sử dụng biểu diễn 2-bổ sung cho số nguyên). – 6502

0

Nó được chuyển đổi sang unsigned, số học chưa ký chỉ xảy ra để cho kết quả bạn thấy. Kết quả của số học không dấu là tương đương với số học đã ký với bổ sung của hai và không có ngoại lệ.

+0

Bạn đang nói về tràn chưa được ký? – user963368

+1

'unsigned' không tràn (theo tiêu chuẩn) nhưng chỉ kết thúc tốt đẹp. Điều này được xác định rõ trong tiêu chuẩn. –

5

Thực tế b được chuyển thành unsigned. Tuy nhiên những gì bạn quan sát là b chuyển đổi sang unsigned và sau đó thêm vào 10 mang lại cho là giá trị 5.

Trên x86 32bit đây là những gì xảy ra

  1. b, coverted để unsigned, trở thành 4294967291 (ví dụ:2**32 - 5)
  2. thêm 10 trở thành 5 vì bọc xung quanh tại 2**32 (2**32 - 5 + 10 = 2**32 + 5 = 5)
+0

Cảm ơn bạn đã trả lời. Đáng buồn là tôi không có đủ đại diện để +1. – user963368

3

Lặp lại phần liên quan của mã từ câu hỏi:

unsigned int a = 10; 
signed int b = -5; 

printf("%d\n", a + b); /* 5 */ 
printf("%u\n", a + b); /* 5 */ 

Trong a + b, b được chuyển thành int không dấu, (cho phép UINT_MAX + 1 - 5 theo quy tắc cho chuyển đổi chưa được ký). Kết quả của việc thêm 10 vào giá trị này là 5, theo các quy tắc của số học chưa ký, và kiểu của nó là unsigned int. Trong hầu hết các trường hợp, loại biểu thức C là độc lập với ngữ cảnh mà nó xuất hiện. (Lưu ý rằng không ai trong số này phụ thuộc vào các đại diện; chuyển đổi và số học được định nghĩa đơn thuần về mặt giá trị số.)

Đối với phần thứ hai printf cuộc gọi, kết quả là đơn giản: "%u" hy vọng một đối số kiểu unsigned int, và bạn' đã cho nó một. Nó in "5\n".

Đầu tiên printf phức tạp hơn một chút. "%d" hy vọng một đối số thuộc loại int, nhưng bạn đang cho nó một đối số thuộc loại unsigned int. Trong hầu hết các trường hợp, một kiểu không phù hợp như thế này dẫn đến hành vi không xác định, nhưng có một quy tắc đặc biệt tương ứng với các kiểu ký hiệu và không ký hiệu có thể hoán đổi thành các đối số hàm - miễn là giá trị có thể biểu diễn trong cả hai loại (vì nó ở đây) . Vì vậy, printf đầu tiên cũng in "5\n".

Một lần nữa, tất cả hành vi này được xác định theo giá trị, chứ không phải biểu diễn (ngoại trừ yêu cầu giá trị đã cho có cùng biểu diễn trong các loại đã ký và chưa ký tương ứng). Bạn sẽ nhận được kết quả tương tự trên một hệ thống có int int và unsigned int là cả hai bit 37 bit, int int có 7 bit padding, unsigned int có 11 bit padding và ký int sử dụng 1s'-complement hoặc sign-and-magnitude đại diện. (Không có hệ thống như vậy tồn tại trong đời thực, theo như tôi biết.)

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