30/09/2018, 21:36

Gặp lỗi trong bài tập nhập thông tin sinh viên?

Hi mọi người. Em có làm 1 bài tập như sau: Nhập vào thông tin của nhiều sinh viên ( < 50 ) gồm họ và tên, số tuổi, điểm toán và điểm anh.
Đây là bài làm của em:

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <math.h>
#include <Windows.h>
#include <limits.h>
typedef struct diemthi DIEMTHI;
struct diemthi
{
    char hoten[50];
    int tuoi;
    float diemtoan;
    float diemanh;
};
void NhapDiem(DIEMTHI &x)
{
    printf("
Nhap ho va ten: ");
    gets(x.hoten);
    printf("Nhap so tuoi: ");
    scanf("%d",&x.tuoi);
    printf("Nhap diem toan: ");
    scanf("%f",&x.diemtoan);
    printf("Nhap diem tieng anh: ");
    scanf("%f",&x.diemanh);
}
void XuatDiem(DIEMTHI x)
{
    printf("
Ho va ten: ");
    puts(x.hoten);
    printf("So tuoi: %d
",x.tuoi);
    printf("So diem toan: %.1f
",x.diemtoan);
    printf("So diem tieng anh: %.1f
",x.diemanh);
}
void NhapMang(DIEMTHI a[], int n)
{
    for (int i = 0; i < n; i++)
    {
        printf("
Nhap thong tin sinh vien %d: ",i+1);
        NhapDiem(a[i]);
    }
}
void XuatMang(DIEMTHI a[], int n)
{
    for (int i = 0; i < n; i++)
    {
        printf("
Thong tin sinh vien %d:
", i+1);
        XuatDiem(a[i]);
    }
}
int main()
{
    int a[50];
    int n;
    do
    {
        printf("
Nhap so luong sinh vien: ");
        scanf("%d",&n);
        if (n < 0 || n > 50)
            printf("
So luong sinh vien khong hop le
");
    } while (n < 0 || n > 50);
    NhapMang(a, n);
    XuatMang(a, n);
    getch();
    return 0;
}

Nhưng khi chạy thì nó bị lỗi như thế này:

Mong các bạn giỏi giúp em nhé! Xin cảm ơn nhiều !

viết 23:52 ngày 30/09/2018

mảng a trong hàm main của bạn là kiểu int mà, còn a trong hàm NhapMang là kiểu DIEMTHI

Người bí ẩn viết 23:41 ngày 30/09/2018

Trời đất, sao sai cái lỗi “trên trời dưới đất” thế này
Cảm ơn bạn @freedom nhiều nhé

Cơ nhưng còn 1 vấn đề nữa là sao khi mình run lên, nó không cho nhập họ và tên mà nhập thẳng tuổi rồi điểm luôn nhỉ? Như picture dưới đây nè:

viết 23:38 ngày 30/09/2018

Vì sau khi bạn nhập n trong hàm main, trong sdtin vẫn còn ký tự '\n' nên khi vào hàm NhapDiem ký tự '\n' sẽ được hàm gets nhận vào x.hoten --> trôi ký tự.–> phải làm sạch stdin, thêm trước gets(...) đoạn này:

int c;
while((c=getchar())!='\n'&&c!=EOF);
Người bí ẩn viết 23:45 ngày 30/09/2018

Bạn có thể nói rõ tác dụng của đoạn code bạn xíu được không ? Giải quyết được vấn đề rồi đấy nhưng chưa hiểu code bạn lắm

viết 23:47 ngày 30/09/2018

Vòng lặp đầu tiên của hàm NhapMang là như mình nói lúc nãy. Sau khi kết thúc vòng lặp đầu tiên, trong stdin sẽ còn 3 ký tự '\n' ở các lần nhập x.tuoi, x.diemtoan, x.diemanh nên nếu không có đoạn code trên trong NhapDiem thì ở các vòng lặp tiếp theo cũng sẽ bị trôi không nhập được x.hoten. Còn về đoạn code đó, nó có ý nghĩa là lấy các ký tự ra khỏi stdin đến khi nào trong stdin không còn '\n' và khác EOF, dùng để làm sạch stdin thôi, tại ANSI-C không có hàm nào làm sạch stdin cả.

Người bí ẩn viết 23:39 ngày 30/09/2018

Cho hỏi ngu xíu, Không biết có liên quan đến C++ hay không mà mình chưa nghe tới stdin bao giờ

viết 23:41 ngày 30/09/2018

stdin là khái niệm trong C mà bạn, stdin là thiết bị nhập chuẩn (standard input) trong C được định nghĩa trong <stdio.h> có file descriptor là 0, được gắn với ngoại vi là bàn phím. C++ thì có dòng nhập chuẩn std::cin, chức năng cũng tương tự như stdin nhưng được định nghĩa trong namespace std, có type là std::istream.

Nguyễn Văn Cao viết 23:41 ngày 30/09/2018

Bác cho em hỏi là kí tự ‘\n’ bị thừa ra là do đâu vậy , em có thấy đoạn code nó nhập kí tự này đâu mà hàm gets lại nhận ạ ( bác chụp rõ cái dòng code gây ra lỗi này hộ em với được không ạ )

viết 23:49 ngày 30/09/2018

Khi bạn nhập dữ liệu vào từ bàn phím thì nó được đưa vào stdin, sau đó các hàm như scanf, gets sẽ lấy dữ liệu từ stdin vào chương trình chứ không đọc trực tiếp từ bàn phím. Bạn tưởng tượng như là khi bạn đọc ghi file vậy, stdin cũng là một file, nhưng là file đặc biệt của hệ thống do hệ điều hành cấp cho ngoại vi là bàn phím. Giả sử bạn nhập vào biến n cho chương trình, bạn phải bấm 3—>Enter, thì cả 3 và kí tự Enter('\n') đều được đẩy lên stdin, sau đó hàm scanf sẽ nhận 3 vào n còn ký tự '\n' vẫn còn trên stdin, sau đó hàm gets sẽ tiếp tục đọc ký tự này và lưu vào x.hoten.

Người bí ẩn viết 23:49 ngày 30/09/2018

Vòng lặp đầu tiên của hàm NhapMang là như mình nói lúc nãy. Sau khi kết thúc vòng lặp đầu tiên, trong stdin sẽ còn 3 ký tự '\n' ở các lần nhập x.tuoi, x.diemtoan, x.diemanh nên nếu không có đoạn code trên trong NhapDiem thì ở các vòng lặp tiếp theo cũng sẽ bị trôi không nhập được x.hoten

Nếu thế thì mấy vòng lặp tiếp theo trở đi ( i >= 1 ) mới bị lỗi không nhập được x.hoten chứ nhỉ?
Còn đằng này là vòng lặp đầu tiên ( i = 0 ) thì cũng bị lỗi không nhập được x.hoten ?

Sau khi kết thúc vòng lặp đầu tiên, trong stdin sẽ còn 3 ký tự '\n' ở các lần nhập x.tuoi, x.diemtoan, x.diemanh nên nếu không có đoạn code trên trong NhapDiem thì ở các vòng lặp tiếp theo cũng sẽ bị trôi không nhập được x.hoten

Nếu còn 3 ký tự '\n' thì :

Khi bạn nhập dữ liệu vào từ bàn phím thì nó được đưa vào stdin, sau đó các hàm như scanf, gets sẽ lấy dữ liệu từ stdin vào chương trình chứ không đọc trực tiếp từ bàn phím

cả thằng gets và 2 thằng scanf kia vẫn không chạy được chứ nhỉ ?

Tức là ý mình là sau khi chạy vòng lặp for đầu tiên ( i = 0 ) thì còn dư 3 ký tự '\n' của x.tuoi , x.diemtoanx.diemanh đúng không. Vậy sang vòng lặp for thứ 2 ( x = 1 ) thì do dư 3 ký tự '\n' chứ không phải 1 ký tự '\n' nên cả thằng gets và 2 thằng scanf phải bị lỗi luôn chứ, đằng này thì khi chạy chỉ có mỗi thằng gets bị lỗi ?

P/S: Do còn còn “ngu” và khó hiểu quá nên có thể mình “làm phiền” bạn 1 tí, hi vọng bạn giải thích rõ giùm mình nhé

viết 23:42 ngày 30/09/2018

Còn đằng này là vòng lặp đầu tiên ( i = 0 ) thì cũng bị lỗi không nhập được x.hoten ?

Tại vì ở vòng lặp đầu, sau khi bạn nhập n trong hàm main thì trong buffer vẫn còn '\n'

cả thằng gets và 2 thằng scanf kia vẫn không chạy được chứ nhỉ ?

Bạn đã bỏ đoạn code làm sạch buffer trước gets rồi nên cho dù có bao nhiêu ký tự '\n' liên tiếp còn lại trong stdin cũng bị lấy ra ngoài, nên gets vẫn nhập bình thường. Còn scanf bạn nhập với định dạng %d%f nên không nhận ký tự '\n' vào biến. Bạn debug thì thấy rõ mà.

Người bí ẩn viết 23:46 ngày 30/09/2018

Thì ra là thế

Bạn đã bỏ đoạn code làm sạch buffer trước gets rồi

Không, ý mình là lúc trước khi chưa bỏ đoạn code ấy, thì sau vòng lặp đầu tiên sẽ dư 3 ký tự '\n' , dẫn đến sang vòng lặp thứ 2 thì thằng gets sẽ hứng 3 ký tự '\n' đúng không ? Còn 2 thằng scanf kia do có định dạng %d%f rồi nên nó sẽ không hứng 2 ký tự '\n' còn lại đúng không ?

viết 23:39 ngày 30/09/2018

hằng gets sẽ hứng 3 ký tự ‘\n’ đúng không ?

Không bạn, gets sẽ kết thúc khi gặp phải '\n' chuỗi x.hoten sau khi thực hiện gets là một chuỗi rỗng, tức là chuỗi chỉ chứa ký tự kết thúc chuỗi '\0' còn hàm scanf thì như bạn nói

Người bí ẩn viết 23:47 ngày 30/09/2018

Mình hiểu rồi, thanks bạn nhiều nhé

Người bí ẩn viết 23:51 ngày 30/09/2018

tại ANSI-C không có hàm nào làm sạch stdin cả.

Mà bạn ơi. Mình quên mất cái trường hợp này !

Theo mình biết thì cái đoạn code của bạn được gọi là Xóa bộ nhớ đệm đúng không ?
Nếu thế thì để fflust(stdin) vẫn được chứ nhỉ

*grab popcorn* viết 23:50 ngày 30/09/2018

fflust(stdin)

fflush thực chất vốn ko cho các luồng vào (stdin chẳng hạn).
Nó cho các luồng ra (stdout, file out).
Như output buffer đang chứa các data mà chưa kịp ghi. Dùng fflush sẽ giúp “đẩy” nó ra.

Như ví dụ này

FILE *f;
f = fopen("bla.txt","w");
fprintf(f,"HEllo");
int c = getchar();
fclose(f);

Khoan hãy nhập vội để nó tiếp tục mà mở file bla.txt thì bạn sẽ thấy nó chả có gì ở trong đó.

Nhưng thêm cái fflush(f) ngay trc hàm getchar và mở file thì thấy ngay.

Vậy nên freedom đã cho bạn 1 vòng while nhỏ để clear buffer là vậy đó.

huyentrang viết 23:49 ngày 30/09/2018

cho em hỏi thêm là khi thêm đoạn code của bác vào thì khi run phải enter 2 lần mới run được tiếp là sao ạ

Bài liên quan
0