2011-12-21 44 views
6

khi mã sau được biên dịch nó đi vào một vòng lặp vô hạn:So sánh char unsigned và EOF

int main() 
{ 
    unsigned char ch; 
    FILE *fp; 
    fp = fopen("abc","r"); 
    if(fp==NULL) 
    { 
     printf("Unable to Open"); 
     exit(1); 
    } 
    while((ch = fgetc(fp))!=EOF) 
    printf("%c",ch); 
    fclose(fp); 
    printf("\n",ch); 
    return 0; 
} 

Các trình biên dịch gcc cũng là cấp cho cảnh báo về việc lập

abc.c:13:warning: comparison is always true due to limited range of data type 

mã chạy tốt khi unsigned char là được thay thế bằng char hoặc int như dự kiến ​​tức là nó chấm dứt.
Nhưng mã cũng chạy tốt cho unsigned int. như tôi có tôi đã đọc trong EOF được định nghĩa là -1 trong stdio.h thì tại sao mã này không thành công cho unsigned char nhưng chạy tốt cho int không dấu.

+2

cơ bản giống như [fgetc không xác định EOF] (http://stackoverflow.com/questions/3977223/fgetc-does-not-identify-eof). Tôi nghĩ rằng chúng tôi có loại câu hỏi này ít nhất một lần một tuần. –

+2

Xem thêm http://c-faq.com/stdio/getcharc.html –

+0

Bản sao có thể có của [Tại sao biến phải sử dụng để giữ giá trị trả về của getchar được khai báo là int?] (http://stackoverflow.com/questions/18013167/why-must-the-variable-used-to-hold-getchars-return-value-be-declared-as-int) –

Trả lời

7

Các quy tắc vàng để viết dòng này là

while ((ch = fgetc(stdin)) != EOF) 

ch nên int .Cộng lừa dễ thương làm ch unsigned thất bại vì EOF là một số lượng int ký kết.

Ok, bây giờ chúng ta đi vào chiều sâu ......

Bước 1:

ch=fgetc(fp) 

fgetc() lợi nhuận -1 (một ký int). Theo các quy tắc vàng của C ch lấy octet cuối cùng của các bit là tất cả các số của 1. Và do đó giá trị 255. Các mô hình byte của ch sau khi thực hiện

ch = fgetc(fp); 

sẽ do đó được

11111111 

Bước 2:

ch != EOF 

Bây giờ EOF là một số nguyên ch là một unsigned char ...

Một lần nữa tôi tham khảo quy tắc vàng của C ...anh chàng nhỏ hơn ch được chuyển thành kích thước lớn int trước khi so sánh nên mẫu byte của nó bây giờ là

00000000000000000000000011111111 = (255)10 

khi EOF

11111111111111111111111111111111 = (-1)10 

Không có cách nào họ có thể được tính bằng ... .... Do đó, tuyên bố để chỉ đạo những điều sau đây trong khi vòng lặp

while ((ch = fgetc(stdin)) != EOF) 

sẽ không bao giờ đánh giá sai ...

Và do đó vòng lặp vô hạn.

+1

Quy tắc vàng là ** luôn khớp với dấu ngoặc đơn của bạn **. Bạn đang thiếu một ')' trong cả hai ví dụ, nó phải là 'while ((ch = fgetc (stdin))! = EOF)'. – Jens

+0

Tôi chỉ làm cho câu trả lời rõ ràng của bạn trông đẹp hơn một chút. Tuy nhiên, bạn có thể vui lòng giải thích về những gì bạn muốn thể hiện bạn mẫu này: '... = (255) 10' và' ... = (-1) 10'? – alk

+0

@alk Câu trả lời này được viết bởi "sinh viên tôi", do đó không nhất quán trong các ký hiệu. (255) 10 biểu thị 255 trong cơ sở 10 – bashrc

1

bạn cần phải sử dụng một int

fgetc() trả về một int đặc biệt để nó có thể báo hiệu kết thúc tập tin

nó chạy tốt với ký char vì EOF (-1) nằm trong phạm vi các , nhưng nó sẽ phá vỡ nếu bạn đọc trong một char với giá trị lớn hơn 127.

Sử dụng một int, bỏ nó vào một char sau khi bạn đã kiểm tra cho EOF

+0

tôi biết int nên được sử dụng trong mã thích hợp nhưng tôi muốn biết lý do tại sao unsigned char không hoạt động nhưng không có công việc int ... –

+0

cách -1 nằm trong phạm vi int unsigned –

+2

Đọc trên các quy tắc khuyến mãi số nguyên. –

0

Khi bạn so sánh int chưa ký với int đã ký, nó chuyển đổi int đã ký thành int chưa ký và so sánh chúng. Do đó khi bạn đọc tập tin với một dấu 'int', đọc EOF cung cấp cho bạn 2^32 + 1 (trên máy int 4 byte) và khi so sánh nó với EOF, nó chuyển đổi EOF thành unsigned cũng là 2^32 + 1 và do đó chương trình dừng lại!

Nếu bạn sử dụng unsigned char ch, khi bạn đọc tệp, đọc EOF trả về 2^32 + 1, và điều này sẽ được đúc thành unsigned char, cắt ngắn giá trị thành 8 bit đầu tiên (trên máy char 1 byte) và cung cấp cho bạn một đầu ra của 255. Do đó bạn đang so sánh 255 và 2^32 + 1, gây ra một vòng lặp vô hạn.

Sự cố ở đây là cắt bớt trước khi so sánh.

Nếu bạn sử dụng

while((ch = fgetc(fp))!=(unsigned char)EOF) 
    printf("%c",ch); 

bạn chương trình sẽ chạy tốt!

6

Có một số chuyển đổi tiềm ẩn đang diễn ra. Chúng không thực sự liên quan đến cảnh báo cụ thể, nhưng tôi đưa chúng vào câu trả lời này để cho thấy trình biên dịch thực sự làm gì với biểu thức đó.

  • ch trong ví dụ của bạn là loại unsigned char.
  • EOF được đảm bảo thuộc loại int (C99 7.19.1).

Vì vậy biểu thức là tương đương với

(unsigned char)ch != (int)EOF 

Các quy tắc xúc tiến nguyên trong C ngầm sẽ chuyển đổi các char unsigned để unsigned int:

(unsigned int)ch != (int)EOF 

Sau đó, các quy tắc cân bằng (aka chuyển đổi số học thông thường) trong C sẽ ngầm chuyển đổi int thành unsigned int, bởi vì mỗi toán hạng phải có sa tôi loại:

(unsigned int)ch != (unsigned int)EOF 

Mở trình biên dịch EOF của bạn có khả năng -1:

(unsigned int)ch != (unsigned int)-1 

đó, giả sử CPU 32-bit, cũng giống như

(unsigned int)ch != 0xFFFFFFFFu 

Một nhân vật không bao giờ có thể có một giá trị cao như vậy, do đó cảnh báo.

2

Tôi cũng đã gặp sự cố này. Giải pháp của tôi là sử dụng feof().

unsigned int xxFunc(){ 
    FILE *fin; 
    unsigned char c; 
    fin = fopen("...", "rb"); 
    if(feof(fin) != 0) return EOF; 
    c = fgetc(fin); 
    fclose(fin); 
... 
} 

Và bạn có thể xác định biến int để so sánh với EOF. Ví dụ:

int flag = xxFunc(); 
while(flag != EOF) {...} 

Điều này phù hợp với tôi.

**CẬP NHẬT QUAN TRỌNG* **

Sau khi sử dụng các phương pháp tôi đã đề cập trước đây, tôi tìm thấy một vấn đề nghiêm trọng. feof() không phải là cách hay để ngắt vòng lặp while. Đây là lý do cho nó. http://www.gidnetwork.com/b-58.html

Vì vậy, tôi tìm cách tốt hơn để thực hiện việc này. Tôi sử dụng một biến int để làm điều đó. tại đây:

int flag; 
unsigned char c; 
while((flag = fgetc(fin)) != EOF) 
{ 
    //so, you are using flag to receive, but transfer the value to c later. 
    c = flag; 
    ... 
} 

Sau khi thử nghiệm, tính năng này hoạt động.

0

một cảnh báo lint được sản xuất với loại triển khai

So sánh kiểu 'char' với EOF

// read the data in a buffer 
611  ch = getc(csv_file); 
612  while (ch != EOF) 

FIX:

// read the data in a buffer 
    while ((ch = getc(csv_file)) != EOF) 
Các vấn đề liên quan