程序在C语言中的函数中修改二维数组后挂起。

huangapple go评论52阅读模式
英文:

Program hangs after modifying 2D array in a function in C

问题

程序是一个井字棋游戏,在从用户那里获取X和Y值之后会出现挂起的情况。

这是主文件:

#include <stdio.h>
#include <string.h>
#include "input.cpp"

int makeMove(char** board, char* moveX, char* moveY, char playerMove) {
	printf("请输入你的X坐标:");
	int answerX = (int)getNum(moveX, 2);

	printf("请输入你的Y坐标:");
	int answerY = (int)getNum(moveY, 2);

	answerX--;
	answerY--;

	if ((answerX < 0) || (answerX > 2)) {
		return -1;
	}

	if ((answerY < 0) || (answerY > 2)) {
		return -1;
	}

	board[answerY][answerX] = playerMove;
	return 0;
}

int main()
{
	int turns = 0;
	const int MAX_TURNS = 9;
	char playerOneChar = 'X';
	char playerTwoChar = 'O';
	char currentChar = playerOneChar;

	while (turns <= MAX_TURNS) {
		char board[3][3];
		memset(board, ' ', 9);

		char moveX[2];
		memset(moveX, ' ', 2);

		char moveY[2];
		memset(moveY, ' ', 2);

		int result = makeMove(board, moveX, moveY, currentChar);
		
		if (result == 0) {
			if (currentChar == playerOneChar) {
				currentChar = playerTwoChar;
			}

			else {
				currentChar = playerOneChar;
			}

			turns++;
		}

		else {
			printf("玩家的移动超出了边界。");
		}
	}
}

这是input.cpp,它旨在安全地从用户那里获取一个数字,以防止缓冲区溢出或其他问题:

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

long getNum(char* buffer, int bufferSize)
{
	long answer;
	int success = 0;

	while (!success)
	{
		// 这里从用户获取输入
		if (!fgets(buffer, bufferSize, stdin))
		{
			// 如果fgets失败,返回退出码1。
			return 1;
		}

		char* endptr;

		errno = 0;
		answer = strtol(buffer, &endptr, 10);

		// 如果出现错误,用户将被告知数字太小或太大。
		if (errno == ERANGE)
		{
			printf("抱歉,这个数字太小或太大。\n");
			success = 0;
		}

		else if (endptr == buffer)
		{
			success = 0;
		}

		else if (*endptr && *endptr != '\n')
		{
			success = 0;
		}

		else
		{
			success = 1;
		}
	}

	return answer;
}

我尝试输入2个数字,但程序之后挂起了。我尝试在互联网上查找信息,但没有解释为什么一个函数无法正确写入2D字符数组的解释。我猜测它挂起的原因是因为char**是指向char*的指针,而不是指向char的2D数组的指针。

英文:

The program is a Tic-Tac-Toe game, and it hangs after taking the X and Y values from the user.

This is the main file:

#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &quot;input.cpp&quot;

int makeMove(char** board, char* moveX, char* moveY, char playerMove) {
	printf(&quot;Make your x move: &quot;);
	int answerX = (int)getNum(moveX, 2);

	printf(&quot;Make your y move: &quot;);
	int answerY = (int)getNum(moveY, 2);

	answerX--;
	answerY--;

	if ((answerX &lt; 0) || (answerX &gt; 2)) {
		return -1;
	}

	if ((answerY &lt; 0) || (answerY &gt; 2)) {
		return -1;
	}

	board[answerY][answerX] = playerMove;
	return 0;
}

int main()
{
	int turns = 0;
	const int MAX_TURNS = 9;
	char playerOneChar = &#39;X&#39;;
	char playerTwoChar = &#39;O&#39;;
	char currentChar = playerOneChar;

	while (turns &lt;= MAX_TURNS) {
		char board[3][3];
		memset(board, &#39; &#39;, 9);

		char moveX[2];
		memset(moveX, &#39; &#39;, 2);

		char moveY[2];
		memset(moveY, &#39; &#39;, 2);

		int result = makeMove(board, moveX, moveY, currentChar);
		
		if (result == 0) {
			if (currentChar == playerOneChar) {
				currentChar = playerTwoChar;
			}

			else {
				currentChar = playerOneChar;
			}

			turns++;
		}

		else {
			printf(&quot;Player move was out of bounds.&quot;);
		}
	}
}

This is input.cpp, and it is designed to safely get a number from the user, without allowing
for buffer overruns or other problems:

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;errno.h&gt;

long getNum(char* buffer, int bufferSize)
{
	long answer;
	int success = 0;

	while (!success)
	{
		// This gets the input from the user
		if (!fgets(buffer, bufferSize, stdin))
		{
			//If fgets fails, return with exit code 1.
			return 1;
		}

		char* endptr;

		errno = 0;
		answer = strtol(buffer, &amp;endptr, 10);

		// If an error occurs, the user is told the number is too small or large.
		if (errno == ERANGE)
		{
			printf(&quot;Sorry, this number is too small or too large.\n&quot;);
			success = 0;
		}

		else if (endptr == buffer)
		{
			success = 0;
		}

		else if (*endptr &amp;&amp; *endptr != &#39;\n&#39;)
		{
			success = 0;
		}

		else
		{
			success = 1;
		}
	}

	return answer;
}

I attempted to input 2 numbers, but the program hanged afterwards. I attempted to research on the internet, but there was a lack of any explanation for why a function was not able to write to a 2D array properly. I am assuming that it hangs because char** is a pointer to char*, rather than being a pointer to a 2D array of chars.

答案1

得分: 1

以下是翻译好的内容:

这个函数声明:

int makeMove(char** board, char* moveX, char* moveY, char playerMove) {

是无效的。

您传递给函数的是:

int result = makeMove(board, moveX, moveY, currentChar);

这个二维数组 board 声明如下:

char board[3][3];

作为参数表达式时,它会被隐式转换为其类型为 char(*)[3] 的第一个元素的指针。但函数参数的类型是 char **char(*)[3]char ** 之间没有隐式转换。因此,这个函数调用已经导致了未定义行为。

您应该像这样声明函数:

int makeMove(char board[][3], char* moveX, char* moveY, char playerMove) {

另一个问题是 fgets 的调用:

if (!fgets(buffer, bufferSize, stdin))

由于 bufferSize 等于 2,因此换行符 '\n' 将不会存储在输入缓冲区中(只有在用户一次性按下 Enter 键时才会存储。在这种情况下,数组将看起来像 { '\n', '\0' })。因此,随后的 fgets 调用将读取一个只包含换行符的空字符串。

请注意,由于文件扩展名是:

#include "input.cpp";

您正在将程序编译为 C++ 程序。C 和 C++ 是两种不同的语言。但无论如何,将一个模块包含为头文件是一个不好的主意。您需要单独编译它。

英文:

This function declaration:

int makeMove(char** board, char* moveX, char* moveY, char playerMove) {

is invalid.

You are passing to the function:

int result = makeMove(board, moveX, moveY, currentChar);

the two-dimensional array board declared like

char board[3][3];

Used as an argument expression it is implicitly converted to pointer to its first element of the type char ( * )[3]. But the function parameter has the type char **. There is no implicit conversion between objects of the pointer types char ( * )[3] and char **. So already this function call invokes undefined behavior.

You should declare the function like:

int makeMove(char board[][3], char* moveX, char* moveY, char playerMove) {

Another problem is that call of fgets:

if (!fgets(buffer, bufferSize, stdin))

As the bufferSize is equal to 2 then the new line character &#39;\n&#39; will not be stored in the input buffer (It will be stored only if the user at once will press the Enter key. In this case the array will look like { &#39;\n&#39;, &#39;\0&#39; }). As a result a subsequent call of fgets will read an empty string that will contain only the new line character.

Pay attention to that it seems due to the extension of the file:

#include &quot;input.cpp&quot;

you are compiling your program as a C++ program. C and C++ are two different languages. But in any case it is a bad idea to include a module as a header. You need to compile it separately.

答案2

得分: 1

makeMove()函数的第一个参数需要是char board[][3],这是一个固定的二维数组,每行长度为3,这与您在main()函数中声明的相符。而char** board则是一种不同的类型,它是一个一维数组,其中每个元素都是指向一维char数组的指针。无论哪种声明方式,都可以使用board[i][j]来访问,但在这两种声明方式下的实现方式是不同的。

英文:

The first argument of makeMove() needs to be char board[][3], which is a fixed 2-D array with rows of length 3, which matches what you declared in main(). char** board is a rather different thing, which is a 1-D array of pointers, each pointing to a 1-D array of chars. Either one can be accessed with board[i][j], but how that is implemented for those two declarations is different.

huangapple
  • 本文由 发表于 2023年5月6日 22:54:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/76189543.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定