Seg Fault When Trying to Return a C99 2D Dynamic Array in C Functions

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

Seg Fault When Trying to Return a C99 2D Dynamic Array in C Functions

问题

I am trying to return a modern 2D dynamic array after allocating memory to it and filling it up with character values. I tried working around some errors but ultimately got stuck at a dead end. I originally had a working 2D dynamic array implementation return in functions using array-of-pointers method from C90 but I am told that has overhead and is out-of-date. So, I decided to try and upgrade using modern C 2D array approach taken from here: https://stackoverflow.com/questions/917783/how-do-i-work-with-dynamic-multi-dimensional-arrays-in-c.

Alas, it ended up not working in my implementation so I am now wondering what I may have done wrong. Please see my below code for full reference: (Goal was to make a 2D matrix of a checkerboard within helper functions to main)

char **allocateToBoardC99(const int nthDim)
{
	char(*boardArray)[nthDim] = malloc(sizeof(char[nthDim][nthDim]));
	return (char **)boardArray;
}
char **getCheckeredBoardC99(const int nthDim)
{
	bool startWithWhiteTile = true;

	// C99 version (more elegant):
	char **arr = allocateToBoardC99(nthDim);

	// Make the board checkered by alternating char assignments based on a bool latch
	for (int rowIndex = 0; rowIndex < nthDim; rowIndex++)
	{
		if (startWithWhiteTile)
		{
			for (int columnIndex = 0; columnIndex < nthDim; columnIndex++)
			{
				arr[rowIndex][columnIndex] = (columnIndex % 2 == 0) ? 'W' : 'B';
			}
			startWithWhiteTile = false;
		}
		else
		{
			for (int columnIndex = 0; columnIndex < nthDim; columnIndex++)
			{
				arr[rowIndex][columnIndex] = (columnIndex % 2 == 0) ? 'B' : 'W';
			}
			startWithWhiteTile = true;
		}
	}
	return (char **)arr;
}
int main(int argc, char *argv[])
{
	// Initialize dimension of the checker board:
	int dim = 8;

	// char **boardMatrix = getCheckeredBoardC90(dim); // Works
	char **boardMatrix = getCheckeredBoardC99(dim);

	printf("hello world");
	printf("\n");

	for (int row = 0; row < dim; row++)
	{
		printf("[");
		for (int column = 0; column < dim; column++)
		{
			printf("%c ", boardMatrix[row][column]);
		}
		printf("]\n");
	}

	return 0;
}

If you're curious about the above getCheckeredBoardC90(dim); working, here is the following sequence of code that made it work (while the newer C99 doesn't):

char **allocateToBoardC90(const int nthDim)
{
	// The calloc() function reserves storage space for an array of NUM elements, each of length SIZE bytes:
	// calloc(size_t NUM, size_t SIZE)
	char *values = calloc(nthDim * nthDim, sizeof(char)); // In our 2D board array, we would have nxn (char)elements (8x8=64 elements)

	// Simple way is to allocate a memory block of size row and access its elements using simple pointer arithmetic:
	char **rows = malloc(nthDim * sizeof(char)); // pointer to overall space

	// Iterate through each *row; 0 to nthRow
	for (int rowIndex = 0; rowIndex < nthDim; rowIndex++)
	{
		rows[rowIndex] = values + (rowIndex * nthDim);
	}
	// Returns an array of pointers (array of arrays)
	return rows;
}
char **getCheckeredBoardC90(const int nthDim)
{
	bool startWithWhiteTile = true;

	//**array[rowIndex][columnIndex]
	// char **arr[nthDim][nthDim];

	// C90 version (lotta pointers; be aware of performance overhead):
	char **arr = allocateToBoardC90(nthDim);

	// Make the board checkered by alternating char assignments based on a bool latch
	for (int rowIndex = 0; rowIndex < nthDim; rowIndex++)
	{
		if (startWithWhiteTile)
		{
			for (int columnIndex = 0; columnIndex < nthDim; columnIndex++)
			{
				arr[rowIndex][columnIndex] = (columnIndex % 2 == 0) ? 'W' : 'B';
			}
			startWithWhiteTile = false;
		}
		else
		{
			for (int columnIndex = 0; columnIndex < nthDim; columnIndex++)
			{
				arr[rowIndex][columnIndex] = (columnIndex % 2 == 0) ? 'B' : 'W';
			}
			startWithWhiteTile = true;
		}
	}
	return arr;
}
int main(int argc, char *argv[])
{
	// Initialize dimension of the checker board:
	int dim = 8;

	char **boardMatrix = getCheckeredBoardC90(dim); // Works
	// char **boardMatrix = getCheckeredBoardC99(dim);

	printf("hello world");
	printf("\n");

	for (int row = 0; row < dim; row++)
	{
		printf("[");
		for (int column = 0; column < dim; column++)
		{
			printf("%c ", boardMatrix[row][column]);
		}
		printf("]\n");
	}

	return 0;
}

If the helper functions return correctly, then main's output should look something like this:

hello world
[W B W B W B W B ]
[B W B W B W B W ]
[W B W B W B W B ]
[B W B W B W B W ]
[W B W B W B W B ]
[B W B W B W B W ]
[W B W B W B W B ]
[B W B W B W B W ]

Specific help on this would be greatly appreciated, thank you!

NOTE: Originally with this dynamic 2D array modern C code, I was trying to return the 2D array from an allocator function (really a reference to it), pass that 2D array's reference to an element-initializer function to fill up the checkerboard like usual, and then finally pass the finalized 2D array (as a reference) to the main function to check the contents of the 2D array through terminal output.

What ended up happening was a lot of trial and error to resolve pointer-to-object issues, pointer casting, etc. This only

英文:

I am trying to return a modern 2D dynamic array after allocating memory to it and filling it up with character values. I tried working around some errors but ultimately got stuck at a dead end. I originally had a working 2D dynamic array implementation return in functions using array-of-pointers method from C90 but I am told that has overhead and is out-of-date. So, I decided to try and upgrade using modern C 2D array approach taken from here: https://stackoverflow.com/questions/917783/how-do-i-work-with-dynamic-multi-dimensional-arrays-in-c.

Alas, it ended up not working in my implementation so I am now wondering what I may have done wrong. Please see my below code for full reference: (Goal was to make a 2D matrix of a checkerboard within helper functions to main)

char **allocateToBoardC99(const int nthDim)
{
	char(*boardArray)[nthDim] = malloc(sizeof(char[nthDim][nthDim]));
	return (char **)boardArray;
}
char **getCheckeredBoardC99(const int nthDim)
{
	bool startWithWhiteTile = true;

	// C99 version (more elegant):
	char **arr = allocateToBoardC99(nthDim);

	// Make the board checkered by alternating char assignments based on a bool latch
	for (int rowIndex = 0; rowIndex < nthDim; rowIndex++)
	{
		if (startWithWhiteTile)
		{
			for (int columnIndex = 0; columnIndex < nthDim; columnIndex++)
			{
				arr[rowIndex][columnIndex] = (columnIndex % 2 == 0) ? 'W' : 'B';
			}
			startWithWhiteTile = false;
		}
		else
		{
			for (int columnIndex = 0; columnIndex < nthDim; columnIndex++)
			{
				arr[rowIndex][columnIndex] = (columnIndex % 2 == 0) ? 'B' : 'W';
			}
			startWithWhiteTile = true;
		}
	}
	return (char **)arr;
}
int main(int argc, char *argv[])
{
	// Initialize dimension of the checker board:
	int dim = 8;

	// char **boardMatrix = getCheckeredBoardC90(dim); // Works
	char **boardMatrix = getCheckeredBoardC99(dim);

	printf("hello world");
	printf("\n");

	for (int row = 0; row < dim; row++)
	{
		printf("[");
		for (int column = 0; column < dim; column++)
		{
			printf("%c ", boardMatrix[row][column]);
		}
		printf("]\n");
	}

	return 0;
}

If you're curious about the above getCheckeredBoardC90(dim); working, here is the following sequence of code that made it work (while the newer C99 doesn't):

char **allocateToBoardC90(const int nthDim)
{
	// The calloc() function reserves storage space for an array of NUM elements, each of length SIZE bytes:
	// calloc(size_t NUM, size_t SIZE)
	char *values = calloc(nthDim * nthDim, sizeof(char)); // In our 2D board array, we would have nxn (char)elements (8x8=64 elements)

	// Simple way is to allocate a memory block of size row and access its elements using simple pointer arithmetic:
	char **rows = malloc(nthDim * sizeof(char)); // pointer to overall space

	// Iterate through each *row; 0 to nthRow
	for (int rowIndex = 0; rowIndex < nthDim; rowIndex++)
	{
		rows[rowIndex] = values + (rowIndex * nthDim);
	}
	// Returns an array of pointers (array of arrays)
	return rows;
}
char **getCheckeredBoardC90(const int nthDim)
{
	bool startWithWhiteTile = true;

	//**array[rowIndex][columnIndex]
	// char **arr[nthDim][nthDim];

	// C90 version (lotta pointers; be aware of performance overhead):
	char **arr = allocateToBoardC90(nthDim);

	// Make the board checkered by alternating char assignments based on a bool latch
	for (int rowIndex = 0; rowIndex < nthDim; rowIndex++)
	{
		if (startWithWhiteTile)
		{
			for (int columnIndex = 0; columnIndex < nthDim; columnIndex++)
			{
				arr[rowIndex][columnIndex] = (columnIndex % 2 == 0) ? 'W' : 'B';
			}
			startWithWhiteTile = false;
		}
		else
		{
			for (int columnIndex = 0; columnIndex < nthDim; columnIndex++)
			{
				arr[rowIndex][columnIndex] = (columnIndex % 2 == 0) ? 'B' : 'W';
			}
			startWithWhiteTile = true;
		}
	}
	return arr;
}
int main(int argc, char *argv[])
{
	// Initialize dimension of the checker board:
	int dim = 8;

	char **boardMatrix = getCheckeredBoardC90(dim); // Works
	// char **boardMatrix = getCheckeredBoardC99(dim);

	printf("hello world");
	printf("\n");

	for (int row = 0; row < dim; row++)
	{
		printf("[");
		for (int column = 0; column < dim; column++)
		{
			printf("%c ", boardMatrix[row][column]);
		}
		printf("]\n");
	}

	return 0;
}

If the helper functions return correctly, then main's output should look something like this:

hello world
[W B W B W B W B ]
[B W B W B W B W ]
[W B W B W B W B ]
[B W B W B W B W ]
[W B W B W B W B ]
[B W B W B W B W ]
[W B W B W B W B ]
[B W B W B W B W ]

Specific help on this would be greatly appreciated, thank you!

NOTE: Originally with this dynamic 2D array modern C code, I was trying to return the 2D array from an allocator function (really a reference to it), pass that 2D array's reference to an element-initializer function to fill up the checkerboard like usual, and then finally pass the finalized 2D array (as a reference) to the main function to check the contents of the 2D array through terminal output.

What ended up happening was a lot of trial and error to resolve pointer-to-object issues, pointer casting, etc. This only led (from what I believe is improper C-pointer casting) to an end-game "Segmentation Fault" in the getCheckeredBoardC99 function.

答案1

得分: 3

A char** 只能用于指向char*数组中的第一项。除非用于2D数组样式的指针查找表,否则char**与2D数组毫不相关。

考虑通过参数返回数组:

void allocateToBoardC99 (size_t n, char(**boardArray)[n] )
{
  *boardArray = malloc(sizeof(char[n][n]));
}

int main (void)
{
  size_t n=5;
  char (*arr)[n];
  allocateToBoardC99(5, &arr);
}
英文:

A char** can only be used to point at the first item in an array of char*. Except that a pointer look-up table can be used for 2D array-like syntax, char** has literally nothing to do with 2D arrays what-so-ever.

Consider returning the array through a parameter instead:

void allocateToBoardC99 (size_t n, char(**boardArray)[n] )
{
  *boardArray = malloc(sizeof(char[n][n]));
}

int main (void)
{
  size_t n=5;
  char (*arr)[n];
  allocateToBoardC99(5, &arr);
}

答案2

得分: 1

以下是您要翻译的内容:

  • "The type char** is not a 2D array so you cannot return 2d array this way. Due to some peculiarities of Variably Modified Types (i.e. VLA and pointers to VLA) they cannot be declared at file scope. So functions cannot return VMT because their return type is visible at file scope. In a case of 2D array there is a workaround in form of returning a pointer to incompleted array type e.g. int (*)[]. This pointer cannot be dereferenced because it points to incompleted type so one must assign it to a complete and compatible array type before use."

  • "Returning a complex type from a function requires a bit clunky syntax but it can be made a lot prettier with popular typeof extension (a feature in C23)."

英文:

The type char** is not a 2D array so you cannot return 2d array this way. Due to some peculiarities of Variably Modified Types (i.e. VLA and pointers to VLA) they cannot be declared at file scope. So functions cannot return VMT because their return type is visible at file scope. In a case of 2D array there is a workaround in form of returning a pointer to incompleted array type e.g. int (*)[]. This pointer cannot be dereferenced because it points to incompleted type so one must assign it to a complete and compatible array type before use.


// function returning a pointer to an array

char (*allocateToBoardC99(const int nthDim))[]
{
    char(*boardArray)[nthDim] = malloc(sizeof(char[nthDim][nthDim]));
    return boardArray;
}

... usage ...

char (*arr)[n] = allocateToBoardC99(n);

Returning a complex type from a function requires a bit clunky syntax but it can be made a lot prettier with popular typeof extension (a feature in C23).


typeof(char[]) * allocateToBoardC99(const int nthDim);

答案3

得分: 0

以下是您提供的代码的翻译部分:

从@Lundin的答案中借鉴并切换到数组参数方法(以及其优点),我得出了以下现代C中获取所需输出的代码解决方案:

1.) 首先,将我的分配辅助函数更改为传递参数结构:

void allocateToBoardC99(size_t nthDim, char (**boardArray)[nthDim])
{
	*boardArray = malloc(sizeof(char[nthDim][nthDim]));
}

2.) 接下来,在填充棋盘之前,在主代码中初始化分配(在这里声明数组,然后继续传递参数):

int main(int argc, char *argv[])
{
	// 初始化棋盘的尺寸:
	size_t dim = 8;

	// char **boardMatrix = getCheckeredBoardC90(dim); // 工作正常
	char(*boardMatrix)[dim];
	allocateToBoardC99(dim, &boardMatrix);

    // 注意:主代码的其余部分暂时隐藏...
}

3.) 现在,我们准备开始填充棋盘。像以前一样将棋盘矩阵作为参数传递,以引用子脚本赋值的目标。还将返回类型更改为void,因为它只执行元素赋值操作:

void getCheckeredBoardC99(size_t nthDim, char (*boardArray)[nthDim])
{
	bool startWithWhiteTile = true;

	// C99版本(更加优雅):

	// 通过交替的char赋值使棋盘成为棋盘,基于一个布尔锁
	for (int rowIndex = 0; rowIndex < nthDim; rowIndex++)
	{
		if (startWithWhiteTile)
		{
			for (int columnIndex = 0; columnIndex < nthDim; columnIndex++)
			{
				boardArray[rowIndex][columnIndex] = (columnIndex % 2 == 0) ? 'W' : 'B';
			}
			startWithWhiteTile = false;
		}
		else
		{
			for (int columnIndex = 0; columnIndex < nthDim; columnIndex++)
			{
				boardArray[rowIndex][columnIndex] = (columnIndex % 2 == 0) ? 'B' : 'W';
			}
			startWithWhiteTile = true;
		}
	}
}

请注意,在第3步中,我们只传递了已声明的不完整数组类型指针(感谢@tstanisl提供了有关这种类型的解释),而不是在allocateToBoardC99中的双指针版本。这是因为getCheckeredBoardC99并不试图引用一个块(像malloc一样),而是引用棋盘中的单个元素。我正在小心地表述这一点,因为我已经认识到并且想要回应@Lundin和@tstanisl的看法,即双指针类型如char **并不等同于/表示2D数组。

4.) 在主代码中在分配函数之后调用元素赋值函数,以完成这个小型过程,还打印现代2D棋盘数组的元素以验证其是否与所需输出相匹配:

int main(int argc, char *argv[])
{
	// 初始化棋盘的尺寸:
	size_t dim = 8;

	// char **boardMatrix = getCheckeredBoardC90(dim); // 工作正常
	char(*boardMatrix)[dim];
	allocateToBoardC99(dim, &boardMatrix);

    // --查看下面完整的代码,以完成棋盘处理--

	getCheckeredBoardC99(dim, boardMatrix);

	printf("hello world");
	printf("\n");

	for (int row = 0; row < dim; row++)
	{
		printf("[");
		for (int column = 0; column < dim; column++)
		{
			printf("%c ", boardMatrix[row][column]);
		}
		printf("]\n");
	}

	return 0;
}

在调试后,我得到了以下正确的输出:

(棋盘的2D表示,交替的方块;B = 黑色,W = 白色)

PS Drive:\path> '...'
hello world
[W B W B W B W B ]
[B W B W B W B W ]
[W B W B W B W B ]
[B W B W B W B W ]
[W B W B W B W B ]
[B W B W B W B W ]
[W B W B W B W B ]
[B W B W B W B W ]
英文:

Taking from @Lundin's answers and switching approaches to the array parameter approach (and its pros), I reached the following code solution to get the desired output in modern C:

1.) First, changing my allocation helper function to the pass by parameter structure:

void allocateToBoardC99(size_t nthDim, char (**boardArray)[nthDim])
{
	*boardArray = malloc(sizeof(char[nthDim][nthDim]));
}

2.) Next, initiating the allocating in the main code before filling up the board (declare the array here and then proceed to pass the buck):

int main(int argc, char *argv[])
{
	// Initialize dimension of the checker board:
	size_t dim = 8;

	// char **boardMatrix = getCheckeredBoardC90(dim); // Works
	char(*boardMatrix)[dim];
	allocateToBoardC99(dim, &amp;boardMatrix);

    // NOTE: rest of the main code hidden for now...
}

3.) Now we are ready to start filling up the checkered board with values. Pass the board matrix as a parameter just like before to reference destination for the subscript assignments. Also, change the return type to void since it's just performing element assignment operations:

void getCheckeredBoardC99(size_t nthDim, char (*boardArray)[nthDim])
{
	bool startWithWhiteTile = true;

	// C99 version (more elegant):

	// Make the board checkered by alternating char assignments based on a bool latch
	for (int rowIndex = 0; rowIndex &lt; nthDim; rowIndex++)
	{
		if (startWithWhiteTile)
		{
			for (int columnIndex = 0; columnIndex &lt; nthDim; columnIndex++)
			{
				boardArray[rowIndex][columnIndex] = (columnIndex % 2 == 0) ? &#39;W&#39; : &#39;B&#39;;
			}
			startWithWhiteTile = false;
		}
		else
		{
			for (int columnIndex = 0; columnIndex &lt; nthDim; columnIndex++)
			{
				boardArray[rowIndex][columnIndex] = (columnIndex % 2 == 0) ? &#39;B&#39; : &#39;W&#39;;
			}
			startWithWhiteTile = true;
		}
	}
}

Note that in step 3, we only pass the declared incomplete array type pointer(credit to @tstanisl for this type's explanation) and not the double pointer version in allocateToBoardC99. This is because getCheckeredBoardC99 isn't trying to refer to a block (like malloc), but rather to single elements in the board. I am trying to word this carefully as I have recognized and would like to echo @Lundin and @tstanisl that double pointer types like char** does NOT equal/mean 2D array.

4.) Call the element assignment function after the allocation function in the main code to complete this mini-process, as well as also printing the modern 2D board array's elements to verify it matches the desired output:

int main(int argc, char *argv[])
{
	// Initialize dimension of the checker board:
	size_t dim = 8;

	// char **boardMatrix = getCheckeredBoardC90(dim); // Works
	char(*boardMatrix)[dim];
	allocateToBoardC99(dim, &amp;boardMatrix);

    // --SEE REST OF CODE BELOW THAT COMPLETES THE BOARD PROCESS--

	getCheckeredBoardC99(dim, boardMatrix);

	printf(&quot;hello world&quot;);
	printf(&quot;\n&quot;);

	for (int row = 0; row &lt; dim; row++)
	{
		printf(&quot;[&quot;);
		for (int column = 0; column &lt; dim; column++)
		{
			printf(&quot;%c &quot;, boardMatrix[row][column]);
		}
		printf(&quot;]\n&quot;);
	}

	return 0;
}

After debugging, I got the following correct output:

(a 2D representation of the board with alternating tiles; B = Black, W = White)

PS Drive:\path&gt; &#39;...&#39;
hello world
[W B W B W B W B ]
[B W B W B W B W ]
[W B W B W B W B ]
[B W B W B W B W ]
[W B W B W B W B ]
[B W B W B W B W ]
[W B W B W B W B ]
[B W B W B W B W ]

If there's anything I missed or didn't explain correctly, feel free to comment (that is, if this question thread isn't locked down; I am new to posting on Stack Overflow so bear with me).

Thank you to everyone who helped me reach a solution to my initial problem as well as also pointing out my C programming misconceptions so I don't get myself into a similar pitfall again.

huangapple
  • 本文由 发表于 2023年5月17日 14:13:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/76269030.html
匿名

发表评论

匿名网友

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

确定