포인터에 대한 좋은 책 있나요?

ktlsu1231의 이미지

안녕하세요.

포인터에 대한 좋은 책 있나요?

시스템프로그래밍 쪽에 나오는 포인터는 일반 시중에 있는

포인터책에 없는 더 복잡한 코드들이 많이 보이네요.

좋은 책 있으면 추천해주세요. T_T

ziron7의 이미지

정보 문화사의 다시 체계적으로 배우는 C 포인터란 책이 좋죠~

yui의 이미지

ktlsu1231 wrote:
안녕하세요.

포인터에 대한 좋은 책 있나요?

시스템프로그래밍 쪽에 나오는 포인터는 일반 시중에 있는

포인터책에 없는 더 복잡한 코드들이 많이 보이네요.

좋은 책 있으면 추천해주세요. T_T

http://bbs.kldp.org/viewtopic.php?t=24890

ktlsu1231의 이미지

ziron7 wrote:
정보 문화사의 다시 체계적으로 배우는 C 포인터란 책이 좋죠~

이 책은 읽었습니다 .
yui 님 감사합니다.

ziron7님 좋은 자료 감사합니다.

sunyzero의 이미지

이상하게 옛날부터 C를 배우실때는 다들 포인터에서 난감해 하시는 분들이 많더군요. 하지만, 포인터를 떼기 위해서 이상하게 꼬아놓은 *, &를 마구잡이로 섞어놓고 무슨 뜻일까요? 하는 이런 문제 백날 살펴봐야 실력은 하나도 안늘어납니다.

포인터를 공부하시는것보다 체계적인 프로그래밍과 시스템을 공부하시다보면 그냥 자연스럽게 익혀지는 것이 포인터입니다. 그리고 어셈블리를 공부하면 더욱 빨리 익힐 수 있고요. 너무 포인터에 대해서 막연하게 공부하시면 그냥 풀에 지쳐서 C를 관둘수도 있으니 포인터에서 머뭇거리지 마시고 그냥 시스템 프로그래밍 책을 보세요. 그게 더 빠르고, 확실하게 배울 수 있습니다.

시중의 이상하게 포인터 꼬아서 가르치는 학원이나 책에서 배운 내용 써먹을때는 시험이나 기타 퀴즈에서뿐 실무나 시스템 프로그래밍에서는 거의 상관이 없습니다. ^^

========================================
* The truth will set you free.

dondek의 이미지

sunyzero wrote:
이상하게 옛날부터 C를 배우실때는 다들 포인터에서 난감해 하시는 분들이 많더군요. 하지만, 포인터를 떼기 위해서 이상하게 꼬아놓은 *, &를 마구잡이로 섞어놓고 무슨 뜻일까요? 하는 이런 문제 백날 살펴봐야 실력은 하나도 안늘어납니다.

포인터를 공부하시는것보다 체계적인 프로그래밍과 시스템을 공부하시다보면 그냥 자연스럽게 익혀지는 것이 포인터입니다. 그리고 어셈블리를 공부하면 더욱 빨리 익힐 수 있고요. 너무 포인터에 대해서 막연하게 공부하시면 그냥 풀에 지쳐서 C를 관둘수도 있으니 포인터에서 머뭇거리지 마시고 그냥 시스템 프로그래밍 책을 보세요. 그게 더 빠르고, 확실하게 배울 수 있습니다.

시중의 이상하게 포인터 꼬아서 가르치는 학원이나 책에서 배운 내용 써먹을때는 시험이나 기타 퀴즈에서뿐 실무나 시스템 프로그래밍에서는 거의 상관이 없습니다. ^^

전적으로 동감합니다.

포인터를 별 거지같이 꼬아놓고 그것을 해석하는 것을 가리키는 강사한테

왜 그러느냐고 한번 물어본적이 있는데,

그 강사왈..(신상을 공개할 수는 없겠져.. :) )

"난 한번 고민해서 무지 어려운 문제 만들어 놓으면 두고두고 학생들한테
써먹을 수가 있지. 그리고 그렇게 어려운거를 풀어주면 애들이 날 대단하게
쳐다 본다."

이런게 무슨 강사인지.....

포인터는 주소값을 저장하는 변수라는 것만 알면 되는것을 ..

그렇게 무지막지하게 꼬아 놓은 것을 대체 왜 배우고 싶어 할까여?

진리를 나의 수준으로 끌어내리지 마라.
나를 진리의 수준으로 끌어올려라. - 배꼽 중에서

mastercho의 이미지

맞습니다

옛날 어떤 인간[-_-;]
포인터 엄청 꼬아놓고 , 포인터를 이상하게 연산 시키는 프로그램을 보여주고는... , 어렵지? 이러는데

어이없는것이 , 그딴식으로 누가 프로그래밍하는냐는거죠 , 갇득이나 그렇게 안써도 안정성이나 버그때문에
머리 아픈데, 그렇게 포인터를 쓰면 망하죠

이렇게 전혀 쓸때없는 것을 가르치는 사람때문에 괜히 포인터를 더 어렵게 만드는게 아닌가 싶습니다

가끔 Q&A에서 그런것을 질문하는 사람을 보면 답답하더라는...

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

lsj0713의 이미지

그러나, 그런 복잡한 포인터 류의 문제가 전혀 가치없는 것만은 아닙니다. 자주 일어나는 것은 아니지만, 가끔 실제로 그런 복잡한 용법을 접할 때가 있으니까요. 포인터와 관련된 것으로는 생각나는 것이 없지만, 복잡한 선언에 대한 질문은 hclc나 이곳에 종종 올라왔던 것이 기억납니다. (주로 signal 함수와 관련된 질문이었던 것 같군요)

그리고, 그런 문제를 풀지 못하면 포인터에 대해 제대로 알고 있다고 말할 수 없습니다. 제대로 알고 있지 않다는 것은 잘못된 가정을 전제로 프로그래밍을 할 수 있다는 것이고, 결국 치명적인 버그로 이어질 수 있습니다.

vacancy의 이미지

그렇게 복잡한 포인터 연산을 해야하는 프로그램이라면
대부분 Design이 잘못되었을 가능성이 상당히 높다고 생각합니다. -_-;

소스 가독성이 높게 작성하는 것도
프로그래밍의 기술이라고 생각하는데요. -_-;

여튼 포인터는 책과 문서만 봐서는 잘 안늘고요.
linked list나 tree 등 간단한 것부터 직접 구현해보시면,
습득이 빠르실겁니다. ;;

mastercho의 이미지

사실 포인터를 마구 엉겨쓸때는 그런 방식으로
포인터를 쓰는 본인은 솔직히 어떻게 돌아가는지 모르겠습니까? , 알겠죠

하지만, 시간이 흐르고 나중에 자기 자신이 보더라도
왜 그렇게 코드를 짰는지 파악하기 힘들고 , 또한 시간도 걸리고 코드 가독성도 떨어집니다

그리고 남이 그 코드를 파악해야한다고 생각해보세요

포인터를 꼬은 문제는
100에 99.99는 그렇게 꼬아서 해결할 필요가 없는 문제입니다

lsj0713님이 보셨다는 그 포인터 문제도 아마도 그렇게 포인터 연산을 하지 않고도 해결할수 있는 문제라
파악됩니다

그리고 포인터는 그런식으로 남발하라고 존재하는게 아닙니다

있다해도 꼭 그렇게 해결해야 하는 문제가 있을까 싶네요

그리고 그렇게 C로 짠여진 프로그램중 대다수는 그렇게
짜지 않아도 되는데도 불구하고 ,
쓰는 사람의 성향때문에 , 그런 코드가 남아있는겁니다
습관이란 무서운것이거든요

유닉스 내부라는 책을 봐도
OS의 스케줄러 모듈이 객체지향적으로 짜야져야 함에도 불구하고 C로 억지스럽게 함수 포인터를 써가며
객체지향을 구현한것을 볼수 있는데[가상함수 흉내]
저자가 말하길 C++로 짜여져야 했지만
기존의 코드와 일관성을 유지하기 위해 그냥 C의 함수포인터로
억지스럽게 짰다고 말합니다

이렇듯 대다수의 아직 남아 있는 C코드는 굳이 그렇게 할 필요가 없는데도 불구하고 일관성때문에 남아 있는 경우가 많은것이죠

자바를 보더라도
포인터 연산이 지원 안되는데도 불구하고
거의 모든 어지간한 문제를 다 해결할수 있다고 봤을적에 , 이런 연산은 삽질임에 틀림없습니다

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

lsj0713의 이미지

저는 그러한 까다로운 코드를 옹호하고자 했던 것이 아닙니다. 다만 그런 까다로운 코드도 '학습용'으로는 나름대로의 가치가 있다는 것이죠. 일부러 그런 코드를 작성할 필요도 없고, 해서도 안되지만 그걸 해석할 능력 정도는 갖추는 것이 좋습니다. 그런 능력을 갖추지 못하면 언젠가 대형사고를 칠 가능성이 농후합니다.

게다가, 항상 원칙을 지켜 제대로 만든 코드만 마주친다는 보장은 없지 않습니까? 또한, 그렇게 짜지 않으면 무척 큰 성능상의 손실을 감수해야 하는 경우에는요?

그리고, 그런 복잡한 코드를 만나면, '이런걸 대체 왜배우지? 그냥 넘어가자'라고 생각하기보다는 '어 재밌네 한번 해석해볼까'라고 생각하는 것이 진정한 guru의 자세라고 생각합니다. :-)

sunyzero의 이미지

제가 던진 말에 대해서 갑자기 리플들이 많아졌군요.

윗분이 말씀하시는 것은 가끔사용되는 volatile이나 const의 의미나 혹은 하드웨어 조작에서 가끔, 그것도 아주 가끔 사용되는 경우일뿐, 문제의 본질인 포인터를 꼬아놓은 문제답지 않은 문제(솔직히 컴퓨터 학원에서 신입생 받을때 포인터 문제 내는거 보면 가관이더군요. 그게 제대로 C를 배우는 사람에게 가르치는 태도인지...)를 말한 것입니다.

그리고 위와 같은 경우도 아무리 꼬아봤자, 포인터는 3-4레벨정도의 depth를 넘는다면 디자인의 문제이며, guru라고 해도 그렇게 짜지는 않을겁니다. 그렇기 때문에 이상하게 C 수업에서 몇몇의 되먹지 못한 허접 강사들이 하는 포인터문제를 보면 웃음밖에 나오지 않더군요. 과연 그렇게밖에 쓸수 없는 상황이라면 모르지만, 배열하나 선언하고, 거의 10 depth의 포인터를 매겨버리면 그게 무엇을 가르칠려고 하는지 모르겠군요. 그래서 그렇게 언급했던겁니다.

윗분도 말씀하시려는 것의 의도는 저와 같아 보이는군요. 다만 윗분의 의도는 진정한 guru의 경지라면 어떤 포인터도 이해할 것이다. (실제 왠만한 실력만 갖추면 껌이죠) 하지만, 가독성도 중요하다. 뭐 이게 모두의 공감하는 부분이 아닐까요? 따라서. 포인터에 너무 정력을 쏟는 것은 바람직하지 못하다.로 결론짓는게 좋을듯 싶습니다. :lol:

========================================
* The truth will set you free.

fibonacci의 이미지

포인터 막 꼬아놓은 프로그램 에러나면, 디버깅하는 사람 죽습니다. -O-;

No Pain, No Gain.

전웅의 이미지

다소 격한 분위기에 뛰어들기가 망설여집니다만, 저는 C 언어 강사가 아니
기에 용기 내어... :-)

몇가지 복잡다단한 문제 (포인터, 복잡한 선언, C 언어 강사들의 문제 등등)
가 혼재하고 있는 듯 합니다.

우선, 실제 C 프로그램을 작성하거나 분석할 때 포인터 자체 (즉, 포인터
유도의 깊이) 의 복잡성이 문제가 되는 경우는 거의 없습니다. 사실, 표준
C 라이브러리 전체를 구현할 때도 이중 포인터 (pointer to pointer) 를 넘
어서 사용할 필요가 없었습니다. 다른 거의 모든 경우에도 포인터가 사용된
문맥의 다른 개념들 (형 변환의 안전성과 의미, null pointer/generic
pointer/function pointer 의 올바른 사용, 메모리의 할당과 대응하는 해제,
포인터가 사용되는 자료 구조 - 각종 list 와 table, 포인터가 사용된 코드
의 의미) 이 복잡성을 더하는 문제였지, 포인터 유도의 깊이 자체가 이해나
코딩을 방해하는 요소가 되지는 않습니다.

하지만, 포인터의 "어려운 점" 을 단지 포인터 유도의 깊이로 한정짓는 것
은 다소 부족한 면이 있다고 생각합니다. C 언어 포인터의 가장 어려운 점
은 그 규칙과 의미에 있습니다. 포인터와 관련된 언어적 규칙이 까다롭고
일부 친근한 환경에서 잘못된 포인터 연산이 우연히 기대하는 결과를 보여
주고 있기에, 포인터의 기초나 실질적인 사용에 능통한 경우라도 규칙과 의
미를 제대로 이해하지 못하면 자신의 프로그램에 언제 터질지 모르는 시한
폭탄을 심어 놓는 일이 쉽게 일어날 수 있다는 것입니다. 예를 들어, 책에
서도 언급했던 내용이지만

void poor_malloc(void **p, size_t s)
{
    *p = malloc(s);
    if (*p == 0) exit_on_error(MEMORY_FAIL);
}

...

int *p;
poor_malloc((void **) &p);

다수의 친근한 환경에서 예상대로 동작해주는 이 코드가 잠재적으로 어떤
문제를 안고 있는지 알기 위해서는 포인터의 의미와 그에 가해지는 제약을
제대로 이해할 필요가 있습니다. 우리가 "포인터가 어렵다" 라고 했을 때에
는 단순히 표면적인 포인터 연산의 모습만이 아닌, 포인터와 관련된 혹은
관련될 수 있는 문맥이 어렵고, 이를 위해 기본적인 포인터의 개념과 규칙
을 분명히 해둘 필요가 있다는 것이 제 생각입니다 - OP 께서 질문하신 포
인터에 대한 깊은 공부는 아마도 이런 것들이 아닐까 추측합니다.

그리고, 위에서 많은 분들이 지적하신 복잡하게 꼬아놓고 의미를 묻는 문제
는 아마도 복잡한 선언과 관련된 문제가 아닐까 생각합니다 - 예전 B모 전
산 학원에서 신입생을 뽑을 때 복잡한 선언을 해석하는 문제를 냈다고 하더
군요. C 의 선언 문법이 매우 잘 디자인된 것은 아니지만, 지극히 기계적인
연습을 통해 의미를 파악하고 또 직접 선언을 쓰는 것이 가능합니다. 물론,
실질적인 프로그래밍 과정에서는 typedef 를 통해 단계적으로 접근하거나
cdecl 같은 tool 을 사용해 선언을 이해하고 작성하는 것이 가능하겠지만,
분명 기계적으로 연습된 사람에 비하면 생산성이 우월하다고 말할 수는 없
습니다. 따라서, "숙련된" 프로그래머라면 그리 어려운 규칙 암기를 요구하
지 않는 복잡한 선언의 해석 방법은 익혀두고 있는 것이 많은 상황에서 유
용하지 않을까 생각합니다 - 연산자 우선순위를 암기하는 것보다 10배(?)는
쉽습니다. 더구나 실제 프로그래밍 환경에서 그와 같은 복잡한 선언을 만날
가능성이 전무하다면 그와 같은 노력이 헛되다고 주장할 수 있지만, 표준이
제공하는 signal() 의 type 만 해도 상당히 어려운 편에 속합니다 - 뉴스그
룹이나 게시판에 복잡한 선언과 관련된 문제가 올라오면 십중팔구는
signal() 과 관련된 질문입니다.

저 역시 지극히 비현실적인 (예를 들면, 언급하신 것처럼 현실적인 유용성
이 없는 10단계나 들어가는 포인터 유도를 해석한다든가 하는 등의) 문제로
점수를 매기려는 C 언어 강사들이 결코 바람직하다고 생각하지는 않습니다
만, signal() 정도를 분석하고 직접 쓸 수 있는 사람이라면 10단계의 포인
터 유도 역시 시간 문제일뿐 그리 "어려운" 문제에 해당한다고 생각하지는
않습니다. 진짜 문제는 그런 문제가 나왔다는 사실보다도 그 문제를 낸 강
사가 복잡한 선언의 해석 및 작성 방법을 체계적으로 학습할 수 있는 기회
를 제공했는지 여부라고 생각합니다.

그리고, 프로그램의 설계가 상당히 잘 된 경우라 해도, 성능에 민감한 부분
이나 포인터에 유난히 의존하는 C 언어 정의의 특성에 의해 겉으로 보기에
포인터를 과도하게 사용하는 경우도 있습니다. 따라서, "많은 포인터 사용
= 쓸데 없이 복잡한 설계" 라는 공식은 항상 참은 아닙니다.

size_t my_strlen1(const char *s)
{
    size_t i;

    for (i = 0; s[i] != '\0'; i++)
        ;

    return i;
}

size_t my_strlen2(const char *s)
{
    const unsigned char *p;

    for (p = (const unsigned char *)s; *p != '\0'; p++)
        ;

    return p - (const unsigned char *)s;
}

my_strlen1() 은 포인터보다는 배열의 의미를 겉으로 살린 경우지만, 여러
가지 이유로 실제 구현에서는 포인터가 난잡하게(?) 사용된 my_strlen2()
(혹은 그와 비슷한 형태) 가 사용됩니다.

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

mastercho의 이미지

Quote:
size_t my_strlen1(const char *s)
{
    size_t i;

    for (i = 0; s[i] != '\0'; i++)
        ;

    return i;
}

size_t my_strlen2(const char *s)
{
    const unsigned char *p;

    for (p = (const unsigned char *)s; *p != '\0'; p++)
        ;

    return p - (const unsigned char *)s;
}

my_strlen1() 은 포인터보다는 배열의 의미를 겉으로 살린 경우지만, 여러
가지 이유로 실제 구현에서는 포인터가 난잡하게(?) 사용된 my_strlen2()
(혹은 그와 비슷한 형태) 가 사용됩니다.

저건 이유가 없을듯 싶지 않나 싶네요 오히려 불필요한 연산 하나만 는게 아닌지요?

최적화를 하더라도 어셈블리는 거의 같다고 봤을적에 ... 왜 저런 코드가 쓰이는지 이해가 가지 않네요

-_-;; 정말 궁금하네요 왜 my_strlen2()가 쓰이나요?

이유가 듣고 싶습니다

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

saxboy의 이미지

제가 지금까지 보았던 가장 훌륭한 포인터에 대한 설명은 K&R 의 The C Programming Language의 포인터 섹션이었던 것 같은데요.

어릴때 C를 배우면서 백과사전만한 임인건님의 터보C 정복같은 책(... 을 폄하하려는 의도는 전혀 아닙니다)에 있는 포인터 설명을 보면서 대마왕같은 단어가 등장하면 괜히 주눅들곤 했었는데 K&R의 설명만큼 간결하면서 핵심을 주워준 것은 없었던 것으로 기억합니다.

아직도 기억나네요. 머릿속에서 복잡하게 꼬여있는 포인터와 배열에 관한 모든 것을 명확하게 풀어준 단 한줄. 2호선 지하철 안에서 읽었었는데 그때의 충격을 아직도 잊을 수가 없습니다.

a is &a[0];

K&R의 이 명저는 정말 대단해요. C를 배우면서 읽고, 가끔 궁금한 곳이 생기면 뒤적여보고, 컴파일러 만들면서 다시 펴보고, 가끔 슬쩍 펴 보는 순간마다 몰랐던 것들을 하나씩 발견하게 되고... 우스갯소리로 이 책을 완전히 이해하면 전산과 졸업해도 된다는 이야기도 있었지요. 허튼 소리가 아니라고 생각합니다. 아직까지도 이 책을 잠깐씩 펴보면 다시 한번씩 무언가 생각하게 해주지요.

자매품 stroustrup의 The C++ Programming Language도 이런 맛깔스런 책중의 한권이라는 생각이 듭니다. 저는 2nd ed. 밖에 없지만...

pynoos의 이미지

mastercho wrote:
Quote:
size_t my_strlen1(const char *s)
{
    size_t i;

    for (i = 0; s[i] != '\0'; i++)
        ;

    return i;
}

size_t my_strlen2(const char *s)
{
    const unsigned char *p;

    for (p = (const unsigned char *)s; *p != '\0'; p++)
        ;

    return p - (const unsigned char *)s;
}

my_strlen1() 은 포인터보다는 배열의 의미를 겉으로 살린 경우지만, 여러
가지 이유로 실제 구현에서는 포인터가 난잡하게(?) 사용된 my_strlen2()
(혹은 그와 비슷한 형태) 가 사용됩니다.

저건 이유가 없을듯 싶지 않나 싶네요 오히려 불필요한 연산 하나만 는게 아닌지요?

최적화를 하더라도 어셈블리는 거의 같다고 봤을적에 ... 왜 저런 코드가 쓰이는지 이해가 가지 않네요

-_-;; 정말 궁금하네요 왜 my_strlen2()가 쓰이나요?

이유가 듣고 싶습니다

저의 경우에도 my_strlen2를 선호합니다. my_strlen 은 느낌학상 무겁고, 가볍게 scan 한다는 기분보다는 하나씩 끄집어 내어 본다는 느낌이 더 들지요.

저 정도는 사실 pointer로 꼬아놓았다는 문제에 들것 같지는 않군요.

전웅님말대로, 대부분 함수 포인터를 만날때, 포인터를 꼬아놓았다는 얘기를 하는 것 같습니다. 하지만 이런 함수포인터 조차(?) 꼬아놓은 문제로 이해한다면... 안되는데 말이죠.

제가 느끼는 포인터가 꼬여있는 복잡(?)한 문제는... 함수에 스트럭쳐를 넘기면서... 그 스트럭쳐 안에 있는 데이터 일부가 두번째 인자로 또 넘어간다든지 하는.. 초기 의도에 없던것을 추가하던 코드에서 발견되는 것에 주로 있었습니다.
포인터 변수가 많아지면서 const 마저 없다면 어떤 것이 입력용이고 어떤 것이 출력용인지 나중에는 머리가 돕니다...

전웅의 이미지

mastercho wrote:
Quote:
size_t my_strlen1(const char *s)
{
    size_t i;

    for (i = 0; s[i] != '\0'; i++)
        ;

    return i;
}

size_t my_strlen2(const char *s)
{
    const unsigned char *p;

    for (p = (const unsigned char *)s; *p != '\0'; p++)
        ;

    return p - (const unsigned char *)s;
}

my_strlen1() 은 포인터보다는 배열의 의미를 겉으로 살린 경우지만, 여러
가지 이유로 실제 구현에서는 포인터가 난잡하게(?) 사용된 my_strlen2()
(혹은 그와 비슷한 형태) 가 사용됩니다.

저건 이유가 없을듯 싶지 않나 싶네요 오히려 불필요한 연산 하나만 는게 아닌지요?

최적화를 하더라도 어셈블리는 거의 같다고 봤을적에 ... 왜 저런 코드가 쓰이는지 이해가 가지 않네요

-_-;; 정말 궁금하네요 왜 my_strlen2()가 쓰이나요?

이유가 듣고 싶습니다

두가지 이유가 있습니다.

하나는 첨자를 사용한 배열 접근보다 (동일한 효과를 얻는) 포인터를 통한
접근이 일반적으로 더 빠르다는 믿음에 기반한 것입니다. 즉, my_strlenn()
같은 함수가 프로그램 내에서 빈번히 사용되기에 그 작은 차이가 최종적으
로 유효한 차이를 만들 것이라는 믿음입니다. 물론, 항상 my_strlen2() 가
더 빠르다는 "보장" 은 없습니다. 또한, profiling 이전에는 my_strlen2()
의 성능에 대한 기여도가 얼만큼인지 말할 수도 없습니다. 하지만, 완전히
동등한 semantic 이 보장된다면 my_strlen2() 로 구현하는 것이 손해는 아
니라는 경험에 의존한 선택입니다.

또 다른 이유 (즉, unsigned char * 로의 변환) 는 "까다로운" 이식성 입니
다. 문자열을 끝내는 null character 는 모든 bit 가 0 인 문자 (값이 0 인
문자가 아닙니다) 로 정의되어 있습니다. 하지만, ones' complement 나
sign-magnitude 를 음수 표현에 사용하는 환경에서는 negative zero 라는
놈이 지원될 수 있습니다. 이때 signed char 로는 -0 과 0 이 모두 0 과 같
게 비교되기에, unsigned char 만이 -0 을 0 에서 구분할 수 있습니다. 그
리고, -0 는 분명 (모든 bit 가 0 이 아니므로) null character 가 아닙니
다. null character 의 이와 같은 까다로운 정의에 의해 (그리고 strlen()
의 원형이 char * 로 고정되어 있기에) 이식성을 고려한 경우에는 unsigned
char * 로의 변환이 불가피한 것입니다.

결국, 포인터를 사용해 가독성을 떨어뜨리면서 (솔직히, 저도 위와 같은 정
도가 프로그램의 가독성을 해친다고 생각하지는 않습니다. for loop 대신
while loop 과 *p++ 를 사용하고 != '\0' 비교를 생략하는 것이 제 원래 프
로그래밍 습관입니다 - 요즘 고치려고 노력 중입니다) 성능과 이식성이라는
다른 가치를 얻은 경우입니다. 이 경우 위와 유사하게 포인터를 사용하지
않는다면 어떠한 경우에도 이식성 있는 방법으로 이 두 가지를 얻을 수 없
습니다.

그럼...

p.s. 참고로 "불필요한 연산 하나" 가 type casting 을 말씀하시는 것이라
면, type casting 은 static 하기에 compile-time 에 이루어질 수 있
습니다. 따라서, run-time overhead 는 갖지 않습니다.

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

mastercho의 이미지

Quote:
p.s. 참고로 "불필요한 연산 하나" 가 type casting 을 말씀하시는 것이라
면, type casting 은 static 하기에 compile-time 에 이루어질 수 있
습니다. 따라서, run-time overhead 는 갖지 않습니다.

return p - (const unsigned char *)s;

그게 아니라 -_-;

2개의 함수를 비교해면 결국 연산은 거의 같은데
마지막에
return 에 보면 - 연산을 더하지 않습니까? --;

- 계산하고 거기서 임시 객체를 리턴하는게 표준 동작 방식이 맞다면

컴파일러가 최적화 할때 그걸 못 잡아낼경우 똑같은 동작에

연산 하나가 더 느는 결과가 아닐런지요?

어셈블리로 확인해도 최소한 더 좋은 결과는 못내지 않을까 싶네요

그리고
표준동작으로 배열 접근 방식은 똑같다고 생각되는데요...? [같은 어셈코드 유발]

따라서 전혀 성능의 차이가 날 이유는 없다고 보고요

비교를 해봐도 결국 첫번째 함수가 모든면에서 우위에 있다고 봐집니다

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

전웅의 이미지

mastercho wrote:
그게 아니라 -_-;

2개의 함수를 비교해면 결국 연산은 거의 같은데
마지막에
return 에 보면 - 연산을 더하지 않습니까? --;

크크크.. 제가 헛다리 짚었군요. ;-)

mastercho wrote:
- 계산하고 거기서 임시 객체를 리턴하는게 표준 동작 방식이 맞다면

컴파일러가 최적화 할때 그걸 못 잡아낼경우 똑같은 동작에

연산 하나가 더 느는 결과가 아닐런지요?

어셈블리로 확인해도 최소한 더 좋은 결과는 못내지 않을까 싶네요

그리고
표준동작으로 배열 접근 방식은 똑같다고 생각되는데요...? [같은 어셈코드 유발]

따라서 전혀 성능의 차이가 날 이유는 없다고 보고요

비교를 해봐도 결국 첫번째 함수가 모든면에서 우위에 있다고 봐집니다

포인터 연산과 배열 indexing 의 성능 문제는 (모든 "성능" 문제가 동일하
듯이) 구체적인 문맥이 제시되지 않은 상태에서는 단정지을 수가 없습니다.
즉, 어떤 CPU 에, 어떤 OS 에, 어떤 컴파일러에, 어떤 옵션을 사용해 프로
그램을 번역 및 실행했느냐에 따라 결과가 완전히 달라질 수 있습니다 - 그
래서 제가 반복해서 "믿음" 이라는 표현을 사용한 것입니다. 예를 들어, 포
인터 연산과 배열 접근을 사용한 프로그램의 성능을 비교할 때 서로 다른
두 환경에서 정반대의 결과가 나오는 것도 가능합니다 - 실제로 있었습니다.

하지만, C 언어에서 포인터 연산이 (배열 indexing 에 비해 상대적으로) 갖
는 의미를 생각해볼 때, 어떤 것이 더 빠를지 알 수 없는 상황에서는
indexing 대신 포인터 연산을 선택하는 것이 이익을 가져다줄 (혹은 최소한
큰 손해는 아닐) 가능성이 크다는 것이 지금까지의 경험에 의존한 일종의
관례입니다 - 특히나, 예측할 수 없는 여러 환경으로 포팅될 가능성이 있는
라이브러리나 프로그램의 경우에는 이러한 선택이 유효할 수 있습니다. 물
론, 그러한 주변 문맥을 무시하고 "모든" 환경에서 포인터 연산이 우월하다
는 단정 역시, "항상" 배열 indexing 이 우월할 것이라는 단정과 마찬가지
로 정당할 수는 없습니다.

그리고, 위와 같은 경우에는 마지막에 수행되는 - 연산이 있음에도 루프에
서 이루어지는 포인터 연산에서 충분히 이득을 볼 수 있다는 생각으로 포인
터 쪽을 선택한 것입니다. 예전에 제가 테스트했던 경험에 의하면 IA-32 에
서 gcc 의 "똑똑한" 최적화 기능을 사용할 경우 위 프로그램은 배열과 포인
터가 같거나 배열이 더 나은 성능을 보인 것으로 기억하고 있습니다. 하지
만, 한 환경에서의 실험 결과는 그 환경에서만 유의미할 뿐, 일반화될 수는
없습니다.

마지막으로, 요즘은 컴파일러의 최적화 기능이 상당히 탁월하기에 (혹은 성
능 문제가 너무 다양한 요소의 영향을 받기에), 특수한 경우가 아니면 동일
한 의미의 배열 indexing 과 포인터 연산을 놓고 성능을 논하는 것 자체를
무의미한 것으로 보기도 합니다. 하지만, 성능 문제를 배제하더라도 현재로
서는 포인터가 C 언어에 제공하는 표현의 유연함은 결코 무시할 수 없는 상
태입니다.

그럼...

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

댓글 달기

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