기본 미션 : 포인터의 핵심 내용을 정리하고 공유하기
선택 미션 : 나만의 언어로 포인터 정리하기
왜.. 벌써 개학이지
드디어 오늘 개학을 해버렸다.
2주동안 자격증 공부만 하다가 한 주 쉬었는데 벌써 개학날이 오늘이다.
시간은 참 빠른 것 같지만 이번 방학은 나름대로 알차게 할 거 다하면서 푹 쉰 것 같아서 나름대로 만족한 방학이다.
드디어 포인터를 배운다. C언어에서 정말 악명높은 포인터를..
선생님께서 너네 포인터 배우면 난리난다고 하셨던 말씀이 떠올라서 배우기도 전에 두렵기만 하지만 괜찮다!
뭐든 흡수해서 내 것으로 만들면 되기 때문이다.
어차피 해야하고 배울 내용인데 미리 내 것으로 만들어놓으면 수업시간에 따라가기 훨씬 쉬울 것이다.
그럼 정리를 시작해보도록 하겠다.
핵심내용을 정리하고 공유하기! 뭔가 마지막이 되면 될 수록 정리하고 공유하는 미션이 많아진다. 갈수록 내용들이 어려워져서 그런 것도 같다. 이번에도 전체적으로 정리하면서 미션을 수행해보도록 하겠다.
메모리
: 우리가 데이터를 넣고 꺼내 쓰는 공간
우리는 데이터가 어디 있는지, 그 위치를 식별할 수 있어야 한다. 혼공단 미션을 했어도 그 블로그 글이 어디있는지 모른다면 제출을 할 수 없는 것이다.
이렇게 주소를 찾을 수 있게 프로그램이 사용하는 메모리의 위치
를 주소 값
으로 식별할 수 있다. 메모리의 위치를 식별하는 주소 값
은 byte
단위로 구분된다.
예를 들어 double
형 변수 d
가 메모리 1029번지
부터 1037번지
까지 8byte
에 걸쳐 할당된다고 해보자. 변수 선언 후에는 8byte
전체를 d
라는 이름으로 사용하게 된다.
따라서 d = 10.29;
와 같은 문장은 메모리의 1029부터 1037번지
까지의 8byte
공간에 10.29
의 값을 저장하는 것이며, d + 0.81;
과 같은 수식은 메모리의 1029번지부터 1037번지
까지의 8byte
공간에 저장된 값과 0.81
을 더하는 연산을 수행하는 것과 동일하다.
결국 지금까지는 변수명으로 메모리의 공간이나 값을 간단히 사용할 수 있었던 것이다.
우리가 늘 하던 방식이라 이해하기 쉬울 것이다.
: 변수가 할당된 메모리 공간의 시작 주소 값을 알려주는 주소 연산자
주소 연산자
의 형태는 다음과 같다.
&변수명
ex) &a
우리는 늘 저장된 공간을 이름, 즉 변수명
으로 사용했었다. 이번에는 주소
로 사용하는 방법에 대해서 살펴보자.
주소
: 변수가 할당된 메모리 공간의 시작 주소
주소는 주소 연산자 &
를 사용해서 구할 수 있다.
예제를 통해 주소 연산자 &
의 사용법을 익히고 변수가 할당된 메모리의 상태를 확인해보자.
소스코드 예제9-1.c
#include <stdio.h>
int main(void) {
int a; //int형 변수 선언
double b; //double형 변수 선언
char c; //char형 변수 선언
printf("int형 변수의 주소 : %u\n", &a);
printf("double형 변수의 주소 : %u\n", &b);
printf("char형 변수의 주소 : %u\n", &c);
return 0;
}
실행결과
int형 변수의 주소 : 2911894660
double형 변수의 주소 : 2911894696
char형 변수의 주소 : 2911894724
컴퓨터는 프로그램 실행 후 남아있는 메모리를 활용하므로 실행결과가 저마다 다를 수 있다. 에러가 아니니 당황하지 않아도 된다.
int a; //int형 변수 선언
double b; //double형 변수 선언
char c; //char형 변수 선언
위의 소스는 변수를 선언하는 부분이다.
저렇게 변수를 선언하면 프로그램 내에서 각 자료형의 크기만큼 메모리에 저장 공간이 할당된다. 이 때에 변수가 메모리 공간 어디에 할당되었는지 궁금하다면 주소 연산자 &
를 사용하면 된다.
printf("int형 변수의 주소 : %u\n", &a);
printf("double형 변수의 주소 : %u\n", &b);
printf("char형 변수의 주소 : %u\n", &c);
위의 소스는 변수의 주소를 출력하는 부분이다.
출력할 때 %u
는 변환문자로, 부호없는 10진수
로 출력하라는 뜻이다.
주소는 보통 16진수
로 표기하기 때문에 주소를 출력할 때에는 전용 변환 문자
인 %p
를 사용하지만 여기서는 설명의 편의를 위해 주소 값
을 10진수
로 출력하며 주소는 음수가 없으므로 %u
변환문자를 사용했다.
변환 문자의 종류는 다음과 같다.
1. %d : 10진수로 출력 정수형
2. %f : 실수형
3. %e : 지수형
4. %o : 8진수로 출력
5. %x : 16진수로 출력
6. %u : 부호없는 10진수로 출력
7. %g : 실수형으로 자동 출력
8. %p : 포인터의 주소를 출력
9. %c : 하나의 문자로 출력 문자형
10. %s : 문자열을 출력
9-1 직접 해보는 손코딩
은 단지 메모리의 주소만을 구해 출력하는 프로그램이다. 이제 변수에 할당된 메모리 주소를 활용하는 방법을 살펴보자.
: 변수의 메모리 주소를 저장하는 변수
메모리의 주소는 필요할 때마다 계속 주소 연산을 수행하는 것보다 한 번 구한 주소를 저장해서 사용하면 편리한데, 이 역할을 포인터
가 수행한다.
포인터
를 사용할 때는 변수처럼 선언하고 사용하면 되는데, 다만 선언할 때 변수 앞에 *
를 붙여주고 사용해야 한다.
포인터의 형태는 다음과 같다.
자료형 *변수명;
예제를 통해 포인터의 선언과 사용법을 살펴보자.
소스코드 예제9-2.c
#include <stdio.h>
int main(void) {
int a; //일반 변수 선언
int *pa; //포인터 선언
pa = &a; //포인터에 a의 주소 대입
*pa = 10; //포인터로 변수 a에 10 대입
printf("포인터로 a 값 출력 : %d\n", *pa);
printf("변수명으로 a 값 출력 : %d\n", a); //변수 a값 출력
return 0;
}
실행결과
포인터로 a 값 출력 : 10
변수명으로 a 값 출력 : 10
위의 소스코드를 면밀히 분석해보자.
포인터의 자료형은 저장할 주소가 어떤 변수의 주소인지 그 변수의 자료형을 적는다. 예를 들어 int형
변수의 주소를 저장하면 int
를 사용하고 double형
변수의 주소를 저장하면 double
을 사용한다.
int *pa; // 6행. 포인터 선언
6행도 선언된 변수의 형태가 int형
이므로 int
를 사용하여 포인터를 선언한다.
포인터 변수가 선언되면 일반변수와 마찬가지로 메모리에 저장 공간이 할당되고 그 이후에는 변수명으로 사용할 수 있다.
pa = &a; // 8행. 포인터에 a의 주소 대입
8행은 포인터에 a
의 시작 주소를 저장하는 문장이다.
이제 포인터 pa
는 변수 a
가 메모리 어디에 할당되었는지 그 위치를 기억하고 있다. 이렇게 포인터가 어떤 변수의 주소를 저장한 경우에는 가리킨다
라고 하고 둘의 관계를 pa -> a
처럼 화살표로 간단히 표현한다.
pa -> a // 포인터 pa는 변수 a를 가리킨다.
위를 해석하면 pa
는 포인터
이며 변수 a
의 주소를 저장하고 있다는 뜻이 된다.
이처럼 포인터가 어떤 변수를 가리키면 포인터로 가리키는 변수를 사용할 수 있다. 즉, 포인터 pa
로 변수 a
를 사용할 수 있다는 것이다.
포인터가 가리키는 변수를 사용할 때는 포인터에 간접 참조 연산자 (*)
또는 포인터 연산자
를 사용한다.
위에서 말한 포인터 연산자
가 9행에 사용된다.
포인터 pa
가 a
를 가리키므로, 즉 pa
에 a의 주소값
이 들어있어서 a
가 있는 곳으로 가므로 *pa
에 10
을 대입하면 결국 a
에 10
을 대입하는 것과 똑같다.
*pa = 10; // 9행. 포인터로 변수 a에 10 대입
printf("포인터로 a 값 출력 : %d\n", *pa); // 11행
printf("변수명으로 a 값 출력 : %d\n", a); // 12행. 변수 a값 출력
마찬가지로 11행에서 *pa
를 출력하면 a
값이 출력된다.
12행에서 a
를 출력한 결과와 같음을 확인할 수 있다.
즉, 9-2 직접 해보는 손코딩
에서 *pa
와 a
의 값이 동일하다는 것을 알 수 있다.
*pa
는 a
의 쓰임과 마찬가지로 대입 연산자 (=)
의 왼쪽에 올 때는 pa가 가리키는 변수의 저장공간 (l-value)
로 사용되고, 오른쪽에 올 때는 pa가 가리키는 변수의 값 (r-value)
로 사용된다. 물론 연산
하거나 출력
할 때도 값으로 사용한다.
만약 여기서 scanf
를 사용하면 어떻게 될까?
scanf
는 입력할 변수가 메모리 어디에 할당 되었는지 저장공간의 위치를 알아야 한다. 따라서 입력할 변수의 주소를 인수로 준다.
포인터 pa
를 통해 변수 a
의 값을 입력할 때도 동일하다. *pa
는 a
와 같으므로 &a
는 &*pa
와 같다. 즉, 간접 참조 연산자로 pa
가 가리키는 변수를 구하고 다시 주소 연산자로 주소를 구한다.
그런데 pa
가 a
의 주소를 저장하고 있으므로 바로 pa
를 사용해도 된다.
&a
로 변수 a
의 저장 공간 찾기
scanf("%d", &a);
pa
로 변수 a
의 저장 공간 찾기
scanf("%d", pa); // 포인터 pa값은 &a
위의 소스코드들은 전부 내가 입력한 정수값이 a
변수에 저장되는 형태이다.
포인터가 어떤 변수를 가리키게 되면 그 이후에는 간접 참조 연산자
를 통해 가리키는 변수를 자유롭게 사용할 수 있다.
예제를 통해 다양한 포인터의 사용법을 살펴보자.
소스코드 예제9-3.c
#include <stdio.h>
int main(void) {
int a = 10, b = 15, total; //변수 선언과 초기화
double avg; //평균을 저장할 변수
int* pa, * pb; //포인터 동시 선언
int* pt = &total; //포인터 선언과 초기화
double* pg = &avg; //double형 포인터 선언과 초기화
pa = &a; //포인터 pa에 a의 주소 할당
pb = &b; //포인터 pb에 b의 주소 할당
*pt = *pa + *pb; //a값과 b값을 더한 값을 total에 저장
*pg = *pt / 2.0; //total을 2.0으로 나눈 값을 avg에 저장
printf("두 정수의 값 : %d, %d\n", *pa, *pb); //a값과 b값 출력
printf("두 정수의 합 : %d\n", *pt); //합계 출력
printf("두 정수의 평균 : %.1f\n", *pg); //평균 출력
return 0;
}
실행결과
두 정수의 값 : 10, 15
두 정수의 합 : 25
두 정수의 평균 : 12.5
int a = 10, b = 15, total; //변수 선언과 초기화
double avg; //평균을 저장할 변수
int* pa, * pb; //포인터 동시 선언
int* pt = &total; //포인터 선언과 초기화
double* pg = &avg; //double형 포인터 선언과 초기화
pa = &a; //포인터 pa에 a의 주소 할당
pb = &b; //포인터 pb에 b의 주소 할당
a
의 값은 10
이고 b
의 값은 15
이다.
포인터 pa
에는 a
의 주솟값이, 포인터 pb
에는 b
의 주솟값이, 포인터 pt
에는 total
의 주솟값이, 포인터 pg
에는 avg
의 주솟값이 저장되어 있다.
*pt = *pa + *pb; //a값과 b값을 더한 값을 total에 저장
*pg = *pt / 2.0; //total을 2.0으로 나눈 값을 avg에 저장
위의 말에 따라서 *pt = *pa + *pb;
는 total = a + b;
와 같다.
또한 *pg = *pt / 2.0;
는 avg = total / 2.0;
와 같다.
printf("두 정수의 값 : %d, %d\n", *pa, *pb); //a값과 b값 출력
printf("두 정수의 합 : %d\n", *pt); //합계 출력
printf("두 정수의 평균 : %.1f\n", *pg); //평균 출력
a
b
두 정수의 값을 출력하는 것은 *pa
*pb
를 출력하는 것과 같다.
a
b
두 정수의 합을 출력하는 것은 *pa + *pb
인 *pt
를 출력하는 것과 같다.
a
b
두 정수의 평균을 출력하는 것은 *pt / 2.0
인 *pg
를 출력하는 것과 같다.
결국 일반 변수를 a
b
total
avg
와 같은 이름으로도 사용할 수 있고 그 변수들을 가리키는 포인터를 간접 참조해도 사용할 수 있음을 보여준다.
const
예약어는 상수
를 배울 때 배웠을 것이다. const
예약어를 포인터
에 사용하면, 이는 가리키는 변수의 값을 바꿀 수 없다는 의미로, 변수에 사용하는 것과는 다른 의미를 가진다.
예제를 통해 포인터에 const
예약어를 사용하는 예시를 살펴보자.
소스코드 예제9-4.c
#include <stdio.h>
int main(void) {
int a = 10, b = 20;
const int* pa = &a; //포인터 pa는 변수 a를 가리킨다.
printf("변수 a 값 : %d\n", *pa); //포인터를 간접 참조하여 a 출력
pa = &b; //포인터가 변수 b를 가리키게 한다.
printf("변수 b 값 : %d\n", *pa); //포인터를 간접 참조하여 b 출력
pa = &a; //포인터 pa가 다시 a를 가리키게 한다.
a = 20; //변수 a 값을 직접 참조하여 20으로 바꾼다.
printf("변수 a 값 : %d\n", *pa); //포인터로 간접 참조하여 바뀐 값 출력
return 0;
}
실행결과
변수 a 값 : 10
변수 b 값 : 20
변수 a 값 : 20
어라? const
로 상수화한 *pa
의 주소값을 변경했는데도 변경한 값 그대로 잘 출력이 됐다.
그렇다면 포인터에 사용된 const의 의미는 무엇일까?
바로 pa가 가리키는 변수 a는 pa를 간접 참조하여 바꿀 수 없다는 것이다.
만약 *pa = 20;
과 같이 pa
를 통해 a
값을 바꾸고자 한다면 다음의 에러 메시지를 보게 될 것이다.
에러
error C2166: l-value가 const 개체를 지정합니다.
변수 a
는 어디까지나 포인터를 통해서만 바꿀 수 없으며 변수 a
자체를 사용하면 얼마든지 바꿀 수 있다.
그렇다면 왜 포인터에 const
를 사용하는 것일까?
포인터에 const
를 사용하는 대표적인 예는 문자열 상수를 인수로 받는 함수
이다. 문자열 상수
는 값이 바뀌면 안 되는 저장공간
이므로 함수의 매개변수를 통해서 값을 바꿀 수 없도록 매개변수로 선언된 포인터에 const
를 사용한다.
포인터
는 메모리는 사용하는 또 다른 방법이다.주소 연산자 &
로 변수가 할당된 메모리의 위치를 확인한다.간접 참조 연산자 *
를 쓴다.포인터와 연산자
구분 | 사용 예 | 기능 |
---|---|---|
주소 연산자 | int a; &a; | 변수 앞에 붙여 사용하며, 변수가 할당된 메모리의 시작 주소 값을 구한다. |
포인터 | char *pc; int *pi; double *pd; | 시작 주소 값을 저장하는 변수며, 가리키는 자료형을 표시하여 선언한다. |
간접 참조 연산자 | *pi = 10; | 포인터에 사용하며, 포인터가 가리키는 변수를 사용한다. |
포인터
는 주소를 저장하는 일정한 크기의 메모리 공간이다. 따라서 언제든지 다른 주소를 저장하거나 포인터끼리 대입할 수 있다.
그러나, 일반 변수와는 달리 대입 연산에 엄격한 기준이 적용된다. 이런 특징을 이해하는 것은 포인터를 더 잘 활용하는 데에 도움을 줄 것이다.
주소
: 변수에 할당된 메모리 저장 공간의 시작 주소 값 자체
포인터
: 그 값을 저장하는 또 다른 메모리 공간
따라서 특정 변수의 주소 값은 바뀌지 않지만, 포인터는 다른 주소를 대입하여 그 값을 바꿀 수 있다.
이 말은 한 마디로 주소
는 상수
이고, 포인터
는 변수
라는 것이다.
따라서 두 포인터가 같은 주소를 저장하는 일, 즉 하나의 변수를 동시에 가리키는 일도 가능하다.
int a; //일반 변수 선언
int *pa, *pb; //가리키는 자료형(여기서는 int형)이 같은 두 포인터
pa = pb = &a; //pa와 pb에 모두 a의 주소를 저장
이 경우, a
값을 바꾸거나 연산하는 데에 pa
pb
를 모두 사용할 수 있다.
주소도 포인터처럼 간접 참조 연산자를 쓸 수 있지만, 상수
이므로 대입 연산자 왼쪽에 올 수 없다.
&a = &b; //a의 주소를 b의 주소로 바꾸는 것은 불가능
즉, 위의 소스코드에서 알 수 있듯이 상수
와 변수
는 용도가 분명히 다르므로 주소
와 포인터
는 서로 구분하여 이해해야 한다.
포인터
도 저장공간
이므로 그 크기가 있다. 포인터의 크기
는 저장할 주소의 크기
에 따라 결정된다.
모든 주소
와 포인터
는 가리키는 자료형에 관계 없이 크키가 같다.
주소
와 포인터
의 크기는 sizeof
연산자로 확인할 수 있다.
예제를 통해 확인해보자.
소스코드 예제9-5.c
#include <stdio.h>
int main(void) {
char ch;
int in;
double db;
char* pc = &ch;
int* pi = ∈
double* pd = &db;
printf("char형 변수의 주소 크기 : %d\n", sizeof(&ch));
printf("int형 변수의 주소 크기 : %d\n", sizeof(&in));
printf("double형 변수의 주소 크기 : %d\n", sizeof(&db));
printf("char * 포인터의 크기 : %d\n", sizeof(pc));
printf("int * 포인터의 크기 : %d\n", sizeof(pi));
printf("double * 포인터의 크기 : %d\n", sizeof(pd));
printf("char * 포인터가 가리키는 변수의 크기 : %d\n", sizeof(*pc));
printf("int * 포인터가 가리키는 변수의 크기 : %d\n", sizeof(*pi));
printf("double * 포인터가 가리키는 변수의 크기 : %d\n", sizeof(*pd));
return 0;
}
실행결과
char형 변수의 주소 크기 : 8
int형 변수의 주소 크기 : 8
double형 변수의 주소 크기 : 8
char * 포인터의 크기 : 8
int * 포인터의 크기 : 8
double * 포인터의 크기 : 8
char * 포인터가 가리키는 변수의 크기 : 1
int * 포인터가 가리키는 변수의 크기 : 4
double * 포인터가 가리키는 변수의 크기 : 8
위의 소스코드에서 ch
in
db
는 각각의 변수 자체의 크기는 다르지만, 그 시작 주소 값의 크기는 8
로 모두 같다는 것을 볼 수 있다.
포인터는 다음 규칙에 따라 제한적으로 사용해야 한다.
포인터
끼리 대입 연산을 수행하면 여러 개의 포인터로 같은 데이터를 다루는 것이 가능하다.
가리키는 자료형이 일치하지 않는 포인터의 대입을 시도하면 경고 메시지가 뜬다.
경고
warning C4133: '=' : 'int *'과(와) 'double *' 사이의 형식이 호환되지 않습니다.
포인터가 가리키는 자료형이 다른 경우라도 형 변환 연산자를 사용하면 경고 메시지 없이 대입할 수 있다.
다음과 같은 예시를 살펴보자.
double a = 3.4; //double형 변수 선언
double *pd = &a; //double형 포인터 pd에 a의 주소 값 대입
int *pi; //int형 포인터 pi 선언
pi = (int *)pd; //double형 pd를 int로 형 변환 시켜서 pi에 대입
이처럼 형 변환을 시킨 포인터는 대입이 가능하다는 것을 알 수 있다.
포인터
를 사용하려면 추가적인 변수 선언이 필요하고, 주소 연산, 간접 참조 연산 등 각종 연산을 수행해야 한다. 그러니 포인터를 일부러 즐겨 사용할 필요는 없는 것이다.
그러나
임베디드 프로그래밍
을 할 때 메모리에 직접 접근하는 경우 에는 포인터가 반드시 필요하다.
임베디드 프로그래밍
: 임베디드 시스템 (Embedded System : 내장형 시스템)
을 제어하기 위한 프로그램
임베디드 프로그래밍
은 오늘날 만드는 거의 모든 생활 기기에서 특정 기능을 제어하기 위해 구현된다.
예를 들어 정수기에서 정수, 냉수를 구분해 물이 나오게 한다든가, 선풍기의 풍량을 조절하는 것이다. 이처럼 각 기능을 담당하는 하드웨어를 제어하는 소프트웨어를 만드는 일을 임베디드 프로그래밍이라고 한다.
두 변수의 값을 바꾸는 함수를 통해 포인터의 필요성을 확인해보자.
소스코드 예제9-7.c
#include <stdio.h>
void swap(int* pa, int* pb); // 두 변수의 값을 바꾸는 함수의 선언
int main(void) {
int a = 10, b = 20; //변수 선언과 초기화
swap(&a, &b); // a, b의 주소를 인수로 주고 함수 호출
printf("a:%d, b:%d\n", a, b); //변수 a, b 출력
return 0;
}
void swap(int* pa, int* pb) { // 매개변수로 포인터 선언
int temp; //교환을 위한 임시 변수
temp = *pa; // temp에 pa가 가리키는 변수의 값 저장, a의 값 저장
*pa = *pb; // pa가 가리키는 변수에 pb가 가리키는 변수의 값 저장, a에 b의 값 저장
*pb = temp; // pb가 가리키는 변수에 temp의 값 저장, b에 a의 값 저장
}
실행결과
a:20, b:10
이 예제에서는 두 변수의 값을 swap
함수 호출을 통해 바꾼다.
먼저 9행의 함수 호출에서 바꿀 변수 a
b
의 주소를 인수로 준 후 swap
함수를 호출한다.
int *pa
에는 a
의 값이, int *pb
에는 b
의 값이 할당된다.
교환을 위한 임시 변수 temp
에 a
의 값인 *pa
를 저장한다.
a
의 값이 들어있는 *pa
에 b
의 값이 들어있는 *pb
를 저장한다.
b
의 값이 들어있는 *pb
에 a
의 값이 들어있는 temp
을 저장한다.
아래의 소스코드들은 풀이의 해당하는 소스코드이다.
temp = *pa;
*pa = *pb;
*pb = temp;
위의 소스코드는 다음과 같다.
temp = a;
a = b;
b = temp;
교환 작업은 swap
함수에서 포인터
를 통해 진행되지만 실제 바뀌는 값은 main
함수의 a
b
가 된다. 결국 swap
함수는 포인터
를 통해 main
함수의 변수 a
b
를 공유하므로 두 변수를 직접 바꾸는 일이 가능해진다.
우선 swap
함수에서 main
함수의 a
b
를 이름으로 직접 사용하는 방법을 생각해보겠다.
소스코드 예제9-8.c
#include <stdio.h>
void swap(void); // 두 변수의 값을 바꾸는 함수 선언
int main(void) {
int a = 10, b = 20; // 변수 선언과 초기화
swap(); // 인수 없이 함수 호출
printf("a:%d, b:%d\n", a, b); // 변수 a, b 출력
return 0;
}
void swap(void) { // 인수가 없으므로 매개변수도 없음
int temp; // 교환을 위한 변수
temp = a;
a = b;
b = temp;
}
이 예제는 컴파일 과정에서 다음과 같은 에러 메시지를 볼 수 있다.
에러
error C2065: 'a' : 선언되지 않은 식별자입니다.
error C2065: 'a' : 선언되지 않은 식별자입니다.
error C2065: 'b' : 선언되지 않은 식별자입니다.
error C2065: 'b' : 선언되지 않은 식별자입니다.
위의 에러가 나는 원인은 간단하다. 지역변수를 다른 함수에서 같은 함수의 변수인 것마냥 사용했기 때문이다.
함수 안에 선언된 변수명은 사용 범위가 함수 내부로 제한되므로, main
함수에 있는 변수 a
b
는 다른 함수인 swap
함수에서 그 이름을 사용할 수 없다.
다음 방법은 main
함수에서 a
b
의 값을 swap
함수에 인수로 주는 방법이다.
소스코드 예제9-9.c
#include <stdio.h>
void swap(int x, int y); // 두 변수의 값을 바꾸는 함수 선언
int main(void) {
int a = 10, b = 20; // 변수 선언과 초기화
swap(a, b); // a, b의 값을 복사해서 swap함수에 전달
printf("a:%d, b:%d\n", a, b); // 변수 a, b 출력
return 0;
}
void swap(int x, int y) {
int temp; // 교환을 위한 변수
temp = x; // temp에 x값 저장
x = y; // x에 y값 저장
y = temp; // y에 temp값 저장
}
실행결과
a:10, b:20
swap(a, b);
에서 함수를 호출할 때 main
함수의 변수 a
b
의 값이 복사되어 swap
함수의 매개변수인 x
y
에 저장된다. 결국 swap
함수 안에서는 a
b
의 복사본을 바꾸므로 main
함수의 a
b
의 값은 변함이 없게 된다.
swap
함수에서 매개변수의 이름을 a
b
로 사용해도 결과는 같다. 왜냐면 변수명이 같아도 함수가 다르면 전혀 다른 변수로 간주하기 때문이다.
주소와 포인터
는 상수와 변수의 차이
가 있다.포인터의 크기
는 주소의 크기와 같다.포인터
에 주소를 저장할 때는 가리키는 자료형
이 같아야 한다.포인터의 주요 기능
중 하나는 함수 간에 효과적으로 데이터를 공유하는 것이다.간접 참조 연산자를 사용한 예 (포인터 pa가 변수 a를 가리킬 때)
구분 | 변수 a 사용 | 포인터 pa 사용 |
---|---|---|
대입 연산자 왼쪽 | a = 10; | *pa = 10; |
대입 연산자 오른쪽 | b = a; | b = *pa; |
피연산자 | a + 20; | *pa + 20; |
출력 | printf("%d", a); | printf("%d", *pa); |
입력 | scanf("%d", &a); | scanf("%d", pa); scanf("%d", &*pa); |
주소와 포인터의 특징
구분 | 사용 예 | 기능 |
---|---|---|
포인터 | int a, b; int *p = &a; p = &b; | 포인터는 변수이므로 그 값을 다른 주소로 바꿀 수 있다. |
포인터의 크기 | int *p; sizeof(p) | 포인터의 크기는 컴파일러에 따라 다를 수 있으며, sizeof 연산자로 확인한다. |
포인터의 대입 규칙 | int *p; double *pd; pd = p; (X) | 포인터는 가리키는 자료형이 일치할 때만 대입한다. |
혼공단의 대장정이 끝났다.
혼공단을 시작하게 된 계기, 혼공단을 하면서 느낀점, 발전한 점 등등을 벨로그에 한 번 써보는 시간을 가질 것이다.
진짜 안녕~