c++에서 이차원배열 반환

loveistt의 이미지

int [10][10] board;

이런식으로 생긴 board라는 2차원 배열이 있습니다.

그런데 어떤 함수가 이 board를 반환하려고 합니다

그래서 이런 식으로 선언했습니다

int** fun() { return board; }

이런 방식을 취했더니 int(*)[10] 을 int ** 으로 변환할 수 없다며 에러가 나네요. new를 통한 동적할당밖에 방법이 없는건지 궁금합니다.

익명 사용자의 이미지

int(*)[10] 을 리턴하도록 하면 될 것 같은데요...

int(*)[10] fun() ;

확인은 안해보았습니다...

loveistt의 이미지

그렇게 하면 파싱 에러가 납니다 ^^;

:)

lkjt의 이미지

반환시 int**로 캐스팅 하면 되는데요?

결과는 확인 안해보았습니다.

loveistt의 이미지

return static_cast<int**>(board);

이렇게 했더니

invalid cast라는 군요 int[10][10] 에서 int**로 갈 수 없다 ㅠㅠ

:)

romantic74의 이미지

c++ 로 컴파일을 한것 같습니다.
c 로 컴파일을 하면 warning 이 나며 컴파일은 됩니다.
타입 불일치를 인정하는 것이죠..
c++ 에서는 int [10][10] 와 int** 는 타입이 다르니 conversion이 안되는 것입니다. c++ 에서는 다른 타입을 명시적으로 변환하기 위해서 reinterpret_cast 연산자를 제공합니다.
reinterpret_cast<int**>(board) 이렇게 return 해 보세요.

doldori의 이미지

어차피 함수 내부의 자동변수라면 반환해도 쓸모가 없죠. 1차원 배열로 생각해 보죠.

int* f()
{
    int a[10];
    // do something
    return a;
}

문법상으로야 맞지만 이 포인터를 넘겨받는 순간 이미 무효화된 포인터입니다.
그럼 이런 방법은 어떨까요?
int* f(int* ar)
{
    // do something
    return ar;
}

이때는 반환값이 무효는 아니지만 이미 알고 있는 정보라서 중복입니다.
다음 같은 경우는 어떻습니까?
int* f()
{
    static int a[10];
    return a;
}

int* g()
{
    return new int[10];
}

이때는 반환값이 유용한 정보가 될 수 있습니다.
배열에 대한 포인터를 반환할 때도 동일한 얘기가 적용됩니다.
int(*f())[10]
{
	int a[10][10];
	return a;
}

int(*g(int (*a)[10]))[10]
{
	return a;
}

int(*h())[10]
{
	static int a[10][10];
	return a;
}

int(*i())[10]
{
	return new int[10][10];
}

int main()
{
	int (*a)[10];
	a = f();  // invalid pointer
	a = g(a); // redundant
	a = h();  // useful? maybe
	a = i();  // ditto
	delete[] a;
}

말이 길었는데 제 생각은 2차원 배열을 주고받을 때는 동적할당으로 하는 것이
그나마 일반적이고, 더 좋은 방법은 vector 같은 컨테이너를 쓰는 것입니다.
doldori의 이미지

romantic74 wrote:
c++ 로 컴파일을 한것 같습니다.
c 로 컴파일을 하면 warning 이 나며 컴파일은 됩니다.
타입 불일치를 인정하는 것이죠..
c++ 에서는 int [10][10] 와 int** 는 타입이 다르니 conversion이 안되는 것입니다. c++ 에서는 다른 타입을 명시적으로 변환하기 위해서 reinterpret_cast 연산자를 제공합니다.
reinterpret_cast<int**>(board) 이렇게 return 해 보세요.

reinterpret_cast는 이럴 때 쓰기 위한 것이 아닙니다. 지금 같은 경우의
캐스팅은 컴파일러에게 거짓말을 하는 것이며(타입을 속였으니까) 컴파일이
된다 하더라도 나중에 보복을 당합니다.
loveistt의 이미지

그냥 동적할당하여 포인터를 넘겼습니다 ^^;

답변 감사합니다.

:)

romantic74의 이미지

reinterpret_cast 가 이럴때 쓰기 위한 것이 아니라고 했는데 그럼 어떨때 쓰는건가요 ?
저는 이 질문에 대한 답을 한 것이며 이러한 방법이 있다고 적은 것입니다.
doldori님이 쓰신 보복이라는 말은 무얼 의미하는 것인가요 ?
정확하게 좋고 나쁨을 설명해 주시기 바랍니다. (상황에 따라 이 cast 방법을 명시적으로 해야 할 때도 있는 것입니다. 묵시적과 명시적은 엄청난 차이입니다.)
지금 여러가지 너무 많이 생각을 하신것 같은데요.. 여러가지 상황을 추측하는 것은 잘못입니다. 정확히 문제의 의도에 맞게 답해 주시기 바랍니다.
그리고 doldori님이 장황하게 답해주신 것은 참 좋은 정보입니다.
그런데 제가 판단하기에는 아마 게임에 적용하기 위해서 board라는 2차원 배열을 전역변수로 만드는 상황이라
doldori님이 말하시는 무효화 같은 것은 안 일어날 것이라 판단됩니다.
( 이런 추측은 금물이지만.. 상황에 따라 달라질 수 있으니까요 ..) 동적 할당하는 것이 일반적이라고 했는데 이것 또한 상황에 따라 달라집니다.
오히려 제가 생각하기에는 그 정도의 배열이라면 전역변수가 좋습니다.
동적할당을 하면 메모리를 철저히 관리를 해 주어야 하니까요.
그리고 vector나 list처럼 stl의 container를 사용하면 자동으로 메모리를 관리해 주니까 잘 사용하면
안정성및 편의성을 확보할 수 있습니다. 하지만 2차원 배열을 사용하기에는 vector가 더 까다로울 수가 있는 경우도 있습니다.
잘 판단해야 하는 것입니다.

doldori의 이미지

romantic74 wrote:
reinterpret_cast 가 이럴때 쓰기 위한 것이 아니라고 했는데 그럼 어떨때 쓰는건가요 ?
저는 이 질문에 대한 답을 한 것이며 이러한 방법이 있다고 적은 것입니다.
doldori님이 쓰신 보복이라는 말은 무얼 의미하는 것인가요 ?
정확하게 좋고 나쁨을 설명해 주시기 바랍니다. (상황에 따라 이 cast 방법을 명시적으로 해야 할 때도 있는 것입니다. 묵시적과 명시적은 엄청난 차이입니다.)

저는 reinterpret_cast가 필요했던 적이 한 번도 없어서 어떤 때 쓰는 것인지는몰라도,
적어도 잘못된 코드를 컴파일"만이라도" 성공하기 위해 쓰는 것이 아니라는
점만은 자신있게 말씀드릴 수 있습니다. 2차원 배열을 넘기는 코드를 작성한
경험이 있으시다면 reinterpret_cast를 쓰라는 답변은 안하셨을 것으로
생각합니다. 이것은 좋고 나쁨의 문제가 아니라 옳고 그름의 문제입니다.
void f(int** a, int m, int n)
{
	for (int i = 0; i < m; ++i)
		for (int j = 0; j < n; ++j)
			a[i][j] = 0;
}

int main()
{
	int a[3][4];
	f(reinterpret_cast<int**>(a), 3, 4);
}

위 코드를 실행하면 어떤 결과를 보일 거라고 생각하십니까?
컴파일러는 이 코드를 컴파일하면서 "일단 네가 하라는 대로 해주겠다. 나중에
후회하지나 말라"고 생각하지 않을까요?

romantic74 wrote:

지금 여러가지 너무 많이 생각을 하신것 같은데요.. 여러가지 상황을 추측하는 것은 잘못입니다. 정확히 문제의 의도에 맞게 답해 주시기 바랍니다.

글쎄요... 제가 이해하기로는 문제의 의도는 2차원 배열을 넘기는 것이며
여러 상황이 있다는 것을 말하고자 했을 뿐입니다. 제가 잘못 이해한 건가요?

romantic74 wrote:

그리고 doldori님이 장황하게 답해주신 것은 참 좋은 정보입니다.
그런데 제가 판단하기에는 아마 게임에 적용하기 위해서 board라는 2차원 배열을 전역변수로 만드는 상황이라
doldori님이 말하시는 무효화 같은 것은 안 일어날 것이라 판단됩니다.
( 이런 추측은 금물이지만.. 상황에 따라 달라질 수 있으니까요 ..) 동적 할당하는 것이 일반적이라고 했는데 이것 또한 상황에 따라 달라집니다.
오히려 제가 생각하기에는 그 정도의 배열이라면 전역변수가 좋습니다.
동적할당을 하면 메모리를 철저히 관리를 해 주어야 하니까요.

오히려 제가 묻고 싶습니다. 이 코드는 게임에 적용하는 것이며 2차원 배열이
전역 변수라는 것은 어떻게 아셨습니까? 저는 아무리 읽어봐도 모르겠습니다.

romantic74 wrote:

그리고 vector나 list처럼 stl의 container를 사용하면 자동으로 메모리를 관리해 주니까 잘 사용하면
안정성및 편의성을 확보할 수 있습니다. 하지만 2차원 배열을 사용하기에는 vector가 더 까다로울 수가 있는 경우도 있습니다.
잘 판단해야 하는 것입니다.

네, 맞습니다. 잘 판단해야 합니다. :)
Seven..의 이미지

잘 몰라서 지나가다 여쭤봅니다..

void f(int** a, int m, int n) 
{ 
   for (int i = 0; i < m; ++i) 
      for (int j = 0; j < n; ++j) 
         a[i][j] = 0; 
} 

int main() 
{ 
   int a[3][4]; 
   f(reinterpret_cast<int**>(a), 3, 4); 
} 

이런식으로 몇번 해봐서.. 포인터를 정수 취급이라던가..

위의 함수 호출에 오류라던가, 아니면 기타 위험한 사항이 있는 것인지요?

VENI VIDI VICI

romantic74의 이미지

board가 전역변수 이고 게임에 쓸 것이다 라고 한것은 저의 추측입니다. 오해하지 마시고요..
그리고 지금 doldori 님의 글에 대해서 토를 달자고 쓰는 것이 아닙니다. 기분 나쁘셨다면 죄송합니다.
저의 프로그래밍 경험으로 봐서 reinterpret_cast가 많이 쓰이지는 않지만
reinterpret_cast 를 써야 하는 상황이 있다는 것입니다. Windows 프로그래밍의 경우 내부적으로는 같은 Type인데도 불구하고
typedef 를 써서 타입을 재정의 해서 사용한 경우는 reinterpret_cast 를 써야 하는 경우가 종종 있습니다.
그리고 stl 소스를 보면 reinterpret_cast를 여러군데 쓰고 있구요.
확인해 보시기 바랍니다. doldori님이 제시한 예제는 잘 쓰이지 않는 상황이지만 가능한 경우입니다.
여러 사람이나 여러 팀과 작업을 하다보면 이런 상황도 맞이할 수 있습니다. 그래서 C++ 제작자가 명시적으로 변환 할수 있도록 장치를 마련해 놓은
것입니다. (명시적이라는 말은 컴파일러가 implicit하게 변환하는 것이 아닌 제작자가 의도적으로 바꾸는 것을 말함 )
그렇게 남용하다 보면 님이 말하는 보복(?)을 당할수도 있겠지요.. 님도 읽기 좋은, 유지보수 가능한 코드를
작성하는 분인것 같습니다. 저도 그렇구요.. ^^ reinterpret_cast 를 써야 하는 경우가 잘못된 코드라는 것은 아니라는 것입니다.
가급적 reinterpret_cast를 쓰지 않도록 설계하는 것이 좋겠지요. 다시 이 질문으로 와서
reinterpret_cast 를 써서 해결할 수 있는 방법이 있고 reinterpret_cast 를 써서 해결할지 아니면 동적 할당을 해서
해야 할지는 질문을 한 사람이 판단할 일입니다. reinterpret_cast 를 써야 할지 동적 할당을 해야 할지는
상황에 따라 또 요구사항의 복잡도에 따라 다양한 전략을 선택할 수 있겠죠.. doldori 님의 글로써 질문자가 충분히 이해하고
적용할 수 있으리라 생각됩니다. 아무튼 처음 글을 올리는데 의견을 주셔서 감사합니다.

정재윤의 이미지

동적할당이냐 아니냐를 떠나서, 이차원 배열을 포인터로 딸랑 넘겨 받았다고 하면, 배열 크기를 모르는 상태에서 컴파일러는 어떻게 코드를 만들어야 할까요?
int a[10][20]이랑 a[5][40] 이랑 구분이 가능할까요?

C FAQ를 한번 쯤 읽어 보시길 권합니다.

loveistt의 이미지

위 정재윤님 말씀에 대해 당연히 구분이 불가능 합니다.

단 제가 구조를 잘못짜는 바람에 저쪽 클래스 내부의 멤버 변수인 board의 내용이 필요합니다.

근데 지금 벡터로 바꾸자니 힘들고 하야... 주어진 board의 크기가 정해져 있기 때문에 급하게 끌어다 썼습니다 ^^;

공부를 좀 더 해야겠네요

:)

doldori의 이미지

Seven.. wrote:
잘 몰라서 지나가다 여쭤봅니다..
void f(int** a, int m, int n) 
{ 
   for (int i = 0; i < m; ++i) 
      for (int j = 0; j < n; ++j) 
         a[i][j] = 0; 
} 

int main() 
{ 
   int a[3][4]; 
   f(reinterpret_cast<int**>(a), 3, 4); 
} 

이런식으로 몇번 해봐서.. 포인터를 정수 취급이라던가..

위의 함수 호출에 오류라던가, 아니면 기타 위험한 사항이 있는 것인지요?


포인터를 정수 취급한다는 말씀을 들으니 생각이 나는군요. Windows API에서
그런 식으로 했던 기억이 있습니다. C를 썼지만 그때의 캐스팅은 C++의 reinterpret_cast와
같은 성격이라고 볼 수 있습니다. 어차피 Windows라는 특정 환경에서만
동작하는 프로그램이고 OS가 그런 캐스팅을 요구한다면 당연히 써야 합니다.
그게 아니라 이식성을 고려해야 할 상황이라면 포인터->정수->포인터의 변환은
절대로 보장되지 않습니다.
그리고 위의 코드는 명백히 잘못된 것입니다. 음... 어떻게 설명을 해야 할까요...
f()는 포인터의 포인터를 받습니다. 포인터의 배열을 넘길 때와 같은 형태라는
것은 알고 계시겠죠. f()에서 a[0]는 int* 형입니다. 따라서 a[0]가 갖고 있는
값은 주소라고 "해석"합니다. main()에서 int a[3][4] = {0};으로 초기화를
했다고 합시다. 만약 sizeof(int) == sizeof(int*)이고, 정수 0과 널 포인터를
표현하는 비트열 패턴이 같다고 가정하면 f()에서 a[0]는 널 포인터가 됩니다.
이것이 다른 타입을 같다고 컴파일러를 속인 결과입니다.
말로 설명하기가 좀 힘든데, 이해가 되셨는지 모르겠네요. 아무튼 불필요한
캐스팅은 매우 위험하다는 점만은 이해하셨으면 좋겠습니다. 사실 잘 설계된
코드는 명시적인 캐스팅이 필요한 경우가 별로 없습니다.

romatic74님, reinterpret_cast를 쓰면 모두 잘못된 코드라고 주장하지는 않습니다.
말씀대로 반드시 필요한 상황이 있으니까요. 다만 이 경우에는 기술적으로 잘못된
답변이었음을 지적하고자 했을 뿐입니다.
아, 그리고 저는 이런 토론이 전혀 기분 나쁘지 않으니 안심하셔도 됩니다. :)

romantic74의 이미지

내부 맴버 변수라면 변수의 포인터를 리턴하지 말고
맴버 함수를 만들어 쓰는걸 검토해 보세요.

romantic74의 이미지

doldori 님의 설명이 맞군요.. 이런 경우는 단지 컴파일만 된다고 reinterpret_cast를 쓰면 문제가 발생하는 경우이군요.. 감사합니다.

Seven..의 이미지

또.. 많이 배워갑니다 ^^

저같은경우는 옛날에.. 무슨 자료 구조를 관리할때

그 포인터 값을 key 값으로 이용했던 적이 있습니다.

그래서 두 키를 비교할때.. reinterpret_cast 를 사용했던 적이 있습니다.

^^ 항상 많이 배워만 가는군요 감사합니다 ^^

VENI VIDI VICI

buelgsk8er의 이미지

아아 반환타입을 배열 포인터로 하려면 저렇게 (첨자부를 선언의 맨 뒤에) 하는 거였군요.. doldori 님의 예제에서 좋은 것 배워갑니다.
사실 실전에서라면 별도의 추상타입을 만들어서 쓰겠지만 몇번인가 어떻게 하는 걸까 궁금했던 때가 있었죠.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.