Lỗi exception thrown: variable are not initialized

Mình đang làm bài toán dự đoán số được ra ngẫu nhiên.
Khi đã đoán đúng số ngẫu nhiên đó thì được lựa chọn chơi tiếp hay không.
Quá trình chạy đoán số ko vấn đề gì chỉ đến khi chọn có chơi tiếp hay không. Nhấn y để chơi tiếp hoặc n để dừng. Trường hợp nhập n thì đã dừng như dự kiến còn muốn tiếp tục khi nhập y thì lỗi báo exception thrown: Run-Time Check Failure #3 - The variable ‘v’ is being used without being initialized..

Mong các cao nhân chỉ điểm dùm.

char play(int a, int b);

enum Status
{
	CONTINUE, WIN, END
};

int main(void)
{
	int x, y, rnumber;
	static char z;
	

	printf("%s", "I have a number between 1 and 1000.\nCan you guess my number?\nPlease type your first guess: ");
	srand(time(NULL));
	
	x = 1 + (rand() % 1000);
	scanf_s("%d", &y);
	
	z = play(x, y);

	while (END != z)
	{
		switch (z)
		{
		case CONTINUE:
			scanf_s("%d", &y);
			z = play(x, y);
			break;
		case WIN:
			x = 1 + (rand() % 1000);
			scanf_s("%d", &y);
			z = play(x, y);
			break;
		}
		
	}
	system("pause");
}

char play(int a, int b)
{
	enum Status v;
	static int i = 1;
	if (a!=b)
	{
		if (a<b)
		{
			printf("%d. Too high. Try again: ", i);
			i++;
			v = CONTINUE;
		}
		else if(a>b)
		{
			printf("%d. Too low. Try again: ", i);
			i++;
			v = CONTINUE;
		}
	}
	else
	{
		printf("%d. Excellent. Continue (y or n)?", i);
		i++;
		if (getchar() == 'y')
		{
			v = WIN;
		}
		else if (getchar() == 'n')
		{
			v = END;
		}
	}
	return v;
  1. Muốn sử dụng được hàm play() bạn phải đặt nó trước hàm main(), hoặc bạn phải khai báo prototype trc (có lẽ do IDE của bạn hỗ trợ như trên, nhưng theo chuẩn phải là như mình nói).

  2. Vì giá trị trả về của hàm play() là kiểu Status nên bạn cũng phải khai báo cho hàm play()Status play(int a, int b), và cả biến z nữa cũng phải kiểu Status luôn (vì bạn gán cho z = play()play() là kiểu Status.

  3. Bạn không nên đặt tên biến như là a, b, c mà hãy cho nó một cái tên đủ để hình dung ra được chức năng của nó là gì.

Vậy chắc hết r :slight_smile:.

6 Likes

Tip 1: đã thử thì IDE vẫn hỗ trợ kiểu khai báo ntn.
Tip 2: đã thử và kết quả còn tệ hơn, lỗi tràn trề. Đã


Tip 3: Đang học nên đặt thế cho tiện dù biết là về sau đi làm cần sửa.

Dù sao cũng cám ơn bạn :smiley:.

Bỏ cái switch(z) đâu mất tiêu rồi?

3 Likes

Tự dưng mất nhưng cũng vẫn lỗi char ko xài được với enum

Bạn làm đúng chưa? play() trả về Status.

3 Likes

Play() khai báo là char, z thì khai báo Status ?
Rồi gán 2 thằng bằng nhau nghĩa là thế nào ?
Nó đã chỉ lỗi rõ ràng dịch ra là biết :expressionless:

4 Likes

Nếu bạn đọc từ đầu thì chạy bằng char ko gặp vấn đề cho đến khi lựa chọn có tiếp tục chơi tiếp hay ko?
Còn đến đoạn sau thử các kiểu thay cả type của play() sang enum và các phần có liên quan thì ko hoạt động được luôn báo lỗi.
Do vậy việc đổi sang enum như bạn nói là lỗi complier hoàn toàn chính xác.
Trở lại vấn đề trên đầu, chương trình vẫn chạy được với char zchar play mình đang băn khoan tại sao các case continueend hoạt động tốt nhưng win lại ko tiếp tục vòng lăp được và báo lỗi exception á.

Cập nhật:
Như ở đây chạy chương trình đoạn case win có yêu cầu thêm có in thêm đoạn string nhưng ko hoạt động. scan số vẫn được nhg ko thực hiện lệnh để tiếp tục vòng lặp while chơi tiếp.

Đã xác định được bug. Bug này do lệnh getchar() gây nên.
Nếu xóa đi thì chương trình này hoạt động ngon nghẻ.
Theo mày mò mình thấy cái getchar() này khi call sẽ lấy luôn giá trị hoặc ký tự cuối của lệnh vừa kết thúc trước nó đâm ra làm sai lệch kết quả định hướng mong muốn.
Các cao nhân hay dùng câu lệnh nào lấy ký tự làm lựa chọn tiếp tục trong trường hợp này để ko lỗi ko. Mình có nghiên cứu trước đó về các lệnh lấy ký tự như gets(), getchr() thì báo ko có trong library stdio.hconio.h của C,C++ trong khi google thì vẫn có lệnh này ??? Mình dùng VS 2019.

1 Like
  • Hàm getchar() là hàm lấy một ký tự từ bộ đệm và trả về ký tự đó. Trc đó bạn nhập số mình đoán thì hàm scanf_s() của bạn nó chỉ lấy số từ bộ đệm và trong bộ đệm còn thừa lại ký tự \n (do bạn nhấn enter).

  • Để làm sạch bộ đệm thì có nhiều cách, dưới đây là 2 cách mà mình hay dùng (Nhớ đặt chúng trc getchar()):

    • fflush(stdin);
    • while (getchar() != '\n');
  • Còn đây là code mình đã sửa hầu hết các lỗi, về phần case WIN: của bạn là do thuật toán của bạn, sau khi Win thì phải đưa ra thông báo là chơi tiếp và phải reset i về 0. Code :point_down:


#include <time.h>
#include <stdlib.h>
#include <stdio.h>


enum Status {
	CONTINUE, WIN, END
};
enum Status play(int a, int b);

int main(void) {
	int x, y, rnumber;
	enum Status z;


	printf("%s", "I have a number between 1 and 1000.\nCan you guess my number?\nPlease type your first guess: ");
	srand(time(NULL));

	x = 1 + (rand() % 1000);
	scanf("%d", &y);

	z = play(x, y);

	while (END != z) {
		switch (z) {
			case CONTINUE:
				scanf("%d", &y);
				z = play(x, y);
				break;
			case WIN:
				printf("%s", "I have a number between 1 and 1000.\nCan you guess my number?\nPlease type your first guess: ");
				x = 1 + (rand() % 1000);
				scanf("%d", &y);
				z = play(x, y);
				break;
		}

	}
	system("pause");
}

enum Status play(int a, int b) {
	enum Status v;
	static int i = 1;
	if (a!=b) {
		if (a<b) {
			printf("%d. Too high. Try again: ", i);
			i++;
			v = CONTINUE;
		} else if(a>b) {
			printf("%d. Too low. Try again: ", i);
			i++;
			v = CONTINUE;
		}
	} else {
		printf("%d. Excellent. Continue (y or n)?", i);
		i++;
		fflush(stdin);
		char c = getchar();
		if (c == 'y') {
			i = 1;
			v = WIN;
		} else if (c == 'n') {
			v = END;
		}
	}
	return v;
}
4 Likes

Mình test thử fflush(stdin) thì vẫn lỗi tại case y bạn ạ.
Test sang scan số 0 và 1 như code dưới thì hoạt động trơn tru ko lỗi.

Ngoài ra nếu để getchar() ở hàm mẫu thì sẽ ko nhập từ bàn phím được, đơ luôn. Phải chuyển lên main thì còn bắt đầu nhập được.

Cái getchar() này hơi phốt quá :frowning:. Hóng cao nhân nào chỉ điểm.

``

Code trên là mình sửa trong code::blocks, nên chạy vẫn ngon, nhưng sang VS thì nó k cho gọi hàm scanf vì hàm này nó chả về giá trị int mà theo VS thì nó phải gán vào một biến nào đó. Và đây là code mình sửa lại trong VS.

#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <cstdlib>

enum Status {
	CONTINUE, WIN, END
};

enum Status play(int a, int b);

int main(void) {
	int x, y, rnumber;
	enum Status z;

	printf("%s", "I have a number between 1 and 1000.\nCan you guess my number?\nPlease type your first guess: ");
	srand(time(NULL));

	x = 1 + (rand() % 1000);
	scanf_s("%d", &y);

	z = play(x, y);

	while (END != z) {
		switch (z) {
		case CONTINUE:
			scanf_s("%d", &y);
			z = play(x, y);
			break;
		case WIN:
			printf("%s", "\nI have a number between 1 and 1000.\nCan you guess my number?\nPlease type your first guess: ");
			x = 1 + (rand() % 1000);
			scanf_s("%d", &y);
			z = play(x, y);
			break;
		}
	}
	system("pause");
}

enum Status play(int a, int b) {
	enum Status v = CONTINUE;
	static int i = 1;
	if (a != b) {
		if (a < b) {
			printf("%d. Too high. Try again: ", i);
			i++;
			v = CONTINUE;
		}
		else if (a > b) {
			printf("%d. Too low. Try again: ", i);
			i++;
			v = CONTINUE;
		}
	}
	else {
		printf("%d. Excellent. Continue (y or n)?", i);
		i++;
		fflush(stdin);
		char c = getchar();
		if (c == 'y') {
			i = 1;
			v = WIN;
		}
		else if (c == 'n') {
			v = END;
		}
	}
	return v;
}

Về mặt cú pháp thì code trên có thể nói khá là ngon, còn về phần thuật toán thì do bạn để phần hỏi chơi tiếp trong hàm play(), nên khi WIN thì nó in ra 2 lần hỏi muốn chơi tiếp không, do lần đầu là nó qua hàm play()case CONTINUE:, lần 2 là ở case WIN:.

5 Likes

Ok chạy được rồi copy nhầm sang bản lưu phụ.

1 Like

Cái code của bạn có vài vấn đề.

Thứ nhất trong switch của main, thiếu default. V được khai báo lúc đầu là chưa initialize. Thế có nghĩa là nếu nó không rơi vào 2 cái case thì nó vẫn là chưa initialize.

Thứ 2 trong hàm play thì tương tự. V không initialize khi nó khai báo. Trong khối if(a!=b), cứ coi là sẽ rơi vào trường hợp này thì code chạy đúng nhưng với a=b nó sẽ chạy vào else lớn.
Trong else lớn chỉ khởi tạo v khi ký tự nhập vào là ‘y’ và ‘n’. Thế khác thì sao ? Thì là v vẫn không xác định giá trị. Nếu rơi vào trường hợp này thì lệnh return v ở cuối hàm là sẽ return giá trị gì ?

Rõ ràng là thuật toán có lỗ hổng có thể dẫn tới ‘v’ is being used without being initialized…

Ở lần sửa code sau, v đã được init khi khai báo. Tất nhiên lỗ hổng được fix vì dù có rơi vào trường hợp nào thì v vẫn có 1 giá trị xác định.

5 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?