복잡한 포인터

rgbi3307의 이미지

char (*(*x())[])();

위에서 x을 어떻게 정의하여 할당할 수 있을까요?

klara의 이미지

1. 안쓴다.
2. 실제로 저런게 필요한 경우가 있을수도 있을 것입니다.
함수포인터의 배열이 인자로 들어가는 함수 포인터의 배열에 대한 포인터의 ... 이런식이 될겁니다.
이런 경우는 중간중간 typedef로 타입을 재정의해주는게 가독성도 훨씬 좋아집니다.
3. 질문인가요? 질문은 질문게시판에 해주세요.

P.S. 복잡한 타입선언을 도와주는 프로그램이있는데, 이름을 까먹었네요.

semmal의 이미지

문제를 위한 문제군요.

실제로 저렇게 쓰는 후배가 있으면 때려줄겁니다.
------------------------------
How many legs does a dog have?

------------------------------
How many legs does a dog have?

niuzeta의 이미지

char
(
*(
*x()
)[]
) ();

음....

...And all in war with Time for love of you,
As he takes from you, I engraft you new.

-Sonnet XV

...And all in war with Time for love of you,
As he takes from you, I engraft you new.

-Sonnet XV
전산계획설계사 지망 영문학과생

kaeri17의 이미지

한창 TCPL볼때는 이런거 다 알았는데 지금은 헷갈리는 군요 ㅠㅠ

freestyle의 이미지

이런 거 혼자 생각해 보고 좋아라 했는데, 그 때 모습이 참 한심하게 느껴집니다;;

char (*(*x())[])();
* 복잡한 선언을 읽는 방법
1. 이름부터 찾는다.

2. 그 이름이 무엇인지 찾는다.

3. 이제 연산자 우선순위와 결합방향을 고려해 읽습니다.
3-1. 함수라면 이후에 읽는 것은 함수의 리턴 타입이다
3-2. 배열이라면 이후에 읽는 것은 배열의 element이다.
3-3. 포인터라면 이후에 읽는 것은 포인터가 가리키는 타입이다.

4. 3번을 반복한다.

1. char (*(*x())[])();
x는 함수이고, 이후에 읽는 것은 함수의 리턴 타입이 됩니다. -> 편의상 A,B,C,D로 바꾸겠습니다.

2. char (*(*A)[])();
A의 리턴타입은 포인터네요. 이후에 읽는 것은 포인터가 가리키는 타입입니다.

3. char (*B[])();
[]가 우선하므로 B는 배열이군요(A는 배열 전체에 대한 포인터가 됩니다) 이후에 읽는 것은 배열 B의 element가 됩니다.

4. char (*C)();
B의 element인 C는 각각 포인터네요. C는 함수 포인터네요.

5. char D();
D는 함수인데 리턴타입은 char이군요.

----------------------
Go to the U-City

----------------------------------------------------------------------------------------
Don't Feed the Trolls!
----------------------------------------------------------------------------------------

rgbi3307의 이미지

복잡한 포인터 선언을 읽을때, freestyle님이 정리해 주신 것은 좋은 방법입니다.
그런데, char (*(*x())[])(); 와 같은 선언은 좀더 난해한듯 합니다.
이 선언은 K&R의 The C Programming Language(2nd Edition) 책에 있는 내용인데요.
저도 이 선언을 어떻게 해석할까에 대해서 한참 고민하고, 실습도 했습니다.
실습한 코드는 아래와 같습니다.

char (*(*x1())[])()
{
	static char *str1[] = {"string1", "str2", "str3"};
	return str1[0];
}
 
char *x2()
{
	static char *str2[] = {"string1", "str2", "str3"};
	return str2[0];
}
 
int main ()
{
	printf ("%s\n", x1());
	printf ("%s\n", x2());
	return 0;
}

실습 결과로 말씀드리면,
"char (*(*x())[])(); 와 char *x(); 는 동일하다" 입니다.
간단하게 해석해서 포인터를 반환하는 함수입니다.

char (*(*x())[])();

이렇게 복잡하게 선언한 또 다른 이유가 있을까요?
혹은, 저의 실습코드에서 잘못된 점이 있을까요?

From:
*알지비 (메일: rgbi3307(at)nate.com)
*틈틈히 커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

winner의 이미지

C언어는 확실히 복잡한 선언을 쓰기에 이해가 안 가는 형태를 가지고 있습니다. 이것은 표현식에서 사용되는 방식(declaration mimics use)으로 선언을 하기 때문입니다. 따라서 C 선언을 이해하기 위해서는 유도자료형을 만드는 가장 작은 단위를 알고, 전체를 이해하는 방식으로 하면 이해가 안 갑니다. 거꾸로 이름(Java에서는 식별자, 여기서는 x를 말합니다.)에서부터 연산자를 통해 최종적으로 어떤 기본자료형으로 이끌어지는를 파악해야 합니다.
즉 bottom-up으로 이해하지 말고, top-down으로 이해해나가는 방식을 써야 합니다. 어찌보면 객체지향언어에서 type보다는 interface를 파악할려는 형태로 C 선언을 바라봐야 이해하기 편합니다. 그렇다고 객체지향언어의 polymorphism이라는 기능이 C에서 그대로 쓸 수 있는 것은 아닙니다만... 이건 언어가 사용하는 사람입장에서 제작된게 아니라서 compiler 제작자 입장에서 바라봐야 하는 요소일 뿐입니다.

이제까지 올리신 글을 보면 C 기초를 계속해서 연구하는 것으로 보입니다. K&R2를 읽는 것도 C 기초연구에 매우 중요합니다. C 기초연구를 하시는데 도움을 주는 다른 책을 알려드리겠습니다. C 함정과 실수(Andrew Koenig), C언어 펀더멘탈(전웅), The C++ Programming Language(Bjarne Stroustrup), C: A Reference Manual('C 프로그래밍 언어'라는 이름의 번역서가 있습니다). 그리고 중요한 전자문서로 C 표준과, C FAQs가 있습니다. C FAQs는 여기서 blog를 쓰시는 cinsk님이 번역하신게 있으니 공부하는데 도움이 많이 될 겁니다. 서적은 품절된 것도 있을텐데 그래도 국내번역이 되었던 것들로 그나마 구하기 쉬울 겁니다.
그리고 이런 것을 전문적으로 다루는 mailing list로 comp.lang.c.moderated, comp.lang.c++.moderated 를 구독하시기 바랍니다. 특히 C++ mailing list 쪽에서는 유명한 책 저자들도 간혹 보이곤 합니다.
KLDP의 질문게시판의 품질도 물론 훌륭합니다만 아무래도 전문적으로 다루는 곳보다는 속시원하지 못하겠죠. 제 생각에는 이곳에 하나하나 의문사항을 올리고 해답을 얻는 것도 좋지만 중요서적과 전자문서를 탐독하는 것이 C를 체계적으로 이해하는데 더 많은 도움을 줄 것 같습니다.

그리고 마지막의 실습 source는 gcc로 compile해본 결과 여러 개의 경고문을 내더군요. Compile이 되고, 실행되지만 이식성을 보장하는 정확한 source라고는 할 수 없습니다. 위에 나열한 책들을 읽어보시면 왜 그런지 아실 수 있을 겁니다.

전웅씨가 지금은 망한 han.comp.lang.c에서 자주 이야기했던 것 중 하나가 공부는 시험을 한 후 추측을 하는게 아니라 시험과 이론적 이해를 병행해야 한다고 했습니다. 기존의 잘된 문서를 찾는 것은 매우 힘들 일일지도 모릅니다만(때로는 없기도 하고...) 정확하고 체계적인 학습을 통해 실력이 일취월장할 수 있게 되는 기회를 제공해줍니다.

cinsk의 이미지

이 글은 rgbi3307님의 위 댓글에 대한 reply입니다.

예전에도 제가 한 번 말씀드린 거 같은데, 정확한 출처가 없이, 단언하지 마시기 바랍니다.

프로그래밍 언어를 배울 때, 해보니까 된다고 해서, 그게 맞다고 생각하면 안됩니다.

위 코드를 컴파일해서 (운 좋게도) 동작한다고 해서, 두 함수가 같다는 결론이 나올 수 없다는 말입니다.

> 소(cow)는 동물(animal)이다. 개(dog)도 동물인데? 아.. 소는 개구나.

이게 참이 될 수가 없죠.

만약 주장하는 바가 맞다면, 상관없지만, 주장하는 바가 틀릴 가능성이 있는데, "이건 두 선언이 같군요"라고 글을 쓰면, (만약 답글이 없을 경우) 다른 사람들이 오해할 가능성이 있습니다. 결국 잘못된 정보를 심어주는 격이죠. 앞으로, 잘 모르는 내용에 대해 글을 올릴 때는 "실습해보니... 같은데요?" 라고 올리지 말고, "...을 모르겠네요, 서로 다른 점이 무엇인가요?"라고 올리세요.

아울러 freestyle님 글을 읽어보시고, 아래 글도 읽어보세요.

http://wiki.kldp.org/wiki.php/CLangaugeComplexDeclaration

--
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
Korean Ver: http://www.cinsk.org/cfaqs/

rgbi3307의 이미지

cinsk님의 개인 웹사이트도 자주 봤습니다. 저와 비슷한 연배의 학연이더구요.
(같은 연배, 같은 학연..을 따지고 싶지는 않습니다. 저도 이런거 따지는거 문지 싫어합니다)
근데, 제가 연배와 학연을 서두에 말씀 드리는 이유는,
저에 대해서 너무 대립각을 세우고 계시다는 불쾌감을 많이 느끼고 있어서입니다.

정확한 출처가 없이, 단언하지 마시기 바랍니다.

--> 정확한 출처가 없이 단언하지 않았습니다.

프로그래밍 언어를 배울 때, 해보니까 된다고 해서, 그게 맞다고 생각하면 안됩니다.

--> 이렇게 생각하고 있지 않습니다.

"실습해보니... 같은데요?" 라고 올리지 말고, "...을 모르겠네요, 서로 다른 점이 무엇인가요?"라고 올리세요.

--> "실습해보니... 같은데요?" "서로 다른 점이 무엇인가요?" 라는 의미로는 올렸습니다만, "...을 모르겠네요,"을 이것을 올리진 않았습니다.  
--> 근데, 의문점에 대해서 질문을 하고 서로 의견 교환하자는 것인데 왜 이렇게 감정이 상하는 걸까요?

From:
*알지비 (메일: rgbi3307(at)nate.com)
*틈틈히 커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

kkb110의 이미지

Quote:

"char (*(*x())[])(); 와 char *x(); 는 동일하다" 입니다.
간단하게 해석해서 포인터를 반환하는 함수입니다.

이 부분에서 읽는 사람에 따라 약간 단정적으로 말씀하시는 뉘양스로 들릴 수도 있는 것 같습니다.
그래서 서로 그냥 약간씩 오해를 하신듯 합니다.

winner의 이미지

제가 생각하기에는 알지비님에게 대립각을 세우는 것이 아니라 기술적 정확성에 대해서 cinsk님이 철저한 면이 있기 때문입니다. 그리고 이런 것은 과학자, 기술자들에게는 일반적입니다. 솔직히 기술적인 질답을 할 때 상대방의 감정같은 것도 일일이 고려하면서 쓰는 것은 피곤한 일입니다.
컴퓨터기술의 다른 분야는 어떨지 모르지만(알지비님의 homepage에 가보니 오라클 관련 책의 저자시더군요.) C언어에 있어서 알지비님과 cinsk님은 기술적 격이 달라 보입니다. 그런데 알지비님이 C언어에 대한 글을 자주 올리다보니 cinsk님이 정확한 표현으로 수정하는 글을 올리게 된 것 뿐입니다. cinsk님의 글은 단지 기술적 내용에 대해서 말씀드린 것일 뿐 알지비님에게 대립각을 세우는 것은 절대 아니라고 봅니다.
cinsk님과 비슷한 연배라고 하시니 제가 이런 말을 하는 것은 주제넘은 것일지 모릅니다만 분명 이런 형태로 특정인에게 자신의 글이 지적당하다보면 기분이 상할 수도 있습니다. 하지만 알지비님은 기술지식에 대한 열망이 있기 때문에 계속 글을 올리시는 것일테고, 그렇다면 기분이 상하는 것은 물론 자신보다 대가인 분에게 접어둘 것은 접어두고, 배우게 된 것에 대해 감사하는 것이 배우는 사람으로서 더 도움이 될 것 같습니다.

rgbi3307의 이미지

저도 엔지니어로서 살아가면서 되도록이면 애매모호한 표현보다 정확하게 표현하는 것을 좋아합니다.
그런데, 저의 표현에 문제가 있다면,
그 문제를 지적한후 그것의 정확한 원인과 해결책이 제시되면 더더욱 좋겠습니다.
저의 처음 발제가 질문형이었습니다.
질문이 너무 짧아서 지적 받을 수도 있습니다.(이런 지적을 감내하고 글을 올렸습니다)
제가 오라클 튜닝에 관한 책을 집필했습니다만,
(오라클도 처음에도 어셈블리로 코딩되었고, 현재 다양한 언어로 버젼업되고 있습니다.)
오라클 DBMS는 컴퓨터로 처리할 수 있는 고급 알고리즘들의 집합체입니다.
저는 C언어를 1990년대 초반 학창시절부터 했습니다.
그런데, 요즘 기초에 충실하게 C언어 지식을 좀더 깊이있게 정리하고 있고,
제가 궁금한 부분은 KLDP와 같은 좋은 커뮤니티에 질문도 올리고 지식도 공유하고자 합니다.
처음 발제로 되돌아 가서,
"char (*(*x())[])();" 을 어떻게 정의하여 할당할 수 있을까요? 라는 질문형이었습니다.
그럼, 기술적인 입장에서
"그것은 이렇게 정의하여 사용하면 됩니다"라는 식의 글들로 의견교환되면 좋을 것입니다.
그러나, 답글다신 분들의 글을 보면 처음 발제의 주제와는 사뭇 다른 비판적인 글들이 많습니다.
다만, freestyle님의 글은 너무 좋아서 제가 답글을 달았고,
제가 실습한 내용을 공유하면서 여전히 의문형의 글을 올렸습니다.
그 의문점에 대해서 지적을하고 해결책을 제시하면 저도 감사의 마음을 전하겠습니다.
그러나 저의 글에 문제점만 지적했지, 해결책은 없는듯 하군요.
더구나, 사적으로 감정이 많이 상하는 부분도 있습니다.
왠만하면 저의 글(단언하는 글)에 대해서 사과하고 끝내고 싶지만, 이번에는 좀더 반박을 하고 싶습니다.

From:
*알지비 (메일: rgbi3307(at)nate.com)
*틈틈히 커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

winner의 이미지

이곳에서는 C표준을 통달하시는 분들이 많아서 올리신 질문은 공유할만한 새로운 문제제기로서의 가치가 매우 떨어집니다. cinsk님이 올리신 링크만 봐도 이미 거기에 대해서 글을 정리해서 올리신 분도 있고요. 달리 말하자면 검색만 해봐도 답이 나옵니다.
알지비님이 올리신 질문은 다른 곳에서는 어떨지 모르지만 여기서는 그냥 알지비님이 C언어에 대해서 잘 모르는 것을 질문한 것에 불과합니다. 자신이 속한 곳에서 자신의 위치를 잘 파악하는 것은 대단히 중요합니다.
모든 답변 글은 다들 고민하시고 쓰셨다고 봅니다. 그런데 어떤 분의 글은 자기가 좋아서 받아들이고, 다른 분의 글은 그렇지 않다고 받아들이지 않는 것은 배우는 사람으로서 소양이 부족하다고 생각합니다.

rgbi3307의 이미지

처음 발제인 "char (*(*x())[])();" 이것은 다른 사람의 답글을 통해서 해결하셨나요?

From:
*알지비 (메일: rgbi3307(at)nate.com)
*틈틈히 커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

winner의 이미지

이미 알고 있어서 그런지 모르지만 좋아하시는 freestyle님의 글로도 충분히 이해가 가고, cinsk님이 걸어주신 http://wiki.kldp.org/wiki.php/CLangaugeComplexDeclaration 에도 충분히 이해가 가게 해주고 있습니다. 그리고 다른 분들의 답변도 발제질문 자체는 아니지만 거기에 따른 부차적 관점에 대해서 잘 설명해줬다고 봅니다. 만일 이런 답변을 원하는게 아니었다면 K&R2에 나왔는데 의문이다라고 처음 발제글에 넣었다면 그런 답변이 적긴 했겠죠. 하지만 설령 그렇게 쓰셨더라도 부차적 관점에 대한 댓글도 충분히 소중한 것입니다.
알지비님이 여전히 잘 모르는 부분은 C언어의 type 변환에 대해서 표준이 보장해 주는 것이 어디서 어디까지인가라는 부분인데 이것은 C선언이 이해가 어렵다는 문제만이 아니기 때문에 다른 서적과 전자문서를 C언어 전체적으로 충분히 이해하는 것이 알지비님에게 도움이 될 거라 생각되어서 관련서적과 전자문서,그리고 mailing list를 알려드립겁니다.
알지비님도 발제질문에 대한 만족할만한 이해를 얻었기 바랍니다.

rgbi3307의 이미지

저도 이런 식의 답글을 엄청 싫어합니다.
말씀하신 공유된 문서들로 처음 발제에 대해서 이해하셨나요?
충분한 실습 결과가 있는지요?

http://wiki.kldp.org/wiki.php/CLangaugeComplexDeclaration
특히, 위의 링크 페이지를 보면,
하단부에,
char (*(*x())[])() /* x: function returning pointer to array[] of pointer to function returning char */ 이게 있는데, 설마 이것으로 이해했으리라 생각하고 계신지요?
저도 글을 올리기 전에 이미 본 내용입니다.
눈에 보이는 실습결과를 올리신다면 감사의 마음을 전하고 싶습니다.

From:
*알지비 (메일: rgbi3307(at)nate.com)
*틈틈히 커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

winner의 이미지

제가 알지비님의 1:1과외선생은 아닙니다만 어떤형태의 실습결과를 보시면 받아들일실지...
회사업무 중에 쓰는게 좀 부담되는군요. 후일 이해하시는데 도움이 되는 형태의 실습결과가 떠오른다면 올려드리겠습니다.

그런데 C언어는 implementation(그러니까 compiler)에 의한 정확성 보장이 거의 없는 언어입니다. 그래서 compile되고, 실행된다고 정확하다고 말하기 어렵죠. 뭐랄까... 실용성 측면에서 무의미할지 모르지만 C언어에 대해서 통달하신 분들은 그래서 표준의 조항을 논리적으로 해석해서 설명합니다. 만일 compile 결과와 실행결과가 그 논리와 다르게 나온다면 그것은 C표준을 지키지 못한 compiler와 실행환경의 한계라고 말하죠. 앞에도 말씀드렸지만 시험(내지는 실습... 보통 영어로는 trial을 쓰는 듯) 결과를 C source가 올바른지 판단하는 기준으로 삼기에는 너무 미약합니다.

rgbi3307의 이미지

위너님이 저의 1:1 과외 선생님이 아니라서 답변 달기에 좀 그렇군요.
나중에 인간적인 친밀감이 쌓이고, 회사업무중이라도 실습결과가 떠오른다면 올려드리겠습니다.

From:
*알지비 (메일: rgbi3307(at)nate.com)
*틈틈히 커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

cinsk의 이미지

웬만하면, 이런 일을 많이 당해서, 답변 안 합니다. 귀찮으니까요.

사람마다 느끼는 바가 다르겠지만, 여긴 "정보전달 > 친목"이 목적인 곳입니다.
잘못된 정보가 존재하면 치명적인 곳이죠.

> 실습 결과로 말씀드리면,
> "char (*(*x())[])(); 와 char *x(); 는 동일하다" 입니다.
> 간단하게 해석해서 포인터를 반환하는 함수입니다.

앞에서도 말했지만, 올리신 이 글에 답글이 하나도 없었다고 가정해보기 바랍니다. 해당 분야에 정확히 아는 사람이 없을 수도 있고, 바빠서 못 볼 수도 있습니다.

이 후, 어떤 사람이 포인터에 관한 글을 검색해서 위 문장을 봤다고 생각해보세요. 어차피 잘 몰라서 이 글을 찾아 읽은 사람이기에, 앞에 올리신 그 글만 읽는다면, 당연히 두 이름 'x'가 같은 것이라고 생각할 겁니다. 제가 우려하는 것은 바로 이런 상황입니다. 정보 전달을 목적으로 하는 곳에서, 불확실한 정보가 올라오는 것은, 아예 정보가 없는 것보다 못합니다.

차라리,
> "char (*(*x())[])(); 와 char *x();의 차이점이 뭘까요?
> 만약 서로 다르다면 아래 코드가 동작하는 이유는 먼가요?

라고 올리셨다면, 답변이 안달리더라도, 타인이 봤을때, 오해가 생길 가능성이 줄어들거라는 겁니다.

입장을 바꿔 말해보죠.
저는 커널을 잘 알지 못합니다. (사실 거의 모릅니다.) 하지만 printk(...)가 있다는 것은 알고 있죠. 만약 제가 다음 글을 올렸다고 생각해보세요.


커널 실험: printk()
linux:
  printk("value of i = %x\n", i);
 
hello.c:
  printf("value of i = %x\n", i);

실험해 보니, "printk()와 printf()는 동일하다" 입니다. 왜 다른 이름을 썼을까요?


여기 뿐만 아니라, kernel mailing list, news group에 위와 같이 질문을 올린다면, 호의적이거나 친절한 답변이 달릴 확율이 얼마나 될 것 같나요? 위 질문에 대답을 해 줄 수 있는 정도의 지식이 있는 사람이라면, 질문을 끝까지 읽기도 전에 "printk()와 printf()는 동일하다" 이 말만 보고 풋~ 이거나, 대답할 의욕?이 사라질 겁니다.

같은 질문을 아래처럼 한다면:


...

실험해 보니, printk()와 printf()는 크게 다른 것 같지 않은데요, 차이점이 뭘까요?


이 경우, 서로 답변할려고 아우성칠 확율은 적지만, 그래도 앞 질문보다 공격적인 답변을 받을 확율은 줄어들 겁니다.

지식 전달이 목적이 아니라면 상관없겠지만, 맞다면, 타인이 자신의 질문만 보고, 잘못된 해석할 우려가 없는 쪽으로 올려야 합니다. 그게 싫다면, 공격적으로 보이는 답변을 감수해야겠죠.

--
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
Korean Ver: http://www.cinsk.org/cfaqs/

rgbi3307의 이미지

> 실습 결과로 말씀드리면,
> "char (*(*x())[])(); 와 char *x(); 는 동일하다" 입니다.
> 간단하게 해석해서 포인터를 반환하는 함수입니다.

위에서 x() 함수는 포인터를 반환하지 않나요?

제가 이렇게 반박하면, cinsk님도 기분이 엄청 상할 것입니다.
제가 지금 그런 상황입니다만,

"char (*(*x())[])(); 와 char *x(); 는 동일하다" 라고 단언한 것은 사과합니다.
(제가 잘 몰라서 이렇게 표현해서 죄송합니다.)

그러나, 바쁘시더라도,
char (*(*x())[])(); 와 char *x(); 의 차이점을 충분히 고려하신 분은
cinsk님과 같은 답글은 올라오지 않습니다.

From:
*알지비 (메일: rgbi3307(at)nate.com)
*틈틈히 커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

rgbi3307의 이미지

제가 실습한 내용에 대해서

> 소(cow)는 동물(animal)이다. 개(dog)도 동물인데? 아.. 소는 개구나.

위의 표현으로 cinsk님의 논리를 전개하신것에 대해서, 사과를 받고 싶습니다.

From:
*알지비 (메일: rgbi3307(at)nate.com)
*틈틈히 커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

cinsk의 이미지

전 이런 식 논쟁은 싫어합니다. 이런 말로 시간 낭비하기 싫군요. rgbi3307님이 이 글을 보고 수긍할 수도 있고, 반박할 수도 있겠지만, 전 제가 관심있는 정보 내용에 관한 게 아니면 더 이상 답을 할 마음이 없습니다.

소는 동물이고... 이 말을 한 이유는,

> 실습 결과로 말씀드리면,
> "char (*(*x())[])(); 와 char *x(); 는 동일하다" 입니다.
> 간단하게 해석해서 포인터를 반환하는 함수입니다.

"실습을 해 보니 되더라, 그러니 두 이름은 같다."의 결론을 반박하기 위한 것입니다. 왜 저렇게 거칠게 말을 했냐면, 예전에도 이렇게 단언하지 말라고 이미 의견을 제시한 적이 있는데(참고), 계속 실습을 해서 되니 되는 것이다. 라고 글을 올리시니 그런 겁니다.

>> 정확한 출처가 없이, 단언하지 마시기 바랍니다.
> --> 정확한 출처가 없이 단언하지 않았습니다.
두 함수가 동일하다고 나와 있는 문헌, 책이 있으면 알려 주세요. "단언"이 어떤 의미인지 논쟁하고 싶지 않지만, 앞 두 이름이 같다고 하는건 제가 봤을 때 "단언"이군요.

시간나시면 HOWTO for beginners를 읽어 보시기 바랍니다.
이 글을 읽으시면, 답변을 하시는 분들이 왜 "친절하게 말하지 않고, 자세히 쓰지 않고, 심지어 기분 나쁜 투로 말을 할 때도 있는가?"에 대해 어느 정도 수긍할 수 있을 것입니다.

마지막으로 한가지 더 말씀드리면, 처음 질문은 "자유게시판"보다 "프로그래밍 QnA"에 올리는 게 낫겠군요.

--
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
Korean Ver: http://www.cinsk.org/cfaqs/

rgbi3307의 이미지

제가 표현한 글은 아래와 같습니다.

실습 결과로 말씀드리면,
"char (*(*x())[])(); 와 char *x(); 는 동일하다" 입니다.
간단하게 해석해서 포인터를 반환하는 함수입니다.
 
char (*(*x())[])(); 
 
이렇게 복잡하게 선언한 또 다른 이유가 있을까요?
혹은, 저의 실습코드에서 잘못된 점이 있을까요?

여기서, "실습을 해서 되니 되는 것이다." 라는 의미가 어디에 있습니까?

"char (*(*x())[])(); 와 char *x(); 는 동일하다" 라고
단언하는 표현에 대해서는 이미 사과 드렸습니다.
그러나, 전체적인 의미는 "이러한 결과가 나왔는데, 이유가 무엇일까요?" 라는 내용입니다.
내용이 기분 나빴나요? KLDP 법도에 어긋났나요?

"char (*(*x())[])(); 와 char *x(); 는 동일하다"는 잘못되었으니,
왜 다른지 이유를 밝혀 주시면 되지 않습니까?
관심 없으면 관두십시오.

From:
*알지비 (메일: rgbi3307(at)nate.com)
*틈틈히 커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

auditory의 이미지

char (*(*x())[])(); 와 char *x();

생긴게 다르니 다르다고 판단하는게 당연하고,
굳이 다르다는 설명을 하자면 앞서 누차 소개된 사이트에 가보면

•int *f() /* f: function returning pointer to int */
•char (*(*x())[])() /* x: function returning pointer to array[] of pointer to function returning char */

라고 설명이 있으니 어떻게 다른지 이해할 수 있어야 합니다.
( 위에 글에서 "설마 이것으로 이해했으리라 생각하고 계신지요?" 라고 쓰셨던데,
당연히 이해할 수 있어야 합니다. 이해가 안가면 이해가 될때까지 생각해야하고요.. )

당연히 다른걸 같다고 주장하고 계시고, 그 근거가 "실험해보니" 밖에 없으니,
사람들이 아래와 같이 설명해 주는 겁니다.

"실험해서 같다고 같다고 말하면 안된다고."

그리고 님이 실습하신 코드로 컴파일 할때 이런 경고가 나지 않던가요?
tmp.c:4: warning: return from incompatible pointer type

rgbi3307의 이미지

발제한 너의 표현이 잘못 되었으니 니가 이해하고 니가 답변하라는 건가요?
사실,
char (*(*x())[])();
이것이 이해가 않갑니다. 한 수 가르쳐 주십시오.

From:
*알지비 (메일: rgbi3307(at)nate.com)
*틈틈히 커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

auditory의 이미지

x: function returning pointer to array[] of pointer to function returning char

영어가 어려우시면, 앞에 freestyle 님이 한글로 쉽게 풀어놓으신 것도 있습니다.

rgbi3307의 이미지

영어도 어렵고, 앞에 freestyle 님이 한글로 쉽게 풀어놓으신 것도 이해 못했습니다.
한수 가르쳐 주십시오.

From:
*알지비 (메일: rgbi3307(at)nate.com)
*틈틈히 커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

auditory의 이미지


제 능력으로는 앞서의 설명보다 더 쉽게 설명드릴 방법이 없네요.

주위에 C언어를 아는분께 여쭤보시는게 좋겠습니다.

rgbi3307의 이미지

감사합니다.

From:
*알지비 (메일: rgbi3307(at)nate.com)
*틈틈히 커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

neocoin의 이미지

'아... 저렇게 쓰면 안되겠구나. ' 하는 느낌 말입니다.

s019923의 이미지

전 rgbi3307님이나 cinsk님 두 분 모두와 전혀 인연이 없습니다만, 그냥 지나가는 제 3자의 입장에서 봤을때, rgbi3307님이 기분이 상하실 수도 있겠다는데, 더 공감이 가는군요.
물론 받아들이는 사람에 따라 차이가 있을 수 있습니다만, 제게는 rgbi3307님의 글이 "의문형"으로 받아 들여졌고, cinsk님의 댓글은 약간 공격적인 느낌으로 다가오는 군요.

다른 사람의 기분을 생각하면서 글을 쓰는 것은 정말 힘들고 귀찮은 일임에는 분명합니다만, 그렇다고해서 결코 포기해서는 안되는 일이라고 생각합니다.

아무런, 내용이 없는, 그냥 지나가다가 써보는 개인적인 생각이였습니다.
태클은 정중히 사양하겠습니다.

winner의 이미지

C는 현대 programmer들에게 결코 편안한 언어가 되지 못합니다. 어셈블리에 비해서 편하다고 할 수는 있지만 또한 이식성을 지원하는데 정확성에 대한 보증을 compiler와 runtime이 하지 않는다는 것을 보면 더 골치아프기도 합니다.
어느 분야나 초급을 떼고 중급으로 넘어가는 과정이 다른 사람의 손길을 가장 필요로 한다고 봅니다. 중급에서 고급으로 가는 과정은 힘들어도 방황은 적을 수 있죠. 그런데 또한 초급에서 중급으로 넘어가는 과정에서 나오는 질문이 그래서 대답하기가 어렵습니다. 질문자의 이해수준을 가늠하기도 어렵고요. 그래서 질문자에 맞춰 정확한 답변을 설명할려면 책의 chapter 1개 분량이 요구될 수 있습니다. 거기다가 이해수준이 체계가 없는 상황에서 체계를 세우는 과정이라 질문도 중구난방에다가 많이 나옵니다. 사실 이런 질문은 체계를 창시한 사람들이 가장 많이 고민했던 문제일 겁니다. 그래서 저는 책 4권이나 추천하고, mailing list를 구독하라는 답변 밖에 못합니다.
이런 질문에 대해서 어떤 형태로든 답변을 자세히 한다는 것은 대단한 노력이 필요하고 문체를 떠나 감사할 일이라고 생각합니다.

아마도 cinsk님의 답변이 rgibi3307님의 기분을 상하게 하는 것이 있다면 게시판에 노이즈를 만든다는 것일텐데 저는 솔직히 공감하는 바입니다. KLDP 게시판이 정확한 자료를 구축하는 DB는 아닙니다만 질답의 마지막에서 올바르지 않은 결착이 나오면 비슷한 의문을 가진 사람이 검색할 때 잘못된 결과를 얻게 되죠. 거기에 대해서 책임의식을 가져야 한다는 것은 아니지만 또한 안타깝다는 생각도 듭니다. 괜히 질문하기 전에 검색해봐라라는 것이 아니죠. rgibi3307님의 의문제기와 지식공유에 대한 글을 담기에 KLDP 자유게시판, 질문게시판은 조금 부적절한 것 같습니다.

ymir의 이미지

보기로 든 예제는 복잡한 선언을 어떻게 해석하는지에 대한 설명을 위해 만들어진 것이지, 실제로 쓰이진 않을겁니다.
다시 풀어 보자면, char (*(*x())[])() 는 char 를 리턴하는 함수들의 포인터 배열, 즉, char (*flist[])() 와 같은 변수의 주소를 리턴하는 함수라는 뜻입니다.

#include <stdio.h>
 
char fa(void) { return 'a'; }
char fb(void) { return 'b'; }
char fc(void) { return 'c'; }
 
char (*(*x())[])()
{
	static char (*xlist[3])() = {&fa, &fb, &fc};
	return &xlist;
}
 
int main(void)
{
	char (**fs)();
 
	fs = (char (**)())x();
 
	printf("%c\n", (*fs[0])());
	printf("%c\n", (*fs[1])());
	printf("%c\n", (*fs[2])());
 
	return 0;
}

억지로 만들어 보긴 했는데, 대략 이런 모습이 될 겁니다.

C는 코드에 대한 제약이 느슨한 편이라, 멋대로 문법을 비틀어서 써도 돌아가는 코드를 만들어 낼 수가 있기 때문에, 문법을 정확히 이해하지 못한다면 오작동 하거나 전혀 다른 의도를 가진 코드를 정상적인 코드로 오해하기 쉽습니다.

위의 실습 결과도 보면, 잘못된 해석이 전혀 다른 의도를 가진 코드를 만들어냈고, 이게 다시 잘못된 결론을 이끌어 냈습니다. 많은 사람이 관심있어 하는 내용이 아니기에 그다지 댓글도 많지 않았습니다만, 결국은 그렇게 되면 잘못된 사실이 정설로 굳어지는 사태가 생길 수도 있을겁니다. cinsk 님의 우려도 이런 관점에서 나오지 않았나 생각됩니다. 모든 사람이 인터넷에서 벌어지는 오류에 대해 일일이 고쳐줘야 할 의무는 없으니까요.

복잡한 선언문을 해석하는 방법에 대해서는 freestyle 님 댓글을 다시 한 번 찬찬히 살펴보시고, 필요하시다면 http://cdecl.org/ 에서 도움을 받을 수도 있습니다.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

rgbi3307의 이미지

저의 궁금증을 풀어 주시는 실습코드를 공유해 주셔서 감사합니다.
다만, 공유해 주신 코드를 컴파일해 보면 리눅스 gcc나 cc에서는 경고메세지가 없는데,
Visual Studio에서는 "return &xlist;(10번라인)"에서 아래와 같은 경고 메세지를 뿌리는 군요.

warning C4048: 배열 첨자가 다릅니다 :
'char (__cdecl *(*)[])()'과(와) 'char (__cdecl *(*__w64 )[3])()'

그래서 아래와 같이 코드를 수정하여 포인터 형을 맞추니, 경고가 없어졌습니다.

#include <stdio.h>
 
char fa(void) { return 'a'; }
char fb(void) { return 'b'; }
char fc(void) { return 'c'; }
 
char (*(*x())[])()
{
	static char (*xa[])() = {fa, fb, fc};
	return (char (*(*)[])()) xa;
}
 
int main(void)
{
	char (**fs)();
 
	fs = (char (**)())x();
 
	printf("%c\n", (*fs[0])());
	printf("%c\n", (*fs[1])());
	printf("%c\n", (*fs[2])());
	return 0;
}

From:
*알지비 (메일: rgbi3307(at)nate.com)
*틈틈히 커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

ymir의 이미지

VS 로부터의 피드백이라니.. 이건 생각지도 못했군요.
그리고 이건 부탁인데, 실습이라는 단어의 사용을 지양해주셨음 하네요.
저는 학생도 아니고, 여긴 학원도 아닐뿐더러, 칭찬받기 위해 만든 코드도 아니고 누군가를 가르치기 위해 만든 코드도 더더욱 아닙니다.
근데 마치 숙제검사 받으려고 제출한것처럼 왠지 찜찜한 기분이 들게 하는군요.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

winner의 이미지

#include <stdio.h>
 
char fa(void) { return 'a'; }
char fb(void) { return 'b'; }
char fc(void) { return 'c'; }
 
char (*(*x())[])()
{
        static char (*xlist[])() = {&fa, &fb, &fc};
        return &xlist;
}
 
int main(void)
{
        char (*(*fs)[])() = x();
        printf("%c\n", (*fs)[0]());
        printf("%c\n", (*fs)[1]());
        printf("%c\n", (*fs)[2]());
 
        return 0;
}

&fa 라는 표현이 제가 공부가 부족해서 불안하긴 한데 아마 맞을 겁니다.
Visual C++에서 확장자를 .c 로 놓고 compile 하셨나보네요. 만일 .cpp로 놓으면 오류가 납니다.
g++를 통해 C++ compile을 하는 것도 마찬가지입니다.

C++로 제대로 compile을 할려면

#include <stdio.h>
 
char fa(void) { return 'a'; }
char fb(void) { return 'b'; }
char fc(void) { return 'c'; }
 
char (*(*x())[3])()
{
        static char (*xlist[])() = {&fa, &fb, &fc};
        return &xlist;
}
 
int main(void)
{
        char (*(*fs)[3])() = x();
        printf("%c\n", (*fs)[0]());
        printf("%c\n", (*fs)[1]());
        printf("%c\n", (*fs)[2]());
 
        return 0;
}

이것은 C와 달리 C++는 pointer의 묵시적 변환이 보다 엄격하거나 더욱 제한하기 때문입니다. 그리고 Visual C++가 앞의 C source에 대해서 경고를 내는 것은 과잉해석이라고 생각합니다.
이 source들을 이해할려면 불완전형에 대한 공부가 필요한데 제가 그동안 안 했던 거였습니다. 이번 기회로 좀더 공부하게 만드셨군요. 감사하며, 제가 앞에서 젠체한 것을 사과하겠습니다.
불완전형은 별 것은 아니라서 알지비님도 공부하시면 감을 잡으실 수 있을 겁니다.

제가 보기에 알지비님이 가장 많이 오류를 범하는 것이 pointer 변환인 것 같습니다. Pointer는 C가 low level로 들어가는 진입점이기 때문에 애매한 부분이 많습니다. C는 간결한 source를 작성할 수 있게 하고 programmer의 의도를 방해하지 않는다는 원칙때문에 pointer 변환이 자유로운 편입니다.

Bjarne Stroustrup이 C++에서 static_cast, dynamic_cast, const_cast를 도입하면서 변환을 제한하자고 생각했던 가장 큰 이유가 pointer 변환의 자유로움 때문이었다고 들었습니다. Class 상속으로 인한 pointer의 내부표현문제도 고민거리이기는 했겠지만 C programmer가 가장 많이 실수하는 부분이라는 거죠. James Gosling은 그것도 모자르다고 생각했는지 pointer를 reference로 전환하면서 더욱 제한하고 compile time 점검만이 아니라 runtime점검까지 넣어버렸죠.

알지비님께서 C 기초를 공부할 때 pointer 변환에 대해서 좀더 확실히 공부하시고 넘어가면 좋을 것 같습니다. 정렬제한이라는 또다른 덫이 있어서 고생 좀 하겠습니다만... 팁이라면 가능한 명시적 변환을 하지 마시고, 불완전형이 완전형으로 전환될 때는 아예 변수를 하나 더 만들어서 그냥 대입해보기를 권합니다(저는 이것을 학습용이 아닌 실제적인 programming을 할 때도 추천합니다.). 그렇게 해서 compile이 경고를 내지 않게 고심했던 것이 제 공부방법이었습니다. 그리고 표준을 엄격하게 확인해보고자 할 때 gcc로 -ansi -Wall -pedantic 옵션을 주었고요.

아, 그런데 저도 적절한 예제를 뭘로 들어야 하나 고심했었는데 ymir님이 적절한 예시를 해주신 것에 대해서 감사합니다.

ymir의 이미지

함수에 대해서는 func, &func, *func 모두 동일하게 함수의 주소 값을 리턴합니다.
뭐를 써도 상관 없기는 한데, 개인적으로 문맥에 맞게 & 나 * 를 적절히 끼워넣습니다.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

winner의 이미지

typedef을 적당히 써가면서 복잡한 선언을 여러 줄에 걸쳐 풀어 쓸 수도 있겠지만 C 문법에 자신이 있다면 이정도까지는 쓸 수도 있겠다 싶습니다.
signal 함수의 원형을 이해하시는 분이라면 이정도도 쓸 수 있겠죠. 완전히 문제를 위한 문제라고만은 생각하지 않습니다.