2차원 구조체 배열을 포인터로 함수내로 불러와 구조체 내부 변수 조작을 하고 싶습니다.

dlstnry의 이미지

안녕하세요. 이제 프로그래밍을 조금씩 접하고있는 뉴비입니다.
체스를 만들어 보다가 막히는 부분이 있어 적습니다.

2차원 구조체 배열을 포인터로 함수내로 불러와 구조체 내부 변수 조작을 하고 싶습니다.
함수 내로 불러들일 때 어떠한 형식으로 인자를 받아야하고 동적할당은 어떤식으로 해야하는지 여쭙고 싶습니다.
'struct something ***A' 이런식으로 호출하긴 했는데 구조체 내부변수 참조 하는데 엑세스 오류가 뜹니다.
3차원 배열 포인터 형식으로도 불러와보고
기존 배열을 2차원 구조체 포인터 배열로 바꿔서 불러보기도 했는데 전혀 모르겠습니다.
불러오는 형식이 잘못된건지 동적할당이 잘못된건지;

일단 해당부분을 추린 코드는 이렇습니다.

#define HIGHT 8
#define WIDTH 18
 
typedef struct
{
	char tyle;
	char chessmen;
	int player;	//0=NP, 1=P1, 2=P2, 3=Highlight
}tileInfo;
 
tileInfo board[HIGHT][WIDTH];
 
void chessmenMove(int x, int y, <이 부분과>)
{
        <이 부분의 동적할당>
 
	switch (boardptr[y][x]->chessmen)  <<여기서 엑세스 오류가 납니다.
}
익명 사용자의 이미지

배열=포인터가 아니고 배열이 특정 구문에서 포인터로 바뀌는 겁니다.
그리고 그 바뀌는 구문에서도 배열의 배열의 배열은
배열의 배열의 포인터로 바뀌지 포인터의 포인터의 포인터로 바뀌진 않습니다.
따라서 2차원 구조체 배열에 대한 포인터를 'struct something ***A'로 적으면 당연히 잘 돌아가지 않습니다.

외부에서 넘겨받은 다차원 배열을 함수 내부에서 조작하고 싶다면

tileInfo (*boardptr)[HEIGHT][WIDTH] = malloc(sizeof(tileInfo [HEIGHT][WIDTH]))
 
...
 
chessmenMove(1, 2, boardptr);
 
...
 
 
void chessmenMove(int x, int y, tileInfo boardptr[HEIGHT][WIDTH]) /* boardptr[][WIDTH] 나 (*boardptr)[WIDTH]로 적어도 상관은 없음 */
{
        /* 새로 판을 만들게 아니라면 함수 내부에서 동적할당은 필요가 없음 */
 
	switch (boardptr[y][x]->chessmen)
}

근데 위와 같은 상황에서는 사실
그냥 일차원 배열을 만들어서 (*((ptr) + WIDTH * (y) + (x))) 와 같은 식으로
접근하는 경우가 더 많습니다.
(당연히 매번 이렇게 길게 써주진 않고 매크로 함수나 인라인 함수를 따로 만들어서 씁니다)

dlstnry의 이미지

감사합니다.
함수로 불러오는 인자의 형태가 잘못 됐다는 걸 글을 쓰고 깨닫긴 했지만 여전히 막혔었는데 그런 방법이 있었군요!
콜 바이 리퍼런스 하면 포인터로 받는다 라고만 머리속에 밖혀있어 생각하지 못했네요;
근데 한가지 궁금한 점이 있습니다. 위의 형태라면 구조체 포인터 배열을 선언하건 구조체 배열 포인터를 선언하건 상관이 없는건가요?
포인터라는게 참 사람 골아프게 하네요ㅎㅎ

익명 사용자의 이미지

void chessmenMove(int x, int y, tileInfo boardptr[HEIGHT][WIDTH]) /* boardptr[][WIDTH] 나 (*boardptr)[WIDTH]로 적어도 상관은 없음 */

이것도 콜 바이 레퍼런스고, 포인터로 받는 것 입니다.
함수 매개변수에도 역시 배열이 포인터로 변환되는 규칙이 적용됩니다.
따라서 boardptr[HEIGHT][WIDTH] 로 적었어도 boardptr은 함수 내부에서 배열이 아닌 포인터고
배열과 달리 그 자체에 대한 대입 연산도 가능합니다. (배열이 복사가 된다는 의미는 아님)
boardptr[][WIDTH] 나 (*boardptr)[WIDTH]로 적어도 상관이 없다는건
세가지 모두 다 컴파일러에서 동일한 의미로 받아들인다는 뜻입니다.

> 위의 형태라면 구조체 포인터 배열을 선언하건 구조체 배열 포인터를 선언하건 상관이 없는건가요?

배열과 포인터, 선언에 관련된 규칙들은 매우 방대합니다.
따라서 실제 구체적인 코드가 아니면 뭐가 맞다고 말씀드리기가 어렵겠네요.

질문자가 생각하는 '위의 형태'가 뭘 의미하는 지는 모르겠는데,
제대로 필요한 내용을 다 배우고 쓰는게 아니라면
뭔가 잘못될 가능성이 높다고 생각하시는게 좋습니다.

HDNua의 이미지

1. 질문을 하실 때는, 오류 메시지를 반드시 같이 보여주는 습관을 들이는 게 좋습니다.
코드만 보았을 때의 오류 메시지와, 실제로 실행했을 때의 오류 메시지가 다른 경우도 있어요.

2. 코드를 보니 board 2차원 배열은 전역으로 정의하신 것 같은데, 이 경우 board를 함수의 인자로 넘길 필요는 없습니다.
다음과 같이 그냥 함수 내에서 선언된 변수처럼 사용해도 충분하지요.

#include <stdio.h>
#define HEIGHT  8
#define WIDTH   18
 
// Object 형식 선언 및 arr2d 2차원 배열 정의
typedef struct _object {
    int x, y, z;
} Object;
Object arr2d[HEIGHT][WIDTH];
 
// 배열 arr2d의 x, y 좌표에 값 설정
void arr2d_set(int x, int y, Object *value);
 
int main(void) {
    Object value = { 1, 2, 3 };
    arr2d_set(1, 2, &value);
    return 0;
}
 
// 배열 arr2d의 x, y 좌표에 값 설정
void arr2d_set(int x, int y, Object *value) {
    // arr2d 배열을 그냥 사용
    arr2d[y][x] = *value;
}

3. 문제의 상황에선 함수 인자의 형식이 문제인데, 2차원 배열의 경우에 한해서 보통 다음 방법을 씁니다.

#include <stdio.h>
#define HEIGHT  8
#define WIDTH   18
 
// Object 형식 선언
typedef struct _object {
    int x, y, z;
} Object;
 
// Object 객체 값 초기화
void Object_init(Object *target, int x, int y, int z);
 
 
 
// 배열 arr2d로부터 특정 좌표의 주소 값 획득
Object *arr2d_get(Object(*arr2d)[WIDTH], int x, int y);
 
// 배열 arr2d의 x, y 좌표에 값 설정
// (!) arr2d를 인자로 넘기려면 "Object(*arr2d)[WIDTH]"와 같이 한다
void arr2d_set(Object(*arr2d)[WIDTH], int x, int y, Object *value);
 
int main(void) {
    // arr2d 2차원 배열 정의
    Object arr2d[HEIGHT][WIDTH];
 
    // value 변수 정의 및 초기화
    Object value;
    Object_init(&value, 1, 2, 3);
 
    // arr2d가 인자에 추가됨
    arr2d_set(arr2d, 1, 2, &value);
 
    // 값이 정확히 대입되었는지 확인
    Object_init(&value, 4, 5, 6);
    value = *arr2d_get(arr2d, 1, 2);
    printf("%d %d %d\n", value.x, value.y, value.z);
    return 0;
}
 
 
 
// Object 객체 값 초기화
void Object_init(Object *target, int x, int y, int z) {
    target->x = x;
    target->y = y;
    target->z = z;
}
 
// 배열 arr2d로부터 특정 좌표의 주소 값 획득
Object *arr2d_get(Object(*arr2d)[WIDTH], int x, int y) {
    return &(arr2d[y][x]);
}
 
// 배열 arr2d의 x, y 좌표에 값 설정
void arr2d_set(Object(*arr2d)[WIDTH], int x, int y, Object *value) {
    arr2d[y][x] = *value;
}

아주아주 문법적으로는 이렇게 하구요.

#include <stdio.h>
#define HEIGHT  8
#define WIDTH   18
 
 
 
// Object 형식 선언
typedef struct _object {
    int x, y, z;
} Object;
 
// Object 객체 값 초기화
void Object_init(Object *target, int x, int y, int z);
 
 
 
// 배열 arr2d로부터 특정 좌표의 주소 값 획득
Object *arr2d_get(Object(*arr2d)[HEIGHT][WIDTH], int x, int y);
 
// 배열 arr2d의 x, y 좌표에 값 설정
// (!) 아주아주 문법적으로 배열을 인자로 넘긴 경우
void arr2d_set(Object(*arr2d)[HEIGHT][WIDTH], int x, int y, Object *value);
 
int main(void) {
    // arr2d 2차원 배열 정의
    Object arr2d[HEIGHT][WIDTH];
 
    // value 변수 정의 및 초기화
    Object value;
    Object_init(&value, 1, 2, 3);
 
    // (!) 인자가 "arr2d"가 아니라 "&arr2d"임에 주목
    arr2d_set(&arr2d, 1, 2, &value);
 
    // 값이 정확히 대입되었는지 확인
    Object_init(&value, 4, 5, 6);
    value = *arr2d_get(&arr2d, 1, 2);
    printf("%d %d %d\n", value.x, value.y, value.z);
    return 0;
}
 
 
 
// Object 객체 값 초기화
void Object_init(Object *target, int x, int y, int z) {
    target->x = x;
    target->y = y;
    target->z = z;
}
 
// 배열 arr2d로부터 특정 좌표의 주소 값 획득
Object *arr2d_get(Object(*arr2d)[HEIGHT][WIDTH], int x, int y) {
    return (&(*arr2d)[y][x]);
}
// 배열 arr2d의 x, y 좌표에 값 설정
void arr2d_set(Object(*arr2d)[HEIGHT][WIDTH], int x, int y, Object *value) {
    // (!) arr2d가 아니라 (*arr2d)임에 주목
    (*arr2d)[y][x] = *value;
}

이 코드가 아주아주 문법적이라고 말씀드린 것은, 3-1의 코드가 문법적으로 올바르지 않다는 것은 아닙니다.
이런 건 "복잡한 선언"이라고 해서 설명하려면 아주 복잡해지는데, 일단 입문이시니 3-1의 코드 방식대로 사용하시면 됩니다.

저는 이렇게 생각했습니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.