1) 지식 창고는 본인이 작성한 콘텐츠(팁/노하우/리소스/강좌 등)을 무료 혹은 가상화폐인 납포인트를 통해 공유하는 공간입니다.
2) 본인이 작성한 콘텐츠에 대해서만 지식 창고에 등록할 수 있으며, 저작권에 위배되는 콘텐츠는 사전경고 없이 삭제될 수 있습니다.
3) 콘텐츠 구매 및 첨부파일 다운로드는 회원그룹 '연구원' 이상 가능하오니, 경험치를 쌓아 진급한 후에 이용 부탁드립니다.
4) 무료 콘텐츠의 본문은 구매절차 없이 즉시 이용할 수 있으며, 판매 납포인트가 있는 콘텐츠는 구매 후 이용할 수 있습니다.
5) 콘텐츠 판매에 따른 납포인트 수익은 지정한 비율(50%)에 따라 판매자에게 지급하며, 납포인트 수익을 통해 진급을 빨리할 수 있습니다.
6) 구매 후 평가를 하면 구매 납포인트의 20%를 돌려 드립니다.
판매자 | 프리미엄 | 판매 납포인트 | 무료 | 평점 | 0점 / 총 0명 참여 |
---|
***************************< 캠퍼스 C 강좌 >******************************
[제목] :
[코드] : campusc1-012 (초급)
[교재] : CAMPUS C (초급, Third edition) [출판사 : 책과스승]
[알림] :이 파일은 "캠퍼스 C"에서 모든 분께 공개한 "초급 강좌"입니다.
이 [알림]의 내용을 지우지 않는다면 누구에게나 임의로 복사해 줄 수
있습니다.그러나 이 강좌 내용에 대한 저작권은 "캠퍼스 C"에 있습니다.
[연락처] : 605-8662 (서울) ("캠퍼스 C", 도서출판 "책과 스승")
천리안 : go campusc
나우콤 : go lcampc
하이텔 ID : campusc
****************************<< 목 차 >>************************************
<1> 2차원 배열 p[][] 의 의미
<2> 2 차원 포인터 ( **P )에 대해서
1> **p 의 개념
2> **wang_bal의 초기화
3> **wang_bal의 사용법
<3> *p[] 에 대하여
1> *p[] 의 개념
2> *p[]의 초기화
3> *p[]의 사용법
**************************< 내용 시작 >********************************
<1> 2차원 배열 p[][] 의 의미
***********************************************************************
2 차원 배열 얘기를 한적이 있는데, 연습을 위해 다시 보면 [162쪽]
의 그림과 같은 형태라고 했다. 즉 아파트 한동에 해당한다. 이 개념은 어려
울 것이 없다고 믿는다. 그러나 집을 찾아주는건 아직도 신경을 써야 하는
문제이다. 다음을 봅니다.
char week[7][4] = { "SUN",
"MON",
"TUE",
"WEN",
"THR",
"FRI",
"SAT"
}
main()
{
printf("%c", week[4][2] ); ---> ?
}
위는 무슨 글자를 찍는가 ? 답은 'R'이다. 사실은 문제를 내고 있는
나도 헷갈린다. 문제가 어려워서가 아니라 따지는게 번거로와서다. 게다가
우리가 설명하고 있는 아파트하고는 층이 뒤집어져서 더욱 혼동되고 있다.
그러나 좌우간 위의 아파트는 옆으로 4집이 붙고 7층짜리 아파트다.
위에서 문제낸 집을 찾으려면 "위로 4층 올라가고 ( [0]부터 따짐) 옆으로
2집" 만큼 가면된다. 그러면 다시 문제를 내 보자
<문> 위에서 week[0][0]의 주소는 100호 라고 하자. 그러면 다음의 주소는 ?
main()
{
printf("%d", &week[4][2] ); ---> ?
}
답은 118호다 이것도 물론 한 집씩 세면 답을 찾을 수 있을것이다.
앞으로 우리의 표현은 정말 하염없이 복잡해지기 시작 할 것이다. C
에서 그 유명한 포인터가 "발광"을 하기 시작하면 그 복잡함이란 정말로 대
단하다. 그러나 거기에는 아주 "간단한 규칙"이 있고 또 그런 표현을 쓰는
이유도
------------------------------
1. 그 집 주소를 찾을 수 있나 ?
2. 그 주소의 값이 얼마냐 ?
------------------------------
이 "두가지 문제"를 찾으려고 하는 데 "궁극적인 목적"이 있다. 따라서 우리
는 항상 이 사실만 염두에 두면 모든 문제는 아주 쉬워 질 수 있다.(중요)
=======================================================================
<2> 2 차원 포인터 ( **P )에 대해서
1> **p 의 개념
2> **wang_bal의 초기화
3> **wang_bal의 사용법
=======================================================================
1> **p 의 개념
-----------------------------------------------------------------------
지난회에서 포인터와 배열이 넘나드는 것을 보았다. 즉 *p 와 p[]
가 같은 의미로 쓰였다.(그 내용을 정확히 이해하지 못했으면 다시 읽고 와
야 한다) 즉 C 에서는
-------------------------------------------
'*' 과 '[]' 이 무조건 일차원 씩에 해당된다
-------------------------------------------
고 기억하자. 그러면 **p 가 이차원 포인터 인 것은 당연하다. 이것도 내용
이 어렵지 않다. 일차원 포인터 *p 를 이해하는 방법과 똑같다. 문패를보자.
여기서 주소는 내 맘대로 적은 것이다.
-----< 100 호 >----- -----------< 200 호 >--------------
가수 임 청하 가수 (의집을 알려주는집) 마 당발
-------------------- ------------------------------------
-------< 300 호 >--------------------------------
가수 (의집을 알려주는집) (을 알려주는집) 왕 발
-------------------------------------------------
여러분은 한국말을 잘 알고 있으니까 모든 내용을 명쾌하게 이해하리라고 생
각 한다. 확실히 하기 위해서 문제를 보자.
<문1>
"왕 발" 씨네 들어갈 수 있는 주소는 "임청하"의 주소 100호 인가 "마당발"
의 주소 200호 인가 ? --> (답) 200호 (마 당발)
<문2>
마 당발 씨 집에 들어 갈 수 있는 주소는 ? --> (답) 100호(임 청하)
<문3>
왕 발 씨네 임청하의 주소를 넣으면 맞는가 ? --> (답) No
<문4>
3번이 왜 틀리는가 ?
(답) 한국말도 모르니 ?
왕발 씨는 "가수 (의집을 알려주는집) (을 알려주는집)" 이야.
<문5>
위 문패에서 "가수" 대신 char, int, long으로 한번씩 바꾼다면 집 크기
는 어찌 되나 ?
(답)
이제 문패에서 (의 집을 알려주는집) 대신 '*' 를 쓰기로 하자.
char 임청하 : --> 1 채 (100 호 )
char *마당발 : --> 2 채 (200,201호)
char **왕발 : --> 2 채 (300,301호)
int 임청하 : --> 2 채 (100,101호)
int *마당발 : --> 2 채 (200,201호)
int **왕발 : --> 2 채 (300,301호)
long 임청하 : --> 4 채 (100,101,102,103호)
long *마당발 : --> 2 채 (200,201호)
long **왕발 : --> 2 채 (300,301호)
임 청하는 돈을 점점 많이 벌어서 집을 늘려가는 모양이다.그러나 그 집을
알려주는 포인터 들은 그렇지 않다. 항상 손가락 하나 들어갈 크기(2 바이
트)만 있으면 된다(중요). 이제 다음과 같은 사실을 유념해야 한다.
-----------------------------------
1. 각각의 변수명이 가질 수 있는 값
2. 각각의 변수들의 아파트 크기
-----------------------------------
-----------------------------------------------------------------------
2> **wang_bal의 초기화
-----------------------------------------------------------------------
위에서 왕발은 마당발만 상대한다는 것을 알았을것이다. 간단한 개
념은 군대에서 중령(**)은 소령(*)만 상대하는것과 똑같다. 아무것도 안붙은
"쫄병"을 중령이 직접 상대한다는 것은 체면문제 아닌가 ? 따라서 초기화는
다음과 같이 한다.
char lim_chungha, *ma_dangbal;
char **wang_bal = &ma_dangbal; ( o )
char **wang_bal = &lim_chungha; ( x ) <-- 쫄병을 직접 상대 하다니
main()
{
}
-----------------------------------------------------------------------
3> **wang_bal의 사용법
-----------------------------------------------------------------------
일차원 포인터의 사용법을 완전히 알았으면 이차원도 역시 다를것이
아무것도 없다. 즉
char lim_chungha, *ma_dangbal, **wang_bal; <-- <의미 1>
main()
{
lim_chungha = 's';
ma_dangbal = &lim_chungha;
wang_bal = &ma_dangbal;
printf("%c", lim_chungha);
printf("%c", *ma_dangbal );
printf("%c", **wang_bal ); <-- <의미 2>
}
지난 시간에 다룬 "'*' 의 3 가지 의미"를 아직 기억하시는지 모르
겠다. 위에 표시한 두군데 "왕발"은 그 의미가 틀리다는 것을 구별해야 한
다.즉
<의미 1> --> 변수 wang_bal 은 2차원 포인터다.
<의미 2> --> 변수 wang_bal (이 가리키는집) (이 가리키는 집)의 값을 찍어
라
여러분은 이제 내가 하는 쓸데없는 설명보다 어느새 그 "의미" 자체
를 눈치 챌것으로 기대한다. 사실 위 <의미 1, 2>는 별개적인 내용이 아니
다. 일종의 공식같은 요령이 있는데
--------------------------------------------
왕발이 2차원 포인터로 선언되었으면,2 차원으로
찍어보라고 할때 "값"을 찍는다.(주소가 아니고)
--------------------------------------------
일반적으로 얘기하면
--------------------------------------------
변수 xxx 가 n 차원 포인터로 선언되었으면,
n 차원으로 찍어보라고 할때 "값"을 찍는다.
--------------------------------------------
나는 이것만 외고 다니기 때문에 별 혼동이 없다. 이 관점에서 위의 문제를
다시 보면 위의 값 3개는 전부 "값"을 찍고 있다는 것을 한눈에 알아 볼 수
있다.
그러면 2차원 포인터 왕발이 찍을 수 있는 주소는 어떤것이 있는가 ?
char lim_chungha, *ma_dangbal, **wang_bal;
main()
{
lim_chungha = 's';
ma_dangbal = &lim_chungha;
wang_bal = &ma_dangbal;
printf("%d", &wang_bal); --> 300 호 (자기 집 주소)
printf("%d", wang_bal); --> 200 호 ( 0 차원 )
printf("%d", *wang_bal); --> 100 호 ( 1 차원 )
printf("%d", **wang_bal ); --> 's' ( 2 차원 )
}
위에 답을 달아 놓았다. 자기집 주소를 찍어보는 '&'는 " 1 차원"과 아무 상
관이 없다. 오로지 '[]'와 '*' 만이 " 1 차원"을 만든다.
중령 "왕발"은 위와 같이 자기 보다 아래 것인 "소령" 표현, "쫄병
(0 차원)"표현이 가능하다. 그리고 이때 까지는 전부 남의 주소를 찍고 있음
을 보라. 오로지 자기가 선언한 계급장 형태가 되어야 비로소 "값"을 찍는
다.
소령인 "마당발"이 중령표시 "**"를 달 수 없다는 것은 당연하다.
그건 "뜻이 이해가 안되기 때문이다".
<문제> [188쪽] (예제 1)을 풀어보세요
<문제> 바로 위 문제에서 틀린 표현은 지우고, 정상적인 표현에 대해 그 결
과를 직접 적어 보시오. 그리고 그 결과가 "값"을 의미하는지 "주소"를 의미
하는지 구별해 보시오. 그리고 변수 선언된 자기의 형태와 구별하시오.
=======================================================================
<3> *p[] 에 대하여
1> *p[] 의 개념
2> *p[]의 초기화
3> *p[]의 사용법
=======================================================================
1> *p[] 의 개념
-----------------------------------------------------------------------
이것도 이차원이라는 사실은 알것이다. 그러나 이제부터 문제는 조
금 복잡해지기 시작 하는데 다음과 같이 선언 되었다고 하자. 그 유명한 마
당발 씨가 또 나오는데,
char *ma_dangbals[3];
main()
{
}
<문제> *ma_dangbals[] 가 포인터 인가 배열 인가 ?
<답> 배열이다.
포인터와 배열이 넘나들 수 있다고 해서 물론 똑같은 것은 아니다.
따라서 우리는 일단 배열인지 포인터 인지는 알고 넘어가야 한다.
이것을 구별할 수 있는 유일한 근거는 연산자 표 이다. 다같이 [88
쪽]을 보자. [] 와 * 중 어느것이 우선순위가 높은가 ?
* ma_dangbals [3]
표에 의하면 []가 우선순위가 높다. 이것은 무슨 의미인가 하면 ,
일종의 삼각관계인데, 예를들어 "Miss 배열[]" 양과 "Miss 포인터 *" 양이
동시에 "마당발" 씨에게 전화를 걸어 만나자고 한다면 "마당발"씨는 "배열"
양에게 먼저 나가기로 되 있다는 뜻이다. 이 둘의 관계가 더 밀접하다.
따라서 ma_dangbal씨는 배열이다. 그러면 "Miss 포인터"의 존재는
무엇인가 ? 사실 이 내용은 여러분이 이미 알고 있다. 다음을 보자.
char lim_chungha;
char * ma_dangbal;
char * ma_dangbals[3];
main()
{
}
위에서 밑줄친 부분의 의미가 똑같다. 즉
1. ma_dangbal 은 "char 변수를 가리키는 포인터" 다
2. ma_dangbals[3] 도 똑같다. (배열 요소 3 놈이 똑같은 일을 한
다)
이걸 우리가 좋아하는 한국말로 쓰면 다음과 같다
-----< 100 호 >----- -----------< 200 호 >------------------
가수 임 청하 가수 (의집을 알려주는집) 마 당발들[3]
int lim_chungha int *ma_dangbal[3]
-------------------- ----------------------------------------
한국말 그대로 마당발들의 집합이다. 즉 마당발[0], 마당발[1], 마당발[2]
가 각각 가수의 집 주소를 가리키는 포인터 변수 역할을 한다. 임청하의 팬
들이 너무 많아서 주소를 알려 주는 사람이 3명이 필요 한 모양이다. 아니면
따로따로 제각각 다른 가수의 이름을 알려 줘도 물론 상관없다.
지금 까지의 설명으로 일단 개념은 이해가 뻍으리라고 믿는다.
-----------------------------------------------------------------------
2> *p[]의 초기화
-----------------------------------------------------------------------
초기화는 다음과 같이 한다.
char *ma_dangbals[3] = {
"lim_chungha",
"so_daiji",
"jo_yongpil",
}
현재 마당발들은 배열 형태를 이루고 있지만 마당발[0], 마당발[1],
마당발[2], 각각은 두 바이트 크기를 갖고 있는 "포인터"다. 따라서 당연히
위의 긴 가수 이름들을 자기가 갖고 있지못한다. 포인터가 항상 그렇듯이
"선두 번지만" 갖고 있을 뿐이다. 개념상으로는
ma_dangbals[0] --> "lim_chungha"
ma_dangbals[1] --> "song_changsik"
ma_dangbals[2] --> "jo_yongpil"
의 선두 번지를 갖고 있는 형상이다.
이 형태를 개념적으로 그려 놓은 것이 [200쪽] 상단의 그림이다. 이 모습을
확실히 기억하는 것이 필요하다.
-----------------------------------------------------------------------
3> *p[]의 사용법
-----------------------------------------------------------------------
실제로 우리가 프로그램 작성에 들어가면 이 표현은 기본적으로 쓰
인다. 이것도 물론 내용을 따지려면 복잡할지도 모르지만 , 보통 우리는 상
투적으로 쓰는 표현이 있어서 그냥 "무심"지경에서 쓰면 된다. 이건 마치 우
리가 운전할 때 "브레이크"를 밟는것과 똑같다. "차를 세우려면 브레이크를
밟아라".우리는 이 사실을 알면 일단 운전을 할 수 있다. 처음부터, "브레이
크를 밟으면 차가 왜 서는가 ?" 이걸 공부하려면 아마 운전 배우는 일을 포
기할 것이다.
따라서 단순히 다음과 같이 알아 두자.
--------------------------------------
1. "길이가 불규칙한 스트링(문자열)들을
2. 동시에 "여러개" 취급할 때
--------------------------------------
는 무조건 *p[] ( 배열 형태를 이루고 있는 포인터)를 쓴다. 남들에게도 이
렇게 말해보라. 아마 여러분을 대단한 실력가로 여길것이다.
사용법을 직접 보기전에 우리의 "영원한 진리"를 외우자.
--------------------------------------------
변수 xxx 가 2 차원 으로 선언되었으면,
2 차원으로 찍어보라고 할때 "값"을 찍는다.
0 차원이나 1 차원에서는 "주소"를 찍는다.
--------------------------------------------
char *ma_dangbals[3] = {
"lim_chungha", /* 100호 부터 시작(가정) */
"so_daiji", /* 200호 부터 시작(가정) */
"jo_yongpil", /* 300호 부터 시작(가정) */
}
main()
{
(1) printf(" %d", ma_dangbals); --> 마당발[0]의 자기집 주소
(2) printf(" %d", *ma_dangbals); --> 100호
(3) printf(" %d", ma_dangbals[0]); --> 100호 (위와 같음)
(4) printf(" %d", *ma_dangbals[1]); --> 's'
}
보시다시피 이제는 앞뒤로 오락가락 하며 우리를 괴롭힐려고 하는데
우리의 "영원한 진리"만 기억하자. 일단은 다른건 몰라도 값을 찍고 있는
지 주소를 찍고 있는지는 알아야 한다. 이건 간단하다. 현재 마당발이 2 차
원으로 선언되 있기 때문에 0 차원, 1 차원은 무조건 주소다.
복잡하게 보이지만 따지는 법은 이렇다. 위에서 본대로 현재 마당발
들은 "배열" 이다.(우선순위에 의해서) 그러므로 우리가 해석할 때도 일단은
"미스 포인터"를 접어두고 "배열 상태만 따진다". 예를 보자.
위 (1) 은 ma_dangbals를 찍으라는 것이다.이것은
ma_dangbals --> &ma_dangbals[0]
과 같은 표현이다(공식). 이건 이제 말로 해보면 쉽게 알 수 있다.
"마당발[0]의 주소" --> 컴파일러가 할당
(2) 번은 *ma_dangbals 를 찍으라는 것인데, 배열을 먼저 따진다.
*ma_dangbals --> *(ma_dangbals) --> *(&ma_dangbals[0])
이건 말로 해보면 웃긴다."(마당발[0]의 주소의)( 값)" 즉 "마당발[0]"의 주
소, 그 주소에 있는 값이라는건 결국 자기 자신 아닌가 ?
위의 표현은 더 간단하게 다음과 같이 된다.
*(&ma_dangbals[0]) --> *&ma_dangbals[0] --> ma_dangbals[0]
위에서 *& 둘이 만나면 없어진다. 이 둘 중에서도 해석 순서는 무조건
----------------------------------------
변수명(ma_dangbal)에 가까운 것 부터 한다
----------------------------------------
말로 해보자 . 여러분 자신이 지금 100호에 들어가 있다고 치자. 여러분이
있는 "집주소"(&)에 있는 "그 사람(*)" --> 여러분 자신이다.
따라서 이것은 바로 (3)의 답이다.
(4)번 *ma_dangbals[1] 도 역시 배열 부터 따진다.
*ma_dangbals[1] --> * ( ma_dangbals[1])
말로 해보면 "마당발[1]" 이 현재 가리키는 곳의 값(*)이다. 현재 마당발[1]
은 "song_changsik"이라는 스트링의 첫 글자를 가리키고 있다. 그 글자를 찍
는다.
해석 하는 법을 하나씩 따져 보면 역시 어려울것은 없다. I.Q zero
인 컴파일러가 해석을 잘하고 있는데 사람이 못할리가 없다.
--------------<문1>-----------------
main()
{
char *p_str="Hi Good Morning !!!";
int i=0;
float *p_num;
while ( p_str[i] != 0 )
printf(" %c", p_str[i++]);
printf("\n\n");
while ( *p_str != 0 )
printf(" %c", *p_str++);
for (i=0 ; i<5 ; i++)
*(p_num+i) = i*1.1f;
printf("\n\n");
for (i=0 ; i<5 ; i++)
printf("\n %p : %.1f", p_num, *p_num++);
printf("\n\n &i => %p", &i);
printf(" \n p_str => %p", p_str);
printf(" \n sizeof(p_str) => %d", sizeof(p_str));
}
--------------<문2>-----------------
main()
{
int i;
char *pp1="Hi ! everybody";
char *pp2="Turbo C V2.0";
printf("\n *(pp1 += 5) => %c", *(pp1 += 5));
printf("\n *(pp2 -= 3) => %c", *(pp1 -= 3));
printf("\n pp2 - prt1 => %d", pp2 - pp1);
printf("\n pp2 == prt1 => %d", pp2 == pp1);
printf("\n pp2 > prt1 => %d", pp2 > pp1);
pp1 = (void *)4;
printf("\n pp1 => %s", pp1);
}
--------------<문3>-----------------
<참고> void type 의 포인터는 모든 타입의 변수를 다 가리킬 수 있다는 뜻
이다. 따라서 이것이 가리키고 있는 타입을 보다 분명히 하기 위하여 cast
연산자를 사용한다.
main()
{
char *pp1 = "It's fun";
void *pp2 = "Yes It is";
void *temp;
int i;
temp = pp1; pp1 = pp2; pp2 = temp;
printf("\n pp1 => %s", pp1);
printf("\n pp2 => %s\n\n", pp2);
for (i=0 ; i<7 ; i++)
printf(" %c ", ((char *)pp2)[i]);
( (long *)pp2 )++;
printf("\npp2 => %c", *(char *)pp2);
printf("\n pp2 => %#X", *(int *)pp2);
}
--------------<문4>-----------------
main()
{
int a[3][2] = { {9,8}, {7,6}, {5,6} };
int i, j;
printf("\n a => %p\n", a);
for (i=0 ; i<3 ; i++)
{
printf("\n a[%d] => %p", i, a[i]);
for (j=0 ; j<2 ; j++)
printf(" a[%d][%d] : %d", i, j, a[i][j]);
}
}
--------------<문5>-----------------
main()
{
int a[2][3][4] = {
{ { 11,12, 13, 14 }, { 5, 6, 7, 8}, { 9,10,11,12} },
{ { 13,14,15,16}, {17,18,19,20}, {21,22,23,24} }
};
int i, *pp;
pp = a;
for (i=0 ; i<2*3*4 ; i++)
{
if (i % 4 == 0)
printf("\n");
printf(" pp[%2d] : %2d ", i, pp[i] );
}
}
--------------<문6>-----------------
main()
{
int a[3][2] = { {1,2}, {3,4}, {5,6} };
int i, j;
printf("\n a => %p\n", a);
for (i=0 ; i<3 ; i++)
{
printf("\n *a + %d => %p", i, *a+i);
for (j=0 ; j<2 ; j++)
printf(" *(*(a+%d)+%d) : %d", i, j, *(*(a+i)+j) );
}
}
--------------<문7>-----------------
main()
{
int a[]={1,2,3}, i;
for (i=0 ; i<3 ; i++)
printf(" a[%d] => %d", i, a[i]);
printf("\n\n");
for (i=0 ; i<3 ; i++)
printf(" %d[a] => %d", i, i[a]);
}
--------------<문8>----------------
main()
{
double **p;
p = (void *)5;
printf("\n %d", sizeof(p) );
printf("\n %d", sizeof(*p) );
printf("\n %d", sizeof(**p) );
printf("\n\n %p", p);
printf(" \n %p", &*p);
printf("\n\n %p", *p);
printf(" \n %p", &**p);
}
--------------<문9>-----------------
main()
{
int i;
char *str;
int *ppaa[3];
str = (void *)4;
printf("\n str : %s\n\n\n", str);
ppaa[0] = (void *)4;
ppaa[1] = (void *)14;
ppaa[2] = (void *)33;
printf("\n ppaa => %p\n", ppaa);
for (i=0 ; i<3 ; i++)
printf("\n *ppaa[%d] => %4X", i, *ppaa[i]);
}
--------------<문10>-----------------
main()
{
char *ppaa[3]={"lim_chungha","so_daiji","Ma_dangbal"};
int i;
printf("\n ppaa => %p\n", ppaa);
for (i=0 ; i<3 ; i++)
printf("\n ppaa[%d](주소 %d) => %s",i, ppaa[i], ppaa[i]);
}
***************************< 끝 마치며 >******************************
수고하셨습니다
***********************************************************************