프로그래밍실습 제 13 강
강의 내용 포인터(pointer) 포인터 출력 (%p또는 %u 사용) 포인터의 출력: 배열의 경우(1) 포인터 변수: 포인터를 저장하는 변수 부함수를 사용한 두 수의 합의 계산 방법1: return문을 사용한 기존의 방법 방법2: 포인터의 사용 포인터를 사용하지 않으면?
제8장: 포인터(pointer) #include<stdio.h> main() { int a=10, b=20; * 모든 변수는 기억장소에 저장된다. * 각 기억장소마다 각자의 주소(address)가 주어져 있으며, 서로 다른 기억장소는 서로 다른 주소를 갖는다. * 포인터는 기억장소의 주소이며, 기억장소의 주소는 변수이름 앞에 &를 붙여 나타낸다. * 주소를 출력하는 변환문자는 %p를 주로 사용하며 16진수로 주소가 출력된다. (%u를 사용하면 10진수 출력을 얻을 수 있다.) #include<stdio.h> main() { int a=10, b=20; printf("%p %p\n",&a,&b); //주소의 16진수 출력 } 출력 예: 00CAF8EC 00CAF8E0
#include<stdio.h> main() { int a=20, b=10; //주소의 10진수 출력 #include<stdio.h> main() { int a=20, b=10; printf("%u %u\n",&a,&b); } 출력 예: 13301984 13301996 기억장소의 주소는 바이트 단위로 할당된다. * 여러 바이트를 차지하는 변수의 경우 변수의 주소는 그 변수를 위해 할당된 기억장소의 최상위 바이트의 주소를 말한다. * 따라서 4바이트를 차지하는 int형의 경우 그 기억장소를 나타내는 4개의 주소 중 최상위 바이트의 주소가 기억장소의 주소가 된다.
포인터 출력 (%p또는 %u 사용) #include<stdio.h> main() { double a=10, b=20; printf("%u %u\n",&a,&b); // 10진수 출력 } 출력 예: 1245048 1245040
포인터의 출력: 배열의 경우(1) #include<stdio.h> main() { short i; short a[4]={10,20,30,40}; for(i=0;i<4;i++) printf("%u\n",&a[i]); } 출력 예: 7077560 7077562 7077564 7077566
포인터의 출력: 배열의 경우(2) #include<stdio.h> main() { int a[2][2]; printf("%u\n",&a[0][0]); printf("%u\n",&a[0][1]); printf("%u\n",&a[1][0]); printf("%u\n",&a[1][1]); } 출력 예: 1245040 1245044 1245048 1245052 ( 4씩 차이가 난다. int형은 4바이트)
포인터의 출력: 배열의 경우(3) #include<stdio.h> main() { //배열이름은 그 배열의 첫 번째 변수의 주소를 나타낸다. #include<stdio.h> main() { int a[2][2]={10,20,30,40}; printf("%p %p\n",&a[0][0],a); printf("%d %p\n",a[0][0],a); } 출력 예: 0012FF70 0012FF70 ( &a[0][0]과 a는 같다.) 10 0012FF70 ( a[0][0]과 a는 다르다.)
포인터 변수: 포인터를 저장하는 변수 포인터 변수는 기억장소의 주소를 저장한다. 즉, 포인터 상수를 저장한다. 포인터 변수의 선언을 위해서는 변수이름 앞에 *를 추가한다. 포인터 변수의 형은 그 주소에 저장되는 일반 변수형과 일치해야 한다. 변수 선언이 아닌 일반 실행문에서의 변수이름 앞에 *는 그 주소가 가리키는 변수에 저장된 값을 나타낸다. #include<stdio.h> main() { int i=100; int *a; // 변수 a는 int* 형임을 선언함. // 즉 변수 a가 포인터 변수임을 선언함 a=&i; // 변수 a에는 int형 변수 i의 기억장소의 주소가 저장됨 printf("%d\n", *a); // 여기서의 *a는 a가 가리키는 // 주소에 저장된 값. 즉 변수 i에 저장된 값 printf("%u\n", a); } 출력: 100 1245052
포인터 변수의 형은 그 주소에 저장되는 일반 변수 형과 일치해야 한다. int *a, b; double *c, *d, e=0.917; float *f, *g, h=3.14f; char *i, j=’A’; a=&b; c=&e; f=&h; i=&j; . . . . . . 주의: 위 예에서 d=&b; 로 할 수는 없다.
//double형 포인터 #include<stdio.h> main() { double a=1.0, *p; p=&a; printf("%.1f\n",a); printf("%u\n",&a); printf("%u\n",p); printf("%d\n",sizeof(double*)); printf("%d\n",&p); printf("%.1f\n",*p); } 출력: 1.0 4913936 4 4913924
부함수를 사용한 두 수의 합의 계산 방법1: return문을 사용한 기존의 방법 #include<stdio.h> main() { int sum(int,int); int a=100,b=200,c; c=sum(a,b); printf("합은 %d입니다.\n",c); } int sum(int a, int b) int c; c=a+b; return c;
참고: 앞의 예에 대하여 주함수와 부함수에서 사용된 변수들의 주소를 다음과 같이 각각 출력하여 보자. #include<stdio.h> main() { int sum(int,int); int a=100,b=200,c; c=sum(a,b); printf("합은 %d입니다.\n",c); printf(“주함수: &a=%p &b=%p &c=%p\n",&a,&b,&c); } int sum(int a, int b) int c; c=a+b; printf(“부함수: &a=%p &b=%p &c=%p\n",&a,&b,&c); return c;
출력 예: 부함수: &a=0018FBC8 &b=0018FBCC &c=0018FBB8 합은 300입니다. 주함수: &a=0018FCB8 &b=0018FCAC &c=0018FCA0 (변수의 주소가 모두 다름을 알 수 있다.)
방법2: 포인터의 사용 #include<stdio.h> main() { void sum(int,int,int*); int a=100,b=200,c; sum(a,b,&c); // &c : 주소에 의한 전달 printf("합은 %d입니다.\n",c); } void sum(int a, int b, int *c) // c는 int* 형 변수 *c=a+b; // c가 가리키는 주소에 저장된 값을 a,b의 합으로 바꿈 출력: 합은 300입니다. 중요: 이 경우 return 문이 사용되지 않았음에도 불구하고 부함수에서의 계산 결과는 주함수의 c값에 영향을 준다. (부함수에서의 *를 간접연산자(indirection operator)라 부른다.)
이유: 주함수에서 부함수 호출시 ‘주소’를 전달하고 있음에 유의하라.(주소에 의한 호출) 따라서 주함수의 변수 c의 주소는 부함수의 변수 c에 전달되어 사용된다. 하나의 주소(기억장소)에는 단 하나의 값만 저장된다. (두 개의 다른 값이 같은 주소에 저장될 수 없다.) 이 경우 부함수에서 직접 주함수의 변수 c에 접근하여 저장된 값을 바꾸게 된다.
// 주함수와 부함수에서 각 변수의 주소를 출력해 보자. #include<stdio.h> main() { void sum(int,int,int*); int a=100,b=200,c; sum(a,b,&c); // &c : 주소에 의한 전달 printf("합은 %d입니다.\n",c); printf("주함수: %u %u %u\n",&a,&b,&c); } void sum(int a, int b, int *c) // c는 int* 형 변수 *c=a+b; // c가 가리키는 주소에 저장된 값을 a,b의 합으로 바꿈 printf("부함수: %u %u %u %u\n",&a,&b,c,&c); 출력 예: 부함수: 16709652 16709656 16709908 16709660 합은 300입니다. 주함수: 16709900 16709904 16709908
부함수 호출 및 계산 과정의 이해 부함수 호출 100 100 200 200 16709908 300 *c=a+b
참고: 부함수에서 사용되는 변수의 이름은 중요하지 않다. #include<stdio.h> main() { void sum(int,int,int*); int a=100,b=200,c; printf("주함수에서 c의 주소: %p\n",&c); sum(a,b,&c); printf("합은 %d입니다.\n",c); } void sum(int a, int b, int *d) // 포인터 변수 이름으로 d를 사용 printf("부함수에서 d에 저장된 주소: %p\n",d); *d=a+b; 출력: 주함수에서 c의 주소: 0012FF3C 부함수에서 d에 저장된 주소: 0012FF3C 합은 300입니다.
주의: 포인터를 사용하지 않으면? #include<stdio.h> main() { void sum(int,int,int); int a=100,b=200,c; sum(a,b,c); // 값에 의한 전달 printf("합은 %d입니다.\n",c); } void sum(int a, int b, int c) // c는 int 형 변수 c=a+b; // 여기에서의 c값은 주함수로 전달되지 않는다. 출력 예: 합은 -858993460입니다. (쓰레기 값, garbage)
이유: 아래에서처럼 주소를 출력하여 보면 주함수와 부함수에서의 c는 전혀 다른 주소를 사용한다.(즉, 서로 다른 변수이다.) 따라서 부함수에서의 변화가 주함수에 영향을 줄 수 없다. #include<stdio.h> main() { void sum(int,int,int); int a=100,b=200,c; printf("주함수: c의 주소: %p\n",&c); sum(a,b,c); printf("합은 %d입니다.\n",c); } void sum(int a, int b, int c) printf("부함수: c의 주소: %p\n",&c); c=a+b; 출력 예: 주함수: c의 주소: 0012FF3C 부함수: c의 주소: 0012FEEC 합은 -858993460입니다.
중요한 점: 부함수로 전달되는 것은 항상 상수이다. #include<stdio.h> main() { int sum(int,int); int a=100,b=200,c; c=sum(a,b); //함수의 호출 printf("합은 %d입니다.\n",c); } int sum(int a, int b) return a+b; 위 프로그램에서 함수 호출시 부함수로 전달되는 것은? 답: 100, 200 (즉, 상수 2개)
따라서 다음과 같이 프로그램하는 것이 가능하다. #include<stdio.h> main() { int sum(int,int); int c; c=sum(100,200); //함수의 호출 printf("합은 %d입니다.\n",c); } int sum(int a, int b) return a+b; 위 프로그램에서 함수 호출시 부함수로 전달되는 것은? 답: 100, 200 (즉, 상수 2개)
정수 n의 값을 입력하면 이를 부함수로 전달하고 출력하는 프로그램을 만들어보자. (단, 포인터를 사용해야 하며 return문을 사용할 수 없다.) #include<stdio.h> main() { int n,s; void square(int,int*); printf("정수 n입력: "); scanf("%d",&n); square(n,&s); printf("n=%d n*n=%d\n",n,s); } void square(int n, int *s) *s=n*n;
참고: 포인터 변수를 사용하여 앞의 예를 다음과 같이 바꿔도 된다. #include<stdio.h> main() { int n,s,*p; void square(int,int*); printf("정수 n입력: "); scanf("%d",&n); p=&s; square(n,p); printf("n=%d n*n=%d\n",n,s); } void square(int n, int *s) *s=n*n; //부함수로 전달되는 것은? n에 저장된 값과 s의 주소 중요한 점: 이 예에서 부함수 호출시 일반 상수 하나와 포인터 상수 하나가 부함수로 전달된다. 즉, 전달되는 것은 모두 상수이다.