2013-07-24 31 views
5

Tôi đang cố gắng: - Để đọc lại giá trị nếu người dùng nhập giá trị không hợp lệ. Nhưng vấn đề là scanf() chỉ thực hiện một lần và sẽ không thực hiện bất kỳ thời gian và chương trình nào khác bị mắc kẹt với vòng lặp vô hạn.Quét sẽ không thực thi lần thứ hai

#include<stdio.h> 
#include<math.h> 
main() 
{ 
    unsigned int a; 
    unsigned int b = pow(2,M-1); 
    unsigned int c; 
    int x; 

    printf("b = %i",b); 

    input: 
    fflush(stdin); 
    fflush(stdout); 
    printf("\nEnter any integer: "); 
    x = scanf("%u",&a); 

    printf("%u",a); 
    if(x==0) 
     goto input; 

    printf("\na = %i",a); 

    c = a & b; 

    printf("\nc = %i",c); 

    if(c) 
     printf("\nthe bit %i is set",M); 
    else 
     printf("\nthe bit %i is not set",M); 
} 

Tôi đã thử sử dụng thêm không gian trước %u và cũng đã cố gắng fflush(stdin) nhưng không làm việc.

EDIT: Tôi biết rằng việc sử dụng goto không được khuyến khích nhưng tôi phải làm theo cách này. (Sử dụng vòng lặp không phải là một tùy chọn). M là một macro mà tôi định nghĩa bằng cách sử dụng dòng lệnh gcc tại thời gian biên dịch.

+0

bạn có thể sử dụng làm vòng lặp while trong trường hợp này. Vui lòng xem mã sửa đổi được đưa ra dưới đây – stev

Trả lời

9

Thận trọng: fflush(stdin); có thể là hành vi không xác định. Đọc: Why fflush(stdin) is wrong?

int fflush(FILE *ostream);
Các ostream điểm để một output stream hoặc một dòng cập nhật trong đó hoạt động gần đây nhất là không đầu vào, các fflush chức năng gây ra bất kỳ dữ liệu bất thành văn cho dòng đó sẽ được chuyển giao đến môi trường máy chủ để được ghi vào tệp; nếu không, số behaviorUndefined.

Bạn có thể thử một vòng lặp và đọc cho đến khi EOF hoặc \n cho this FAQ entry thay vì fflush(stdin) như tôi đã gợi ý dưới đây trong câu trả lời của tôi.

Edit: nhờ @Jonathan Leffler:

Có nền tảng nơi fflush(stdin) là hoàn toàn được xác định (như là một phần mở rộng phi tiêu chuẩn trên nền tảng đó). Ví dụ chính là một hệ thống các hệ thống nổi tiếng được gọi chung là Windows. Đặc điểm kỹ thuật của Microsoft về int fflush(FILE *stream);Nếu số stream mở để nhập, fflush xóa nội dung của bộ đệm.

Tôi có thêm sự nghi ngờ trong mã của bạn; M trong biểu thức unsigned int b = pow(2,M-1); là gì? Nó sẽ là một lỗi nếu bạn không xác định nó. Bạn có đăng mã hoàn chỉnh không?

Về bạn logic của phát hiện lỗi:

đọc lại giá trị nếu người dùng nhập vào một hợp lệ

Không, scanf() không trả lại một mã lỗi. Nó trả về số lượng chuyển đổi thành công.

int scanf (const char * format, ...);
Return Value
Mở thành công, hàm trả về số lượng các mục của danh sách đối số đầy thành công.Số này có thể khớp với số số lượng dự kiến ​​hoặc ít hơn (thậm chí là 0) do lỗi khớp, lỗi đọc hoặc phạm vi tiếp cận của tệp kết thúc.

Nếu một lỗi xảy ra đọc hoặc file end-of-đạt được trong khi đọc, chỉ số thích hợp được thiết lập (feof hoặc ferror). Và nếu xảy ra trước khi có thể đọc thành công bất kỳ dữ liệu nào, thì trả lại EOF.

Nếu lỗi mã hóa diễn giải các ký tự rộng, đặt chức năng errno đến EILSEQ.

Vì vậy, thực sự tùy thuộc vào lỗi gặp phải giá trị trả về có thể bằng không, EOF. Bạn nên sử dụng macro int ferror (FILE * stream);errno để phát hiện lỗi (kiểm tra ví dụ được đưa ra tại liên kết).

lỗi có thể vì đầu vào không hợp lệ có thể là:

EILSEQ: Input chuỗi byte không tạo thành một ký tự hợp lệ.
EINVAL: Không đủ đối số; hoặc định dạng là NULL.
ERANGE: Một chuyển đổi số nguyên sẽ vượt quá kích thước có thể được lưu trữ trong loại số nguyên tương ứng.

Kiểm tra scanf manual để biết danh sách đầy đủ.

Lý do vòng lặp vô hạn:

Hệ thống theo dõi trong đó đầu vào đã được nhìn thấy cho đến nay. Mọi cuộc gọi đến số scanf đều được chọn từ nơi người cuối cùng ngừng nhập dữ liệu khớp. Điều này có nghĩa là nếu xảy ra lỗi với scanf trước đó, thông tin nhập không khớp với vẫn chưa được đọc, như thể người dùng đã nhập trước. Nếu cẩn thận không được thực hiện để loại bỏ lỗi đầu vào, và một vòng lặp được sử dụng để đọc đầu vào, chương trình của bạn có thể bị bắt trong một vòng lặp vô hạn.

Vì vậy, ví dụ như trong mã của bạn:

x = scanf("%u", &a); 
     // ^
     // need a number to be input 

Nhưng giả sử bạn không nhập vào một số nhưng một chuỗi không hợp lệ được nhập ví dụ "name" (thay vì một số, như bạn nói). Điều này sẽ làm cho hàm scanf() bị lỗi khi cố gắng khớp một số nguyên không dấu ("%u") và từ "name" chưa được đọc. Vì vậy, lần sau thông qua vòng lặp, các scanf() không chờ đợi cho đầu vào người dùng mới, nó cố gắng để chuyển đổi "tên" một lần nữa.

Tương tự, nếu đầu vào là 29.67, các "%u" sẽ phù hợp với chỉ hai nhân vật đầu tiên (29), rời khỏi .67 đầu vào là chưa đọc cho cuộc gọi tiếp theo để scanf().

Ngay cả khi đầu vào là chính xác, như 29, dòng mới đã kết thúc dữ liệu nhập vẫn chưa đọc.Thông thường, đó không phải là vấn đề vì hầu hết các chuyển đổi tự động bỏ qua không gian trắng hàng đầu, chẳng hạn như dấu dòng mới từ dòng trước đó. Tuy nhiên một số chuyển đổi ("%c""%[") không bỏ qua bất kỳ khoảng trống trắng nào, vì vậy bạn phải thực hiện theo cách thủ công.

Để tránh vô hạn vòng lặp này Một gợi ý:

(hãy nhớ rằng: khi tôi khuyến cáo việc sử dụng ferror(), lỗi có giá trị là một lợi thế để phát hiện đầu vào không hợp lệ Thêm vào đó, nó chỉ cho mục đích học tập và nếu bạn. cần phải thực hiện một ứng dụng nghiêm trọng bạn nên sử dụng fgets(str) thay vì scanf() sau đó phân tích str đầu vào để xác minh xem đầu vào là hợp lệ)

input: 
    //fflush(stdout); //use if needed, as \n used in printf no need of fflush-stdout 
    printf("\nEnter any integer: "); 
    x = scanf("%u", &a); // always wait for new symbols 
    printf("%u", a); 

    if(x == 0){ // x=0, if error occurred 
     // read all unread chars 
     while ((ch = getchar()) != '\n' && ch != EOF); 
     goto input; 
    } 

Nó chỉ là một ý kiến ​​cho rằng sẽ p ossibly làm việc với mã của bạn (làm việc cho tôi, mã của bạn + gcc). Nhưng nếu bạn sử dụng kỹ thuật này không chính xác nó có thể để lại một lỗi trong mã của bạn:

đọc How do I flush the input buffer?

Nếu bạn chắc chắn rằng dữ liệu không mong muốn là trong dòng đầu vào, bạn có thể sử dụng một số đoạn mã sau các đoạn trích để xóa chúng. Tuy nhiên, nếu bạn gọi những điều này khi không có dữ liệu trong luồng đầu vào, chương trình sẽ đợi cho đến khi có, cho bạn kết quả không mong muốn.

+1

+1 cho điều này - * Bạn có thể thử một vòng lặp và đọc cho đến khi EOF hoặc \ n *, nó nêu rõ rằng OP nên trục xuất việc sử dụng 'goto' :) – 0decimal0

+2

. mạnh! Có những nền tảng mà 'fflush (stdin)' được định nghĩa đầy đủ (như là một phần mở rộng không chuẩn trên nền tảng đó). Ví dụ chính là một hệ thống các hệ thống nổi tiếng được gọi chung là Windows. Xem đặc tả của Microsoft về ['fflush()'] (http://msdn.microsoft.com/en-us/library/9yky46tz.aspx): ** Nếu luồng được mở để nhập, 'fflush' xóa nội dung của bộ đệm. ** –

+0

@JonathanLeffler Ok! bây giờ tôi có thể nhắc nhở Trước đây với turbo-C trong DOS ở trường đại học tôi đã sử dụng 'fflush (stdin)'. Và điều đó không sai! ~~ Cảm ơn! –

1

Đây là giải pháp để tránh vòng lặp vô hạn. Thay vì sử dụng goto, vui lòng sử dụng vòng lặp while:

do { 
    printf("\nEnter any integer: "); 
    x = scanf("%u",&a); 

    printf("%u",a); 
    if(x == 0) 
    { 
      char c; 
      printf("hit any key \n"); 
      c = getchar(); 
    } 

    } while(x==0); 
+1

** Không ** .... đó là chính xác –

+0

stev, 'x' được gán giá trị trả về bởi scanf(), nó scanf không quét một giá trị nó trả về 0, đọc: http://www.cplusplus.com/ tham khảo/cstdio/scanf/ –

+0

Grijesh, Vâng, tôi đã bỏ lỡ câu hỏi của anh ấy. scanf sẽ không trả về EOF trừ khi luồng đầu vào bị đóng - đó sẽ là giao diện điều khiển đóng. Vì vậy, nó chạy vào vòng lặp vô hạn. – stev