제 5 장 포인터(pointer) - kocwcontents.kocw.net/kocw/document/2013/koreasejong/... · 2016. 9....

33
제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를 포인터 변수(pointer variable), 또는 단순히 포인터라 한다. 5.1 주소연산자와 포인터 변수 5.1.1 주소 연산자 (&) &(address of): 주소 연산자-메모리 주소 연산자로 변수 앞에 붙은 &는 변수가 위치한 시작 주소를 의미한다. 다음은 주소연산자 사용이 불가능한 경우이다. register 변수 상수나 산술 식 예) &8; // 상수 &(x+1); // 산술 식 register x; // &x 사용불가 & 연산자 사용법(무차원 배열의 변수) #include <stdio.h> int g; static char gc; void main() { static char sc; char c; int a; double b; /* 변수의 memory 주소 */ printf("&g=%p &gc=%p &sc=%p\n",&g,&gc,&sc); // static 영역 printf("&c=%p &a=%p &b=%p\n\n",&c,&a,&b); // stack 영역 } #include <iostream.h> int g; static char gc; void main() { static char sc; char c='A'; int a; double b; /* 변수의 memory 주소 */ cout<<"&g="<<&g<<'\t'<<"gc="<<gc<<'\t'<<"sc="<<sc<<'\n'; cout<<"&c="<<&c<<'\t'<<"&a="<<&a<<'\t'<<"&b="<<&b<<"\n\n"; } 설명: 정수나 실수와 달리 C++에서 주소표기법을 사용하면 그 속의 문자열이 출력된다. 전역 과 정적 변수는 NULL로 자동 초기화하므로 그 속에 있는 NULL이 출력되어 아무 것도 보여 주지 않는다. 전역과 정적 변수는 static영역에 오름차순으로, 자동변수는 stack영역에 내림차

Upload: others

Post on 11-Mar-2021

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

제 5 장 포인터(Pointer)주소(address)값을 저장하는 변수를 포인터 변수(pointer variable), 또는 단순히 포인터라 한다.

5.1 주소연산자와 포인터 변수

5.1.1 주소 연산자 (&)&(address of): 주소 연산자-메모리 주소 연산자로 변수 앞에 붙은 &는 변수가 위치한 시작 주소를 의미한다. 다음은 주소연산자 사용이 불가능한 경우이다. register 변수 상수나 산술 식 예) &8; // 상수

&(x+1); // 산술 식register x; // &x 사용불가

& 연산자 사용법(무차원 배열의 변수)#include <stdio.h>int g;static char gc;void main() {

static char sc;char c; int a; double b; /* 변수의 memory 주소 */ printf("&g=%p &gc=%p &sc=%p\n",&g,&gc,&sc); // static 영역printf("&c=%p &a=%p &b=%p\n\n",&c,&a,&b); // stack 영역

}

#include <iostream.h>int g; static char gc;void main() {

static char sc;char c='A'; int a; double b; /* 변수의 memory 주소 */ cout<<"&g="<<&g<<'\t'<<"gc="<<gc<<'\t'<<"sc="<<sc<<'\n'; cout<<"&c="<<&c<<'\t'<<"&a="<<&a<<'\t'<<"&b="<<&b<<"\n\n";

}설명: 정수나 실수와 달리 C++에서 주소표기법을 사용하면 그 속의 문자열이 출력된다. 전역과 정적 변수는 NULL로 자동 초기화하므로 그 속에 있는 NULL이 출력되어 아무 것도 보여주지 않는다. 전역과 정적 변수는 static영역에 오름차순으로, 자동변수는 stack영역에 내림차

Page 2: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

순으로 메모리를 할당받고 있다. 5.1.2 간접 연산자 (*)간접 연산자(*)의 뒤 변수는 메모리 주소만을 갖는 특별한 변수이다.

포인터 변수의 선언형식: int *pt;pt는 어떤 주소(address number)를 값으로 갖는 변수이고, (*)는 간접연산자로 point to를 의미하며 pt가 갖고 있는 번지내의 값이나 내용을 뜻한다.*pt 앞 int는 포인터가 가리키고 있는 번지수의 자료형이다.*pt=NULL: pt 포인터 변수가 어떤 변수의 주소 값도 갖고 있지 않고 \0로 초기화 되어 있다.

포인터 변수 pt를 변수 x의 주소상수로 초기화하는 방법:int *pt=&x; 또는 int *pt, x; pt=&x; void *pt로 선언하면 pt에 모든 자료형 변수의 주소를 저장할 수 있다. 단 특수 연산은 불가능.

5.1.3 포인터 및 문자열 출력에 사용하는 주소 식포인터를 공부하려면 변수, 배열, 함수 및 구조체의 시작주소를 아는 것이 중요하다. 앞에서 배운 것처럼 다음에 사용하는 두 식은 배열이 시작하는 주소를 의미하며 포인터 변수에 대입하거나 넘겨 줄 수 있다. 또한 이 들은 C/C++에서 문자열 출력에 사용하는 식이기도 하다.

차원 변수 주소 식1 주소 식2 무차원 배열 int k; (출력식: k) &k 1차원 배열 int a[5]; a &a[0] 2차원 배열 int b[2][3] b[0] &b[0][0] 3차원 배열 int c[2][3][4] c[0][0] &c[0][0][0]

4차원 배열 int d[2][3][4][5] d[0][0][0] &d[0][0][0][0]

5.2 정수/실수 배열과 포인터

5.2.1 무차원 배열과 포인터

예제) 포인터 변수#include <stdio.h>void main() {

int m=10;int *pt; // 포인터

Page 3: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

pt=&m; // 포인터 pt에 m의 주소대입printf("&pt=%p pt=%p &m=%p\n",&pt,pt,&m);printf("m=%d *pt=%d\n",m, *pt); // m=10, *pt=10*pt=*pt+10;printf("m=%d *pt=%d\n",m, *pt); // m=20, *pt=20

}

#include <iostream.h>void main() {

int m=10;int *pt; // 포인터pt=&m; // 포인터 pt에 m의 주소대입cout<<"&pt="<<&pt<<" "<<"pt="<<pt<<" "<<"&m="<<&m<<'\n';cout<<"m="<<m<<" "<<"*pt="<<*pt<<'\n'; // m=10, *pt=10*pt=*pt+10;cout<<"m="<<m<<" "<<"*pt="<<*pt<<'\n'; // m=20, *pt=20

}설명: &pt는 포인터 변수의 주소이고 그 속에 pt 즉 &m의 주소 값이 들어있다. *pt는 &m번지에 있는 내용이므로 바로 m값이다. *pt=*pt+10은 pt가 가리키고 있는 곳 즉 &m방의 값에 10을 더하여 다시 그 방에 넣으라는 의미이므로 m값은 20이 된다.

5.2.2 다차원 배열의 포인터

예제) 다차원 배열의 포인터#include <stdio.h>void main() {

int i;int a[5]={1,2,3,4,5};int b[2][3]={{11,12,13}, {21,22,23}};int c[2][3][4]={{{111,112,113,114}, {121,122,123,124},{131,132,133,134}},

{{211,212,213,214}, {221,222,223,224}, {231,232,233,234}}}; int *pta,*ptb,*ptc;

pta=a; // or pta=&a[0]ptb=b[0]; // or ptb=&b[0][0] 이외의 방법 사용 불가 ptc=c[0][0]; // or ptc=&c[0][0][0] 이외의 방법 사용 불가printf("&pta=%p &ptb=%p &ptc=%p\n",&pta,&ptb,&ptc);printf(" pta=%p ptb=%p ptc=%p\n",pta,ptb,ptc);printf(" a=%p b[0]=%p c[0][0]=%p\n\n", a,b[0],c[0][0]);printf(" *pta=%d *ptb=%d *ptc=%d\n",*pta,*ptb,*ptc);printf("a[0]=%d b[0][0]=%d c[0][0][0]=%d\n\n",

Page 4: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

a[0],b[0][0],c[0][0][0]);for(i=0;i<6;i++) {

printf("*(ptb+%d)=%d ",i,*(ptb+i));printf(" ptb+%d=%p\n",i,ptb+i);

}putchar('\n');printf(" *(ptc+16)=%d ptc+16=%p\n", *(ptc+16), ptc+16);printf(" c[1][1][0]=%d &c[1][1][0]=%p\n\n",c[1][1][0],&c[1][1][0]);

}

#include <iostream.h>void main() {

int i;int a[5]={1,2,3,4,5};int b[2][3]={{11,12,13}, {21,22,23}};int c[2][3][4]={{{111,112,113,114}, {121,122,123,124},{131,132,133,134}},

{{211,212,213,214}, {221,222,223,224}, {231,232,233,234}}}; int *pta,*ptb,*ptc;

pta=a; // or pta=&a[0]ptb=b[0]; // or ptb=&b[0][0] 이외의 방법 사용 불가 ptc=c[0][0]; // or ptc=&c[0][0][0] 이외의 방법 사용 불가cout<<"&pta="<<&ptb<<" "<<"&ptb="<<&ptb<<" "<<"&ptb="<<&ptb<<'\n';cout<<" pta="<<pta<<" "<<"ptb="<<ptb<<" "<<"ptc="<<ptc<<'\n';cout<<" a="<<a<<" "<<"b[0]="<<b[0]<<" "<<"c[0][0]="<<c[0][0]<<"\n\n";cout<<"*pta="<<*pta<<" "<<"*ptb="<<*ptb<<" "<<"*ptc="<<*ptc<<'\n';cout<<"a[0]="<<a[0]<<" "<<"b[0][0]="<<b[0][0]<<" "

<<"c[0][0][0]="<<c[0][0][0]<<"\n\n"; /* pointer는 모든 배열을 일차원 배열로 취급한다. */for(i=0;i<6;i++) {

cout<<"*(ptb+"<<i<<")="<<*(ptb+i)<<" ";cout<<"ptb+"<<i<<"="<<ptb+i<<'\n';}

cout<<'\n';cout<<" *(ptc+16)="<<*(ptc+16)<<" "<<"ptc+16="<<ptc+16<<'\n';cout<<"c[1][1][0]="<<c[1][1][0]<<" "<<"&c[1][1][0]="

<<&c[1][1][0]<<"\n\n"; }설명: 일반적으로 배열명은 그 배열의 시작번지이고 포인터 상수인 반면, 포인터는 주소를 담는 1byte 크기의 변수이다. 배열의 시작주소를 포인터 변수에 대입한 식은 1차원 배열: pta=a, 2차원배열: ptb=b[0], 3차원 배열: ptc=c[0][0] 이다. 포인터는 모든 배열을 순차적으로 나열된 1차원 배열로 취급한다. 2차원 b배열 포인터의 ptb, ptb+1, ptb+2는 각 각

Page 5: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

&b[0][0], &b[0][1], &b[0][2]의 주소를 의미하고 여기에 간접연산자를 붙인 *ptb, *(ptb+1), *(ptb+2)들은 b[0][1], b[0][1], b[0][2]의 원소 값인 11, 12, 13을 뜻한다. 따라서 ptb+i로 i가 1씩 증가하는 주소 값을 조사해 보면 int b 배열의 경우 한 원소 당 4bytes 씩 증가하고 있음을 알 수 있다. 5.2.3 배열의 포인터 연산

예제) 포인터 연산#include <stdio.h>void main() {

int i,j;int b[2][3]={{11,12,13}, {21,22,23}};

int *ptb;ptb=b[0];for(i=0;i<6;i++) {

*(ptb+i) -=10; // 각 원소에서 10을 뺌printf("*(ptb+%d)=%d\n",i,*(ptb+i));

}putchar('\n');for(i=0;i<2;i++) {

for(j=0;j<3;j++)printf("b[%d][%d]=%d\n",i,j,b[i][j]);

}putchar('\n');*ptb=*(ptb+1)+10; //*ptb=*(ptb+1)+10 = 2+10=12printf("*ptb=%d b[0][0]=%d\n\n",*ptb, b[0][0]);ptb++; //수행후 다음 원소의 주소로 증가. 현재결과 b[0][1]++ptb; //수행전 다음 원소의 주소로 증가, 현재결과 b[0][2]printf(" ptb=%p \t &b[0][2]=%p\n",ptb, &b[0][2]);printf("*ptb=%d \t\t b[0][2]=%d\n\n",*ptb,b[0][2]); //*ptb=3, b[0][2]=3

}

#include <iostream.h>void main() {

int i,j;int b[2][3]={{11,12,13}, {21,22,23}};

int *ptb;ptb=b[0];for(i=0;i<6;i++) {

*(ptb+i) -=10; // 각 원소에서 10을 뺌cout<<"*(ptb+"<<i<<")="<<*(ptb+i)<<'\n';

Page 6: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

}cout<<'\n';for(i=0;i<2;i++) {

for(j=0;j<3;j++)cout<<"b["<<i<<"]["<<j<<"]="<<b[i][j]<<'\n';

}cout<<'\n';*ptb=*(ptb+1)+10; //*ptb=*(ptb+1)+10 = 2+10 =12cout<<"*ptb="<<*ptb<<" "<<"b[0][0]="<<b[0][0]<<"\n\n";ptb++; //수행후 다음 원소의 주소로 증가. 현재결과 b[0][1]++ptb; //수행전 다음 원소의 주소로 증가. 현재결과 b[0][2]cout<<"ptb="<<ptb<<'\t'<<"&b[0][2]="<<&b[0][2]<<'\n';cout<<"*ptb="<<*ptb<<"\t\t"<<"b[0][2]="<<b[0][2]<<"\n\n";

}설명: 첫 번째 for 루프에서는 포인터를 이용하여 각 원소에서 10을 뺏다. 그리고 두 번째 for 루프에서 각 원소를 출력해 보면 처음원소보다 각 각 10이 작음을 알 수 있다. *ptb=*(ptb+1)+10은 두 번째 배열의 원소에 10을 더하여 첫 번째 배열의 원소에 대입하라는 의미로 곧 b[0][0] =b[0][1]+10=2+10=12 라는 의미이다. 포인터변수에 증가연산자를 사용하면 다음 배열의 주소를 갖게 되므로 ptb로부터 연속적인 증가연산자의 적용(ptb++; ++ptb;)은 수행 후 ptb가 세 번째 원소의 배열 즉 &b[0][2]의 주소를 갖고 있음을 의미한다. 따라서 마지막 line은 *ptb= b[0][2]=3이다.

5.2.4 함수의 호출함수의 가인수는 포인터이고 넘겨주는 실인수는 변수의 주소이어야 한다.

예제) 포인터에 배열주소 넘겨주기#include <stdio.h>void array(int *pta, int *ptb, int *ptc) {

int i; /* 배열의 각 원소별 출력 */ for(i=0; i<8; i++){

printf("*(pta+%d)=%d\t",i,*(pta+i));printf("*(ptb+%d)=%d\t",i,*(ptb+i));printf("*(ptc+%d)=%d\n",i,*(ptc+i));

}putchar('\n');for(i=0; i<8; i++){

printf("pta[%d]=%d \t",i,pta[i]);printf("ptb[%d]=%d\t",i,ptb[i]);printf("ptc[%d]=%d\n",i,ptc[i]);

Page 7: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

} putchar('\n');}void main() {

int a[8]={1,2,3,4,5,6,7,8};int b[2][4]={{11,12,13,14}, {21,22,23,24}};int c[2][2][2]={{{111,112}, {121,122}}, {{211,212}, {221,222}}};array(a,b[0],c[0][0]); // or array(&a[0], &b[0][0], &c[0][0][0])

}

#include <iostream.h>void array(int *pta, int *ptb, int *ptc) {

int i; /* 배열의 각 원소별 출력 */ for(i=0; i<8; i++){

cout<<"*(pta+"<<i<<")="<<*(pta+i)<<"\t";cout<<"*(ptb+"<<i<<")="<<*(ptb+i)<<"\t"; cout<<"*(ptc+"<<i<<")="<<*(ptc+i)<<"\n";

}cout<<'\n';for(i=0; i<8; i++){

cout<<"pta["<<i<<"]="<<pta[i]<<"\t";cout<<"ptb["<<i<<"]="<<ptb[i]<<" \t"; cout<<"ptc["<<i<<"]="<<ptc[i]<<"\n";

} cout<<"\n";}void main() {

int a[8]={1,2,3,4,5,6,7,8};int b[2][4]={{11,12,13,14}, {21,22,23,24}};int c[2][2][2]={{{111,112}, {121,122}}, {{211,212}, {221,222}}};array(a,b[0],c[0][0]); // or array(&a[0], &b[0][0], &c[0][0][0])

}설명: 주소로 넘겨받은 배열을 함수에서 출력해본 프로그램이다. 첫 주소를 넘겨받은 포인터들은 마치 배열을 포인터들의 1차원 배열로 생각하여 출력한다. 여기서 포인터에 넘겨주는 주소 식에 특별히 주목하라. 단순히 배열을 넘겨주는 것이라면 배열명만 사용했음을 기억할 것이다.

예제) 포인터로 방에 계산결과 저장#include <stdio.h> void fn(int x, int y, int *z) {

Page 8: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

*z=x-y; // *z=1-2}void main() {

int a=1, b=2, c;fn(a,b,&c);printf("c=%d\n",c); // c=-1

}

#include <iostream.h> void m(int x, int y, int *z) {

*z=x-y; // *z=1-2}void main() {

int a=1, b=2, c;m(a,b,&c);cout<<"c="<<c<<'\n'; // c=-1

}설명: *z에 c의 주소(&c)를 넘겨주고 fn()에서 연산한 결과를 c방에 저장.

5.3 문자 배열과 포인터함수인자 전달은 정수/실수에서와 마찬가지로 동일하다. 그러나 주소를 알기위하여 포인터에 사용된 주소 식을 출력에 사용한다면 앞에서 배운바와 같이 C++는 주소가 아닌 그 주소로부터 NULL을 만날 때까지의 문자열을 출력한다. 이와 달리 C에서는 %p format으로 주소출력을, 동시에 %s format으로 문자열을 출력한다.

5.3.1 무차원 배열과 포인터

예제) 포인터 변수#include <stdio.h>void main() {

char ch='A', *pt=&ch; printf("&pt=%p pt=%p &ch=%p\n\n",&pt,pt,&ch); printf("ch=%c *pt=%c\n",ch, *pt);*pt=*pt+32; //포인터 연산. 대문자를 소문자로 바꿈printf("ch=%c *pt=%c\n",ch, *pt);

}

#include <iostream.h>

Page 9: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

void main() {char ch='A', *pt=&ch; cout<<"&pt="<<&pt<<" "<<"pt="<<pt<<" "<<"&ch="<<&ch<<'\n';cout<<"ch="<<ch<<" "<<"*pt="<<*pt<<endl;*pt=ch+32; //포인터 연산. 대문자를 소문자로 바꿈cout<<"ch="<<ch<<" "<<"*pt="<<*pt<<endl;

}&pt는 포인터 주소이고 그 속에 pt값 즉 &ch의 주소 값이 들어있다. C++에서 주소표기법으로 기술된 pt와 &ch는 C와 달리 주소를 보여주는 것이 아니라 문자를 출력하고 그것도 정상적인 문자를 출력하는 것은 아니다. 이유: 단 문자 일 경우 문자열이 될 수 있는 NULL이 없기 때문임.

5.3.2 다차원 배열의 포인터

예제) 다차원 배열의 포인터#include <stdio.h>void main() {

int i;char a[8]={'a','b','c','d','e','f','g','\0'}; char b[2][4]={{'A','B','C','D'},{'E','F','G','\0'}};char c[2][2][2]={{{'h','i'},{'j','k'}},{{'m','n'},{'o',NULL}}};

char *pta,*ptb,*ptc;pta=a; // or pta=&a[0]ptb=b[0]; // or ptb=&b[0][0] 이외의 방법 사용 불가 ptc=c[0][0]; // or ptc=&c[0][0][0] 이외의 방법 사용 불가printf("&pta=%p &ptb=%p &ptc=%p\n",&pta,&ptb,&ptc);printf("pta=%p ptb=%p ptc=%p\n",pta,ptb,ptc);printf(" a=%p b[0]=%p c[0][0]=%p\n\n", a,b[0],c[0][0]);printf(" *pta=%c *ptb=%c *ptc=%c\n",*pta,*ptb,*ptc);printf(" a[0]=%c b[0][0]=%c c[0][0][0]=%c\n\n",

a[0],b[0][0],c[0][0][0]);/* pointer는 모든 배열을 일차원 배열로 취급한다. */for(i=0;i<8;i++) {

printf("*(ptb+%d)=%c ",i,*(ptb+i));printf("ptb+%d=%p ",i,ptb+i);printf("ptb+%d=%s\n",i,ptb+i);

}putchar('\n');printf(" *(ptc+4)=%c ptc+4=%p ptc+4=%s\n",

*(ptc+4), ptc+4,ptc+4);

Page 10: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

printf("c[1][0][0]=%c &c[1][0][0]=%p &c[1][0][0]=%s\n\n",c[1][0][0],&c[1][0][0],&c[1][0][0] );

}

#include <iostream.h>void main() {

int i;char a[8]={'a','b','c','d','e','f','g','\0'}; char b[2][4]={{'A','B','C','D'},{'E','F','G','\0'}};char c[2][2][2]={{{'h','i'}, {'j','k'}}, {{'m','n'}, {'o',NULL}}}; char *pta,*ptb,*ptc;pta=a; // or pta=&a[0]ptb=b[0]; // or ptb=&b[0][0] 이외의 방법 사용 불가 ptc=c[0][0]; // or ptc=&c[0][0][0] 이외의 방법 사용 불가cout<<"&pta="<<&pta<<" "<<"&ptb="<<&ptb<<" "<<"&ptc="<<&ptc<<"\n\n";cout<<"pta="<<pta<<" "<<"ptb="<<ptb<<" "<<"ptc="<<ptc<<'\n';cout<<" a="<<a<<" "<<"b[0]="<<b[0]<<" "<<"c[0][0]="<<c[0][0]<<"\n\n";cout<<"*pta="<<*pta<<" "<<"*ptb="<<*ptb<<" "<<"*ptc="<<*ptc<<'\n';cout<<"a[0]="<<a[0]<<" "<<"b[0][0]="<<b[0][0]<<" "

<<"c[0][0][0]="<<c[0][0][0]<<"\n\n";for(i=0;i<8;i++) {

cout<<"*(ptb+"<<i<<")="<<*(ptb+i)<<" ";cout<<"ptb+"<<i<<"="<<ptb+i<<'\n';

}cout<<'\n';cout<<"*(ptc+4)="<<*(ptc+4)<<" "<<"ptc+4="<<ptc+4<<'\n';cout<<"c[1][0][0]="<<c[1][0][0]<<" "<<"&c[1][0][0]="<<&c[1][0][0]<<'\n';

}설명: 일반적으로 배열명은 그 배열의 시작번지이고 포인터 상수인 반면, 포인터는 주소를 담는 1byte 크기의 변수이다. 배열의 시작주소를 포인터 변수에 대입식은 정수/실수 배열과 마찬가지로 1차원 배열: pta=a, 2차원배열: ptb=b[0], 3차원 배열: ptc=c[0][0]이다. 포인터는 모든 배열을 순차적으로 나열된 1차원 배열로 취급한다. 2차원 배열의 경우 포인터 ptb, ptb+1, ptb+2를 C의 %p format로 출력해 보면 이들은 각 각 &b[0][0], &b[0][1], &b[0][2]의 주소를 나타낸다. 여기에 간접연산자를 붙인 *ptb, *(ptb+1), *(ptb+2)들은 b[0][1], b[0][1], b[0][2]의 원소 값인 A, B, C를 뜻한다. 따라서 ptb+i로 i가 1씩 증가하는 주소 값을 조사해 보면 char로 정의된 b 배열은 한 원소 당 1byte씩 증가하고 있다. 동시에 C의 포인터 식들에 %s format을 사용하면 문자열을 출력하고, C++에서는 C의 %p format과 같은 것이 없기 때문에 오직 문자열만 출력한다.

예제) 번지수를 지정하여 문자열 출력#include <stdio.h>

Page 11: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

void main() {char c[2][2][3]={{{'a','b','c'}, {'d','e',NULL}},

{{'m','n','o'},{'p','q','\0'}}}; char *ptc;ptc=c[0][0];printf("ptc=%s \tc[0][0]=%s \t\t&c[0][0][0]=%s\n",

ptc,c[0][0],&c[0][0][0]);printf("ptc=%p \tc[0][0]=%p \t&c[0][0][0]=%p\n\n",

ptc,c[0][0],&c[0][0][0]); // C++에서는 없음printf("ptc+6=%s \tc[0][0]+6=%s \t&c[0][0][0]+6=%s\n",

ptc+6,c[0][0]+6,&c[1][0][0]);printf("ptc+6=%p \tc[0][0]+6=%p \t&c[1][0][0]=%p\n",

ptc,c[0][0],&c[0][0][0]); // C++에서는 없음putchar('\n');

}

#include <iostream.h>void main() {

char c[2][2][3]={{{'a','b','c'}, {'d','e',NULL}}, {{'m','n','o'},{'p','q','\0'}}}; char *ptc;ptc=c[0][0];cout<<"ptc="<<ptc<<'\t'<<"c[0][0]="<<c[0][0]<<'\t'

<<"&c[0][0][0]="<<&c[0][0][0]<<"\n";cout<<"ptc+6="<<ptc+6<<'\t'<<"c[0][0]+6="<<c[0][0]+6<<'\t'

<<"&c[1][0][0]="<<&c[1][0][0]<<"\n\n";}설명: C++에서 포인터의 주소 값을 알아내려고 하는 표기법은 C의 %s format과 동일하게 되어 그 주소로부터 NULL까지의 문자열이 출력된다.

5.3.3 배열의 포인터 연산

예제) 포인터 연산#include <stdio.h>void main() {

int i,j;char b[2][3]={{'a','b','c'}, {'d','e','\0'}};

char *ptb;ptb=b[0];for(i=0;i<5;i++) {

*(ptb+i) -=32; // 각 원소에서 32를 뺌. 대문자로 전환식

Page 12: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

printf("*(ptb+%d)=%c\n",i,*(ptb+i));}putchar('\n');for(i=0;i<2;i++) {

for(j=0;j<3;j++)printf("b[%d][%d]=%c\n",i,j,b[i][j]);

}putchar('\n');*ptb=*(ptb+1)+1;// *ptb=*(ptb+1)+1='B'+1='C'printf("*ptb=%c b[0][0]=%c\n\n",*ptb, b[0][0]);ptb++; //수행후 다음 원소의 주소 현재결과 b[0][1]++ptb; //수행전 다음 원소의 주소, 현재결과 b[0][2]printf("ptb=%p \t &b[0][2]=%p\n",ptb,&b[0][2]);printf("*ptb=%c \t\t b[0][2]=%c\n\n",*ptb,b[0][2]); // C

}

#include <iostream.h>void main() {

int i,j;char b[2][3]={{'a','b','c'}, {'d','e','\0'}};

char *ptb;ptb=b[0];for(i=0;i<5;i++) {

*(ptb+i) -=32; // 각 원소에서 32를 뺌cout<<"*(ptb+"<<i<<")="<<*(ptb+i)<<'\n';

}cout<<'\n';for(i=0;i<2;i++) {

for(j=0;j<3;j++)cout<<"b["<<i<<"]["<<j<<"]="<<b[i][j]<<'\n';

}cout<<'\n';*ptb=*(ptb+1)+1;//*ptb=*(ptb+1)+1='B'+1='C'cout<<"*ptb="<<*ptb<<" "<<"b[0][0]="<<b[0][0]<<'\n'; // Cptb++; //수행후 다음 원소의 주소 현재결과 b[0][1]++ptb; //수행전 다음 원소의 주소, 현재결과 b[0][2]cout<<"*ptb="<<*ptb<<" "<<"b[0][2]="<<b[0][2]<<"\n\n"; // C

}설명: 첫 번째 for 루프에서는 포인터를 이용하여 각 원소에서 32를 뺏다. 그리고 두 번째 for 루프에서 각 원소를 출력해 보면 대문자로 바뀌었음을 알 수 있다. *ptb=*(ptb+1)+1은 두 번째 배열의 원소에 1을 더하여 첫 번째 배열의 원소에 대입하라는 의미로 곧

Page 13: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

b[0][0]=b[0][1]+1= 'B'+1="C' 라는 의미이다. 포인터변수에 증가연산자를 사용하면 다음 배열의 주소를 갖게되므로 ptb로부터 연속적인 증가연산자의 적용(ptb++; ++ptb;)은 수행 후 ptb가 세 번째 원소의 배열 즉 &b[0][2]의 주소를 갖고 있음을 의미한다. 따라서 마지막 line은 *ptb= b[0][2]='C'이다.

5.3.4 함수의 호출

예제) 포인터 인수#include <stdio.h>void ch_array(char *pta, char *ptb, char *ptc) {

int i; /* 배열의 각 원소별 출력 */ for(i=0; i<8; i++){

printf("*(pta+%d)=%c\t",i,*(pta+i));printf("*(ptb+%d)=%c\t",i,*(ptb+i));printf("*(ptc+%d)=%c\n",i,*(ptc+i));

}putchar('\n');for(i=0; i<8; i++){

printf("pta[%d]=%c \t",i,pta[i]);printf("ptb[%d]=%c\t",i,ptb[i]);printf("ptc[%d]=%c\n",i,ptc[i]);

} putchar('\n');}void main() {

char a[8]={'a','b','c','d','e','f','g','\0'}; char b[2][4]={{'A','B','C','D'},{'E','F','G','\0'}};char c[2][2][2]={{{'h','i'}, {'j','k'}}, {{'m','n'}, {'o',NULL}}}; ch_array(a, b[0], c[0][0]);

}

#include <iostream.h>void ch_array(char *pta, char *ptb, char *ptc) {

int i; /* 배열의 각 원소별 출력 */ for(i=0; i<8; i++){

cout<<"*(pta+"<<i<<")="<<*(pta+i)<<"\t";cout<<"*(ptb+"<<i<<")="<<*(ptb+i)<<"\t"; cout<<"*(ptc+"<<i<<")="<<*(ptc+i)<<"\n";

Page 14: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

}cout<<'\n';for(i=0; i<8; i++){

cout<<"pta["<<i<<"]="<<pta[i]<<" \t";cout<<"ptb["<<i<<"]="<<ptb[i]<<"\t"; cout<<"ptc["<<i<<"]="<<ptc[i]<<"\n";

} cout<<"\n";}void main() {

char a[8]={'a','b','c','d','e','f','g','\0'}; char b[2][4]={{'A','B','C','D'},{'E','F','G','\0'}};char c[2][2][2]={{{'h','i'}, {'j','k'}}, {{'m','n'}, {'o',NULL}}}; ch_array(a, b[0], c[0][0]);

}주소로 넘겨받은 배열을 함수에서 출력해본 프로그램으로 방법은 정수/실수에서와 동일하다. 첫 주소를 넘겨받은 포인터들은 마치 배열을 포인터들의 1차원 배열로 생각하여 출력한다. 여기서 포인터에 넘겨주는 주소 식에 특별히 주목하라. 이와 달리 단순히 배열을 넘겨주는 것이라면 배열명만 사용했음을 기억할 것이다.

5.3.5 문자열 포인터

1) 초기화 및 출력

예제) 초기화 및 출력#include <stdio.h>void main() {

char *pts="daughter"; //다른 초기화법: char *pts; pts="daughter";char a[]="mother";printf("&pts=%p\n",&pts); // 포인터 주소

printf(" pts=%p &pts[0]=%p\n",pts,&pts[0]); // 변수값(daughter가 시작되는 주소) printf(" pts=%s &pts[0]=%s\n\n",pts,&pts[0]);// 문자열 출력: daughter

printf("a=%p &a[0]=%p\n",a,&a[0]);printf("a=%s &a[0]=%s\n\n",a,&a[0]);pts=a; // pts에 a의 배열 첫 주소 대

입 printf("pts=%p &pts[0]=%p\n",pts,&pts[0]); // pts는 mother가 시작되는 주소

Page 15: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

printf("pts=%s &pts[0]=%s\n\n",pts,&pts[0]); // mother 출력}

#include <iostream.h>void main() {

char *pts="daughter"; //다른 초기화법: char *pts; pts="daughter";char a[]="mother";cout<<"&pts="<<&pts<<'\n'; // 포인터 주소cout<<"pts="<<pts<<"\t"<<"&pts[0]="<<&pts[0]<<"\n";// 문자열 출력: daughter

cout<<"a="<<a<<"\t"<<"&a[0]="<<&a[0]<<"\n"; // 문자열 출력: motherpts=a;cout<<"pts="<<pts<<"\t"<<"&pts[0]="<<&pts[0]<<"\n\n";// 문자열 출력: mother

}설명: 문자열 포인터 pts의 주소 값은 문자열이 시작되는 daughter의 'd' 주소이고 &pts에 저장된다. 또한 pts는 일차원 문자배열과 동등하여 &pts[0]도 같다. 또 다른 초기화 방법:char *pts;pts="mother";로도 초기화 할수 있으며 그 의미는 치환문의 syntax에도 불구하고 문자열이 pts에 복사되는 것이 아니라 다만 문자열의 첫 주소('m'의 주소)가 복사된다.

예제) C와 C++의 출력식 비교#include <stdio.h>void main() {

char *pts;pts="Pointer Test";printf(" &pts=%p\n",&pts); // 변수의 주소printf(" pts=%p \t\t &pts[0]=%p\n",pts,&pts[0]); //첫 원소주소.printf(" pts=%s \t &pts[0]=%s\n",pts,&pts[0]); printf(" *pts=%c \t\t pts[0]=%c\n\n",*pts, pts[0]);printf("pts+1=%p \t\t &pts[1]=%p\n",pts+1, &pts[1]); //두 번째 원소 주소 printf("pts+1=%s \t &pts[1]=%s\n",pts+1, &pts[1]); printf("*(pts+1)=%c \t\t pts[1]=%c\n\n",*(pts+1),pts[1]);

}

#include <iostream.h>void main() {

char *pts;pts="Pointer Test";cout<<" &pts="<<&pts<<'\n'; // 변수의 주소

Page 16: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

cout<<" pts="<<pts<<'\t'<<"&pts[0]="<<&pts[0]<<'\n';cout<<" *pts="<<*pts<<" \t\t "<<"pts[0]="<<pts[0]<<"\n\n";cout<<"pts+1="<<pts+1<<'\t'<<"&pts[1]="<<&pts[1]<<'\n';cout<<"*(pts+1)="<<*(pts+1)<<" \t\t "<<"pts[1]="<<pts[1]<<"\n\n";

}설명: C에서 %p format으로 포인터 값을 출력하고 %s로 문자열을 출력하였다. 그러나 C++에서는 전자의 format이 없고 오직 후자인 문자열만 출력한다.

2) 문자열 포인터의 함수호출

예제) 함수호출#include <stdio.h>void pt_fn(char *p, char *q) {

printf(" p=%p \t q=%p\n\n",p,q);printf("*p=%c\t\t*q=%c\n",*p,*q);printf(" p=%s \t q=%s\n",p,q);

}void main() {

char *ps="Language";char *qs;qs="Pointer";printf("ps+3=%p \t qs+3=%p\n",ps+3, qs+3);pt_fn(ps+3,qs+3); // pt_fn(&ps[3], &qs[3]);

}

#include <iostream.h>void pt_fn(char *p, char *q) {

cout<<"*p="<<*p<<"\t\t"<<"*q="<<*q<<'\n';cout<<" p="<<p<<" \t "<<"q="<<q<<'\n';

}void main() {

char *ps="Language";char *qs;qs="Pointer";pt_fn(ps+3,qs+3); // pt_fn(&ps[3], &qs[3]);

}

설명: 함수 pt_fn()의 가인수 *p, *q에 포인터변수 ps+3, qs+3을 넘겨주고 있다. ps+3은 Language중 ‘g'의 주소이고, qs+3은 Pointer중 ’n'의 주소이다. 이들을 넘겨받은 pt_fn()에서의 문자 출력은 *p=g, *q=n 이고 문자열 출력은 p=guage, q=nter이다.

Page 17: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

예제) string length 구하기#include <stdio.h>int lenth(char *ps) {

int n=0;do

n++;while(*ps++);return (n);

}void main() {

char *ch="This";int i;i=lenth(ch);printf("%d\n", i); // 5

}

#include <iostream.h>int length(char *ps) {

int n=0;do

n++;while(*ps++);return (n);

}void main() {

char *ch="This";int i;i=length(ch);cout<<i<<'\n'; // 5

}

5.4 포인터 배열포인터 변수가 배열로 표현되는 것으로 주로 문자열 포인터에 사용하며 가리키고자하는 곳의 첫 주소들을 배열로 갖고 있다.

일반 배열의 예 char a[2][4]={"Jan","Feb"}; a[0] = |J|a|n|∖0|

a[1] = |F|e|b|∖0|포인터 배열의 예 char *pa[2]={"Jan","Feb"}; pa[0] ⃞→ |J|a|n|∖0|

Page 18: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

pa[1] ⃞→ |F|e|b|∖0|이 경우 포인터 배열 *pa[2]는 각 원소에 Jan과 Feb가 시작하는 첫 주소를 갖고 있다.

5.4.1 포인터 배열의 초기화 및 출력

예제) 포인터 배열#include <stdio.h>void main() {

int i, j;char *ptar[2]={ "SUN","TUES"};printf("&ptar[0]=%p &ptar[1]=%p\n",&ptar[0],&ptar[1]);printf(" ptar[0]=%p ptar[1]=%p\n",ptar[0],ptar[1]);printf(" ptar=%p ptar+1=%p\n\n",ptar,ptar+1);// 배열명은 주소 상수for(i=0;i<2;i++)

for(j=0;j<4;j++) {printf("*(ptar+%d)+%d=%p ",i,j,*(ptar+i)+j); printf("ptar[%d]+%d=%p ",i,j,ptar[i]+j);printf("*(ptar+%d)+%d=%s\t",i,j,*(ptar+i)+j);printf("ptar[%d]+%d=%s\n",i,j,ptar[i]+j);

}printf("\n");/* character 출력 */for(i=0;i<2;i++)

for(j=0;j<4;j++) {printf("*(*(ptar+%d)+%d)=%c\t",i,j,*(*(ptar+i)+j));printf("*(ptar[%d]+%d)=%c\n",i,j,*(ptar[i]+j));

}}

#include <iostream.h>void main() {

int i, j;char *ptar[2]={ "SUN","TUES"};cout<<"&ptar[0]="<<&ptar[0]<<" "<<"&ptar[1]="<<&ptar[1]<<'\n';cout<<" ptar[0]="<<&ptar[0]<<" "<<"&ptar[1]="<<&ptar[1]<<'\n';cout<<" ptar="<<ptar<<" "<<" ptar+1="<<ptar+1<<"\n\n"; for(i=0;i<2;i++)

for(j=0;j<4;j++) {cout<<"*(ptar+"<<i<<"+"<<j<<")="<<*(ptar+i)+j<<"\t\t"; cout<<"ptar["<<i<<"]+"<<j<<"="<<ptar[i]+j<<"\n";

Page 19: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

}cout<<"\n";/* character 출력 */for(i=0;i<2;i++)

for(j=0;j<4;j++) {cout<<"*(*(ptar+"<<i<<"+"<<j<<"))="<<*(*(ptar+i)+j)<<" ";cout<<"*(ptar["<<i<<"]+"<<j<<")="<<*(ptar[i]+j)<<"\n";

}}설명: &ptar[0]와 &ptar[1]의 주소에 각 각 "SUN"과 “TUES"가 시작되는 주소를 변수로 갖고 있다. 따라서 ptar[0]하면 SUN을 출력하고 ptar[1]하면 TUES를 출력한다. 조금 복잡하기는 하지만 나머지 표기법은 원소들과 각 글자의 주소를 알아보는 것들이다. 5.4.2 포인터 배열의 함수 호출일반 배열의 호출법과 같다.

예제) 포인터 배열의 호출#include <stdio.h>void pt_arfn(char *par[]) {

printf("par[0]=%s \t par[1]=%s\n",par[0],par[1]);printf("*(par[0])=%c\t*(par[1])=%c\n\n",*(par[0]),*(par[1]));

}void main() {

char *ptar[4]={"SUN","TUESDAY","FRIDAY","THURS"};pt_arfn(ptar);pt_arfn(ptar+1);pt_arfn(ptar+2);

}

#include <iostream.h>void pt_arfn(char *par[]) {

cout<<"par[0]="<<par[0]<<"\t"<<"par[1]="<<par[1]<<'\n';cout<<"*(par[0])="<<*(par[0])<<'\t'<<"*(par[1])="<<*(par[1])<<"\n\n";

}void main() {

char *ptar[4]={ "SUN","TUESDAY","FRIDAY", "THURS"};pt_arfn(ptar);pt_arfn(ptar+1);pt_arfn(ptar+2);

}설명: 첫 호출 pt_arfn(ptar); 의 ptar은 SUN이 시작되는 주소이고, 두 번째 호출 pt_arfn

Page 20: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

(ptar+1);의 ptar+1은 TUESDAY의 시작주소를 각각 *par에 넘겨주고 있다.

예제) string length 구하기#include <stdio.h>char len(char *s[], int j) {

int k=0;while(s[j][k])

k++;return k;

}void main() {

char *str[]={"corbol","fortran","pascal"};int i;for(i=0;i<3;i++)

printf("%s=%d\n",str[i],len(str,i));}

5.5 함수의 인수전달 법

5.5.1 값에 의한 호출(call by value) 실인수(real argument) 값을 가인수(dummy parameters)에 전달.값을 주면서 호출하기(Call by value)에서는 전달 값이 복사되므로 원래의 값이 변경되지 않는다.

예제) 값에 의한 호출#include <stdio.h>void fn(int b) {

b=b+1;printf("&b=%p b=%d\n", &b,b);// b=11

}void main() {

int a=10;fn(a);printf("&a=%p a=%d\n", &a, a); // a=10

}

#include <iostream.h>void fn(int b) {

b=b+1;

Page 21: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

cout<<"&b="<<&b<<'\t'<<"b="<<b<<'\n'; // b=11 }void main() {

int a=10;fn(a);cout<<"&a="<<&a<<'\t'<<"a="<<a<<'\n'; // a=10

}설명: 인수로 a를 매개로 하여 함수를 호출하면 함수의 가인수 b는 자신의 번지에 a의 값 10을 복사하고 이것을 이용하여 함수 내에서 계산을 한다. 따라서 b가 변한다 해도 a는 변하지 않는다. 그러나 배열의 경우에는 복사본이 아닌 원본을 함수 내에서 사용하므로 원본이 변한다. 이것은 참조에 의한 호출에서 참고하자.

5.5.2 참조에 의한 호출(call by reference) 호출시 주소를 넘겨받고 호출된 함수는 포인터 변수로 주소를 받게 된다. 따라서 함수 내에서의 포인터 연산은 곧 원본 데이터 값을 가지고 연산하는 것이므로 원본이 변하게 된다.

예제) 참조에 의한 호출 형태 1#include <stdio.h>void fn(int *b) {

printf(" b=%p &b=%p\n",b,&b);*b=*b+1;printf("*b=%d\n",*b);

}void main() {

int a=10;printf("&a=%p\n", &a);fn(&a);printf(" a=%d \n", a); // a=11

}

#include <iostream.h>void fn(int *b) {

cout<<" b="<<b<<'\t'<<'\n';*b=*b+1;cout<<"*b="<<*b<<'\n';

}void main() {

int a=10;cout<<"&a="<<&a<<'\n';fn(&a);

Page 22: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

cout<<" a="<<a<<'\n'; // a=11}설명: 포인터 b가 a의 주소를 넘겨받으므로 *b는 곧 a자체이다. 따라서 *b의 내용이 변하면 그것은 바로 a가 변하는 것이다. 예제) 참조에 의한 호출 형태 2#include <stdio.h>void fn(int &b) {

printf("&b=%p b=%d\n",&b,b);b=b+1;printf("b=%d\n",b);

}void main() {

int a=10;printf("&a=%p\n", &a);fn(a);printf("a=%d\n", a); // a=11

}

#include <iostream.h>void fn(int &b) {

cout<<"&b="<<&b<<'\t'<<"b="<<b<<'\n';b=b+1;cout<<"b="<<b<<'\n';

}void main() {

int a=10;cout<<"&a="<<&a<<'\n';fn(a);cout<<"a="<<a<<'\n'; // a=11

}참조(&)는 원변수 a에 대한 별칭을 b로 부르고자 할 때 쓰는 표현법으로 b의 주소 &b는 바로 a의 주소 &a와 같다. 따라서 값 b의 변화는 곧 a의 변화를 의미한다.

예제) 배열의 참조#include<stdio.h>void fn(char b[]) {

int i;for(i=0; i<5; i++)

b[i]=b[i]+32;b[5]=NULL;

Page 23: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

}void main() {

char a[5]="ABCD";printf("a=%s\n",a);fn(a);printf("a=%s\n",a);

}

#include<iostream.h>void fn(char b[]) {

int i;for(i=0; i<5; i++)

b[i]=b[i]+32;b[5]=NULL;

}void main() {

char a[5]="ABCD";cout<<"a="<<a<<'\n';fn(a);cout<<"a="<<a<<'\n';

}설명: 함수호출시 배열의 첫 주소를 함수인자에 전달하므로 함수 속에서 배열의 변화는 곧 원본배열의 변화를 초래하게 된다. 즉 함수는 배열의 복사 본을 사용하는 것이 아니라 원본배열의 주소를 가지고 직접 그 배열에 접근하여 계산한다.

연습 프로그램

연습 1) *에 의한 수의 sort.#include <stdio.h>void sort(int *m, int *n) {

int tmp=*m;*m=*n;*n=tmp;

}void main() {

int q[4]={5,4,2,3};int i,j;for(i=0;i<4;i++)

for(j=i+1; j<4; j++) if(q[j]<q[i])

sort(&q[i], &q[j]);

Page 24: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

/* sort 방법 i j if 조건 sort()를 호출하여 원소 교환

---------------------------------------------------i=0 j=1 (q[1]=4)<(q[0]=5) q[0]=4, q[1]=5 j=2 (q[2]=2)<(q[0]=4) q[0]=2, q[2]=4 j=3 (q[3]=3)<(a[0]=2) no exchange i=1 j=2 (q[2]=4)<(q[1]=5) q[1]=4, q[2]=5 j=3 (q[3]=3)<(q[1]=4) q[3]=4, q[1]=3i=2 j=3 (q[3]=4)<(q[2]=5) q[2]=4, q[3]=5 */

for(i=0;i<4;i++)printf("q[%d]=%d\n",i,q[i]);

}

#include <iostream.h>void sort(int *m, int *n) {

int tmp=*m;*m=*n;*n=tmp;

}void main() {

int q[4]={5,4,2,3};int i,j;for(i=0;i<4;i++)

for(j=i+1; j<4; j++) if(q[j]<q[i])

sort(&q[i], &q[j]);/* sort 방법

i j if 조건 sort()를 호출하여 원소 교환

---------------------------------------------------i=0 j=1 (q[1]=4)<(q[0]=5) q[0]=4, q[1]=5 j=2 (q[2]=2)<(q[0]=4) q[0]=2, q[2]=4 j=3 (q[3]=3)<(a[0]=2) no exchange i=1 j=2 (q[2]=4)<(q[1]=5) q[1]=4, q[2]=5 j=3 (q[3]=3)<(q[1]=4) q[3]=4, q[1]=3i=2 j=3 (q[3]=4)<(q[2]=5) q[2]=4, q[3]=5 */

for(i=0;i<4;i++)cout<<"q["<<i<<"]="<<q[i]<<'\n';

Page 25: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

}

연습 2) 참조 &에 의한 문자 sort#include <stdio.h>void sort(char &m, char &n) {

char tmp=m;m=n;n=tmp;

}void main() {

char q[]="ceabd";int i,j;for(i=0;i<5;i++)

for(j=i+1; j<5; j++) if(q[j]<q[i])

sort(q[i], q[j]);printf("q=%s\n", q);

}

#include <iostream.h>void sort(char &m, char &n) {

char tmp=m;m=n;n=tmp;

}void main() {

char q[]="ceabd";int i,j;for(i=0;i<5;i++)

for(j=i+1; j<5; j++) if(q[j]<q[i])

sort(q[i], q[j]);cout<<"q="<<q<<'\n';

}

5.6 포인터의 포인터 및 포인터 비교

5.6.1 포인터의 포인터

Page 26: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

포인터 변수가 가리키는 주소를 다시 가리키는 포인터

예제) 포인터의 포인터#include <stdio.h>void main() {

int a=23, *sp, **dp;sp=&a;dp=&sp;printf("&dp=%p &sp=%p &a=%p\n",&dp,&sp, &a);printf(" dp=%p sp=%p\n",dp,sp);printf("**dp=%d\n",**dp);

}

#include <iostream.h>void main() {

int a=23, *sp, **dp;sp=&a;dp=&sp;cout<<"&dp="<<&dp<<'\t'<<"&sp="<<&sp<<'\t'<<"&a="<<&a<<'\n';cout<<"dp="<<dp<<'\t'<<"sp="<<sp<<'\n';cout<<"**dp="<<**dp<<'\n';

}설명: dp=&sp, sp=&a이므로 **dp는 sp를 가리키고 다시 sp는 a를 가리키므로 **dp=a이다.

5.6.2 포인터 비교포인터 비교는 아래의 두 경우에만 유효하다.i) 포인터와 NULL과의 비교ii) 동일배열의 임의요소를 가리키고 있는 2개의 포인터 비교예) char a[10], b[10], *p=a, *p_last=a[9], *q=b;

if(p !=NULL) //유효if(p<p_last) //유효if(p<q) //무의미

예제) pointer 비교#include <stdio.h>void main() {

double *p;double a[3]={5.03, 2.01, 3.25};p=a;if(p<p+2) {

Page 27: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

printf("%p<%p 이므로 if수행\n",p,p+2); printf("*p=%.2lf\n", *p); // *p=5.03

}else

printf("%lf\n", *(p+2)); }

#include <iostream.h>void main() {

double *p;double a[3]={5.03, 2.01, 3.25};p=a;if(p<p+2) {

cout<<p<<'<'<<p+2<<" 이므로 if수행\n"; cout<<"*p="<<*p<<'\n'; // *p=5.03

}else

cout<<"*(p+2)="<<*(p+2)<<'\n';}

5.7 함수 포인터(Function pointer)함수명은 함수의 실행코드가 위치한 곳, 즉 함수의 주소를 의미한다.따라서 배열 명처럼 함수명은 포인터이다.수치해석에서 자주사용선언형식: (data_type) (*함수_pointer명) (매개변수 list)호출형식: (*함수_pointer명) (매개변수 list)int a(); 반환 값이 int인 함수int* a(); 반환 값이 int형 포인터인 함수void (*a)(): 반환 값이 void이고 포인터인 함수

5.7.1 함수 포인터 선언 및 정의

예제) 함수포인터 선언 및 정의#include <stdio.h>void afn(int n) {

printf("add number=%d\n\n", n+n);}void mfn(int n) {

Page 28: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

printf("multiple number=%d\n", n*n);}void (*pfn)(int);void main() {

int t=10;printf("&afn=%p &mfn=%p &pfn=%p\n", &afn,&mfn,&pfn);printf(" afn=%p mfn=%p pfn=%p\n\n", afn,mfn,pfn);

// pfn은 00000000으로 초기화 되어 있음

pfn=afn; // or pfn=&afn;printf("pfn=%p\n",pfn); //pfn이 afn주소를 가짐 pfn(t); // afn()을 수행pfn=&mfn; // or pfn=mfn;printf("pfn=%p\n",pfn); // pfn이 mfn 주소를 가짐 pfn(t); // mfn()을 수행

}

#include <iostream.h>void afn(int a) {

cout<<"adding number="<<a+a<<"\n\n";}void mfn(int m) {

cout<<"multiple number="<<m*m<<"\n\n";}void (*pfn)(int);void main() {

int t=10;cout<<"&afn="<<&afn<<" "<<"&mfn="<<&mfn<<" "<<"&pfn="<<&pfn<<"\n";cout<<" afn="<<afn<<" "<<"mfn="<<mfn<<" "<<"pfn="<<pfn<<"\n\n";

// pfn은 000000으로 초기화 되어 있음

pfn=afn; // pf=&af; 도 가능cout<<"pfn="<<pfn<<'\n'; //pfn가 afn주소를 가짐 pfn(t); // afn()을 수행pfn=&mfn; // pfn=afn; 도 가능cout<<"pfn="<<pfn<<'\n'; //pf가 mf주소를 가짐pfn(t); // mfn()을 수행

}함수가 시작되는 주소는 &afn이다. 그리고 함수 명 afn의 주소 값을 알아보면 &afn과 같다. 즉 함수주소에 담긴 값은 곧 자신의 주소이다. 다시 말하면 자신이 시작되는 주소번지의 1byte에 자신의 번지를 담아 놓고 있다.

Page 29: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

예제) 함수 포인터에 다른 함수 주소를 할당하여 사용#include <stdio.h>void eng() { printf("Good Day\n"); }void fre() { printf("Bonjour\n"); }void ger() { printf("Gutentag\n"); }void main() {

void (*ptr) ();char ch; ptr=eng;printf("input character e, f, g or q for(quit): ch= ");scanf("%c", &ch);while(ch !='q') {

switch(ch) {case 'e':case 'E': ptr(); break;case 'f':case 'F': ptr=fre; ptr(); break;case 'g':case 'G': ptr=ger; ptr(); break;

}scanf("%c", &ch);}

}

#include <iostream.h>void eng() { cout<<"Good Day\n"; }void fre() { cout<<"Bonjour\n"; }void ger() { cout<<"Gutentag\n"; }void main() {

void (*ptr) ();char ch; ptr=eng;cout<<"input character e, f, g or q for(quit): ch= ";cin>>ch;while(ch !='q') {

switch(ch) {case 'e':case 'E': ptr(); break;case 'f':case 'F': ptr=fre; ptr(); break;case 'g':case 'G': ptr=ger; ptr(); break;

Page 30: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

}cin>>ch;}

}

5.7.2 함수의 인수로서의 함수 포인터

예제) 함수 인수의 함수포인터#include <stdio.h>int gf(int a, int b, int (*cf)(int a, int b)) {

printf(" cf=%p &cf=%p\n",cf, &cf);return(cf(a, b));

}int maxf(int a, int b) {

printf("maxf=%p\n", maxf);return((a>b)?a:b);

}void main() {

int r;r=gf(1,2,maxf); // or gf(1,2,&maxf)

printf("max is %d\n", r);}

#include <iostream.h>int gf(int a, int b, int (*cf)(int a, int b)) {

cout<<" cf="<<cf<<" &cf="<<&cf<<'\n';return(cf(a, b));

}int maxf(int a, int b) {

cout<<"maxf="<<maxf<<'\n';return((a>b)?a:b);

}void main() {

int r;r=gf(1,2,maxf); // or gf(1,2,&maxf)

cout<<"max is "<<r<<'\n';}

5.7.3 전달인자로의 함수 포인터

Page 31: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

예제) 전달인자로의 함수 포인터#include <stdio.h>void fn(void (*pfn)(int p, int q), int p, int q) {

pfn(p,q); printf("value(pfn)=%p\n",pfn);

}void af(int a, int b) { printf("%d+%d=%d\n", a, b, a+b); }void sf(int a, int b) { printf("%d-%d=%d\n", a, b, a-b); }void mf(int a, int b) { printf("%d*%d=%d\n", a, b, a*b); }void main() {

printf("af=%p\t sf=%p\t mf=%p\n\n",af,sf,mf);fn(af, 100, 5);fn(sf, 100, 5);fn(mf, 100, 5);

}

#include <iostream.h>void fn(void (*pfn)(int p, int q), int p, int q) {

pfn(p,q); cout<<"value(pfn)="<<pfn<<'\n';

}void af(int a, int b) { cout<<a<<'+'<<b<<'='<<a+b<<'\n'; }void sf(int a, int b) { cout<<a<<'-'<<b<<'='<<a-b<<'\n'; }void mf(int a, int b) { cout<<a<<'*'<<b<<'='<<a*b<<'\n'; }

void main() {cout<<"af="<<af<<'\t'<<"sf="<<sf<<'\t'<<"mf="<<mf<<'\n';fn(af, 100, 5);fn(sf, 100, 5);fn(mf, 100, 5);

}

5.7.4 반환값이 char형의 포인터 변수

예제 1) 반환 값이 char형의 포인터#include <stdio.h>char *mon(int n) {

int i;static char *p[]= {"illegal", "Jan", "Feb", "Mar", "Apr"};

Page 32: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

return (n<1||n>5) ? p[0] : p[n];}void main() {

int n;printf("input month number: ");scanf("%d", &n);printf("%s\n", mon(n));

}

#include <iostream.h>char *mon(int n) {

static char *p[]= {"illegal", "Jan", "Feb", "Mar", "Apr"}; return (n<1||n>5) ? p[0] : p[n];

}void main() {

int n;cout<<"input month number: ";cin>>n;cout<<mon(n)<<'\n';

}설명: static char *p[]로 포인터 배열이 선언되었고, p[0]나 p[1], p[2], p[3] 및 p[4]는 각 각 문자열이 시작되는 주소로서 이들이 반환된다. 따라서 함수는 포인터 배열이 반환되므로 char* mon() 또는 char *mon()처럼 선언하여 사용한다.

예제 2) 반환 값이 포인터#include <stdio.h> char *pfn(char *s) {

char *w;switch (*s) {

case '0':w="zero";return(w); // w는 포인터 변수.

case '1':w="first";return (w);

case '2':w="second";return (w);

default:w="default";return w;

Page 33: 제 5 장 포인터(Pointer) - KOCWcontents.kocw.net/KOCW/document/2013/koreasejong/... · 2016. 9. 9. · 제 5 장 포인터(Pointer) 주소(address)값을 저장하는 변수를

}}void main() {

char *pt, *q;char *r="012";pt=r;q=pfn(pt);printf("%s\n",q);++pt;q=pfn(pt);printf("%s\n",q);

}설명: 이 함수에서는 반환 값이 w로 주소 값이다. 따라서 함수는 주소 값을 반환하는 것이므로 char *pfn()으로 선언되었다.