728x90
반응형
Chap 9 포인터
1. 포인터의 기본 개념
- 변수 선언으로 메모리 공간을 확보하고, 데이터를 넣고 꺼내 쓰는 공간으로 사용
- 변수명: 메모리 공간을 식별할 수 있는 이름
- 하지만 함수 내부(선언된 블록)에서만 그 변수를 사용할 수 있음
- 같은 변수명을 사용해도 블록이나 함수가 다르면 별도의 저장 공간을 확보하므로
전혀 다른 변수로 사용되는 것! - 포인터:: 사용 범위를 벗어난 경우에도 데이터를 공유할 수 있게 도와줌
1) 메모리의 주소
- 메모리: 데이터를 넣고 꺼내 쓰는 공간, 그 위치를 식별할 수 있어야 함
- 프로그램이 사용하는 메모리의 위치는 주소 값으로 식별할 수 있음
- 메모리의 위치를 식별하는 주소 값은 바이트 단위로 구분됨!
- 이 값은 0부터 시작하고, 1씩 증가!!
- 따라서 2바이트 이상의 크기를 갖는 변수는 여러 개의 주소 값에 걸쳐 할당됨
- int형 변수a가 메모리 100번지부터 할당되었다면 100번지부터 103번지까지 4바이트에 걸쳐 할당됨
- 변수 선언 이후에는 4바이트 전체를 a라는 이름으로 사용!
- 따라서 a = 10;과 같은 문장은 메모리의 100번지부터 103번지까지 4바이트의 공간에 10을 저장하며,
a + 20;과 같은 수식은 메모리 100번지부터 103번지까지 4바이트에 저장된 값과 20을 더하는 연산을 수행 - 변수명으로 메모리 공간이나 값을 간단히 사용할 수 있음!!
2) 주소 연산자
- 변수를 이름이 아닌 주소로 사용하는 방법에 대해 알아볼 참임
- 주소: 변수가 할당된 메모리 공간의 시작 주소를 의미
- 시작 주소를 알면 그 위치부터 변수의 크기만큼 메모리를 사용할 수 있음.
- 주소는 주소 연산자 &를 사용해서 구함.
- 예제를 통해 &의 사용법을 익히고 변수가 할당된 메모리의 상태를 확인하자!
변수의 메모리 주소 확인 // 9-1.c |
#include <stdio.h> int main(void) { int a; double b; char c; printf("int형 변수의 주소 : %u\n", &a); printf("double형 변수의 주소 : %u\n", &b); printf("char형 변수의 주소 : %u\n", &c); return 0; } |
int형 변수의 주소 : 2425354020 double형 변수의 주소 : 2425354056 char형 변수의 주소 : 2425354084 |
- int형 변수 a의 주소를 출력한 것이므로
변수 a는 20번지부터 23번지까지 4바이트에 할당되었음 - double형 변수는 56번지부터 63번지까지 8바이트가 할당되었으며
- char형 변수는 84번지 한 바이트에 할당되었다
[ 메모리 주소의 출력 변환 문자 ]
- 주소는 보통 16지수로 표기
- 주소를 출력할 때는 전용 변환 문자인 %p를 사용하는 것이 좋다.
- %p는 주소값의 데이터 크기에 따라 자릿수를 맞춰 16진수 대분자로 출력함.
- 설명의 편의를 위해 10진수로 출력하여 %u 변환문자를 사용했음.
3) 포인터와 간접 참조 연산자
- 변수에 할당된 메모리 주소를 활용하는 방법을 알아보자.
- 메모리의 주소는 필요할 때마다 계속 주소 연산을 수행하는 것보다 한 번 구한 주소를 저장해서 사용하면 편리함.
- 포인터가 바로 변수의 메모리 주소를 저장하는 변수이다.
- 따라서 주소를 저장할 포인터도 변수처럼 선언하고 사용함
- 선언할 때는 변수 앞에 *만 붙여 주면 됨.
포인터의 선언과 사용 |
#include <stdio.h> int main(void) { int a; int* pa; pa = &a; *pa = 10; printf("포인터로 a값 출력 : %d\n", *pa); printf("변수명으로 a값 출력 : %d\n", a); return 0; } |
![]() |
- 만약 변수 a가 메모리 100번지부터 103번지까지 할당되었다면 주소 값 100이 pa에 저장됨
- 포인터 pa는 변수 a가 메모리 어디에 할당되었는지 그 위치를 기억하고 있음.
- 이렇게 포인터가 어떤 변수의 주소를 저장한 경우 '가리킨다'고 말하며
둘의 관계를 pa → a 처럼 화살표로 간단히 표현 - pa는 포인터이며 변수 a의 주소를 저장하고 있음
- 이를 간접 참조 연산자(*) 또는 포인터 연산자라고 함
4) 여러 가지 포인터 사용해 보기
- 포인터가 어떤 변수를 가리키게 되면 그 이후에는 간접 참조 연산자를 통해 가리키는 변수를 자유롭게 쓸 수 있음
포인터를 사용한 두 정수의 합과 평균 계산 |
#include <stdio.h> int main(void) { int a = 10, b = 15, total; double avg; int* pa, * pb; int* pt = &total; double* pg = &avg; pa = &a; pb = &b; *pt = *pa + *pb; *pg = *pt / 2.0; printf("두 정수의 값 : %d, %d\n", *pa, *pb); printf("두 정수의 합 : %d\n", *pt); printf("두 정수의 평균 : %.1lf\n", *pg); return 0; } |
![]() |
'5) const를 사용한 포인터
- const 예약어를 포인터에 사용하면 이는 가리키는 변수의 값을 바꿀 수 없다는 의미
- 변수에 사용하는 것과는 다른 의미를 가짐
포인터에 const 사용 |
#include <stdio.h> int main(void) { int a = 10, b = 20; const int* pa = &a; printf("변수 a 값 : %d\n", *pa); pa = &b; printf("변수 b 값 : %d\n", *pa); pa = &a; a = 20; printf("변수 a 값 : %d\n", *pa); return 0; } |
![]() |
- 포인터에 사용된 const의 의미
:: pa가 가리키는 변수 a는 pa를 간접 참조해 바꿀 수 없다는 뜻
2. 포인터 완전 정복을 위한 포인터 이해하기
- 포인터:: 주소를 저장하는 일정한 크기의 메모리 공간
- 다른 주소를 저장하거나 포인터까지 대입 가능
- 일반 변수와는 달리 대입 연산에 엄격한 기준이 적용됨
1) 주소와 포인터의 차이
- 주소는 변수에 할당된 메모리 저장 공간의 시작 주소 값 자체
- 포인터는 그 값을 저장하는 또 다른 메모리 공간
- 특정 변수의 주소 값은 바뀌지 않지만, 포인터는 다른 주소를 대입해 그 값을 변경할 수 있음
- 변수 a의 주소는 100이고, b의 주소는 200으로 프로그램 실행 중에는 그 값이 바뀌지 않는다.
- 포인터 p는 a, b 중 어떤 주소를 대입하느냐에 따라 가리키는 변수가 바뀜
- 주소는 상수, 포인터는 변수
- 두 포인터가 같은 주소를 저장하는 일도 가능
2) 주소와 포인터의 크기
- 포인터도 저장 공간이므로 그 크기가 있음
- 포인터의 크기는 저장할 주소의 크기에 따라 결정됨
- 크기가 클수록 더 넓은 범위의 메모리를 사용
- 포인터의 크기는 컴파일러에 따라 다를 수 있으나 모든 주소와 포인터는 가리키는 자료형과
관계 없이 그 크기가 같다는 것에 변함이 없음 - 주소와 포인터의 크기는 sizeof 연산자로 확인할 수 있음
주소와 포인터의 크기 |
#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; } |
![]() |
3) 포인터의 대입 규칙
[ 규칙 1 ] 포인터는 가리키는 변수의 형태가 같을 때만 대입해야 한다
- 포인터끼리 대입 연산을 수행하면 여러 개의 포인터로 같은 데이터를 다루는 것이 가능
- 그러나 규칙을 지키지 않는 대입 연산은 그 결과를 예상할 수 없다
허용되지 않는 포인터의 대입 |
#include <stdio.h> int main(void) { int a = 10; int* p = &a; double* pd; pd = p; printf("%lf\n", *pd); return 0; } |
-92559592117432107884277659021957555520241347761778250032873472.000000 |
- 변수 p와 변수 pd는 모두 포인터지만 가리키는 자료형이 다르다
- 컴파일러는 p에 저장된 값을 int형 변수의 주소로 생각하고,
pd에 저장된 값을 double형 변수의 주소로 생각한다. - 따라서 pd에 p를 대입한 후에 간접 참조 연산을 수행하면 변수 a에 할당된 영역 이후의
할당되지 않은 영역까지 사용하게 됨.
[ 규칙 2 ] 형 변환을 사용한 포인터의 대입은 언제나 가능
- 포인터가 가리키는 자료형이 다른 경우라도 형 변환 연산자를 사용하면 경고 메시지 없이 대입 가능
- 물론 대입한 후에 포인터를 통한 사용에 문제가 없어야 함
double a = 3.4; double *pd = &a; int *pi; pi = (int *)pd; |
- pi에 간접 참조 연산을 수행하면 변수 a의 일부를 int형 변수처럼 사용할 수 있다.
- 이런 사용 방법은 포인터로 메모리를 직접 쪼개 쓰는 것이므로
데이터가 메모리에 저장되는 방식을 충분히 이해하고 있어야 함. - 만약 *pi=10;과 같이 a의 일부분에 정수를 저장하면 정수와 실수의 데이터 크기와 저장 방식이 다르므로
a에 저장한 실수 값은 사용할 수 없다.
4) 포인터를 사용하는 이유
- 변수를 사용하는 가장 쉬운 방법은 이름을 쓰는 것이지만
임베디드 프로그래밍을 할 때 메모리에 직접 접근하는 경우나 동적 할당한 메모리를 사용하는 경우
포인터가 반드시 필요하다.
(1) 두 변수의 값을 바꾸며 포인터 이해하기
포인터를 사용한 두 변수의 값 교환 |
#include <stdio.h> void swap(int* pa, int* pb); int main(void) { int a = 10, b = 20; swap(&a, &b); printf("a:%d, b:%d\n", a, b); return 0; } void swap(int* pa, int* pb) { int temp; temp = *pa; *pa = *pb; *pb = temp; } |
![]() |
(2) 포인터 없이 두 변수의 값을 바꾸기
- swap 함수에서 main 함수의 a, b를 이름으로 직접 사용하는 방법
다른 함수의 변수 사용하기 |
#include <stdio.h> void swap(void); int main(void) { int a = 10 b = 20; swap(); printf("a:%d, b:%d\n" a, b); return 0; } void swap(void) { int temp; temp = a; a = b; b = temp; } |
1>C:\studyC\HonGongChap9\HonGong.c(165,28): error C2059: 구문 오류: ')' 1>C:\studyC\HonGongChap9\HonGong.c(174,10): error C2065: 'a': 선언되지 않은 식별자입니다. 1>C:\studyC\HonGongChap9\HonGong.c(175,4): error C2065: 'a': 선언되지 않은 식별자입니다. 1>C:\studyC\HonGongChap9\HonGong.c(175,7): error C2065: 'b': 선언되지 않은 식별자입니다. 1>C:\studyC\HonGongChap9\HonGong.c(176,4): error C2065: 'b': 선언되지 않은 식별자입니다. |
변수의 값을 인수로 주는 경우 |
#include <stdio.h> void swap(int x, int y); int main(void) { int a = 10, b = 20; swap(a, b); printf("a:%d, b:%d\n", a, b); return 0; } void swap(int x, int y) { int temp; temp = x; x = y; y = temp; } |
![]() |
728x90
반응형
'혼공 스터디 > 혼자 공부하는 C언어' 카테고리의 다른 글
[ 혼공C ] 5주차 : 배열 (0) | 2024.02.01 |
---|---|
[ 혼공C ] 4주차 : 함수 (1) | 2024.01.24 |
[ 혼공C ] 3주차 : 선택문, 반복문 (0) | 2024.01.17 |
[ 혼공C ] 2주차 : 변수와 연산자 (1) | 2024.01.13 |
[ 혼공C ] 1주차 공부 : C언어란? (2) | 2024.01.07 |