포인터 정리
C언어에서 첫 번째 난관이라고 할 수 있고, 깊게 들어가자면 복잡하지만 그만큼이나 유용하고 C언어를 강력하게 만들어 주는 무기, 포인터를 배열과 연관지어 정리한다.
포인터와 연산자
포인터는 주소의 값을 보관하는 변수이다.
즉, 포인터란 메모리상에 위치하는 특정한 데이터의 주소의 값을 보관하는 변수이다.
단, 포인터에는 시작 주소의 값이 들어간다는 것을 명심하자.
포인터의 형태
(데이터 형) * (포인터의 이름);
ex)
int * p1; //int형 주소를 받는 포인터
char * p2;//char형 주소를 받는 포인터
포인터와 함께 쓰이는 연산자
&연산자 : 주소를 불러온다
ex)
int a = 3;
int *p1 = &a; : 변수 a의 주소값을 포인터 변수 p1에 저장한다.
*연산자 (역참조 연산자) : 메모리 주소에 저장된 값을 불러온다.
- 예를 들어 <<*주소>> 라고 한다면 해당 주소에 저장된 값을 불러온다는 의미이다.
ex)
int a = 3;
int *p1 = &a; : 변수 a의 주소값을 포인터 변수 p1에 저장한다.
printf("%d", *p1); // 여기서 *는 포인터 변수 선언이 아닌 역참조이다.
포인터 변수 p1에는 a의 주소가 들어가 있다. 따라서 프린트 되는 것은 p1의 주소, 즉 a의 주소가 가지는 값 3이다.
포인터와 배열
배열과 포인터는 밀접한 관계가 있다. 쉽게 배열은 포인터다, 라고 생각하면 된다. 배열의 이름은 주소의 첫 인덱스 값을 가르킨다.
배열의 각 값, 요소는 각각 자료형 만큼의 방을 (주소를) 할당하고 있다.
아주 간단히 예를 들어 int arr[3] = {1, 2, 3}; 이라는 배열이 있다.
0번 인덱스의 주소값이 0이라면 1번 인덱스의 주소값은 4일 것이다. 왜냐하면 int형의 크기가 4이기 때문이다. 아래의 코드를 참조하면 더욱 이해하기 편하다.
코드 0.
int main(void){
int arr1[] = {0, 1, 2};
int *p1 = arr1;
printf("%p\n", p1); //1
printf("%p\n", (p1+1)); //2
printf("%p\n", (p1+2)); //3
return 0;
}
설명 : (각 번호는 주석에 적힌 번호이다)
1. 배열 첫 주소 : 배열의 첫 주소를 출력한다.
2. 배열의 두 번째 주소 : (p1+1)을 하면 포인터가 저장된 주소 + sizeof(자료형) 만큼 이동된 주소를 가르킨다. 이 말은 자료형의 크기만큼 더해진 주소를 가르킨다는 것이다. 즉 (p1+1)은 int형의 크기인, 4바이트 후의 주소를 가르킨다. 출력 결과를 보면 주소값이 4 더해진 것을 볼 수 있다.
3. 배열의 세 번째 주소 : 이번에는 (p1 +2)로 배열의 세 번째 요소를 가르킨다. 이는 본래 p1이 가르키는 주소에 4 + 4 만큼 더해진 주소이다.
정말 늘어나는 주소는 형태에 영향을 받을까? 아래 코드를 살펴보면 해답이 나온다. 이번에는 int형이 아닌 char형을 이용한다.
코드 1.
#include <stdio.h>
int main(void){
char arr1[] = {0, 1, 2};
char *p1 = arr1;
printf("%p\n", p1); //1
printf("%p\n", (p1+1)); //2
printf("%p\n", (p1+2)); //3
return 0;
}
설명 : (각 번호는 주석에 적힌 번호이다)
1. 배열 첫 주소 : 배열의 첫 주소를 출력한다.
2. 배열의 두 번째 주소 : (p1+1)을 하면 포인터가 저장된 주소에 char의 자료형 1만큼을 더한다. 출력 결과를 보면 주소값이 1 더해진 것을 볼 수 있다.
3. 배열의 세 번째 주소 : 이번에는 (p1 +2)로 배열의 세 번째 요소를 가르킨다. 이는 본래 p1이 가르키는 주소에 1+ 1 만큼 더해진 주소이다.
위 두 코드를 보면 배열이 어떤 식으로 구성되어있는지 어렴풋이 보일 것이다. 배열은 결국 주소를 담고 있으며, 그 주소마다 일정 공간을 할당해 값을 담고 있다.
배열과 포인터와의 관계 코드
배열이 포인터와 밀접한 관계가 있다면, 포인터로 배열을 다룰 수있을까? 그렇다. 이에 대해서는 아래 코드를 참조.
코드 2.
int main(void){
int odd[5] = {3, 5, 7, 9, 11};
printf("odd[0] = %d\n", odd[0]); // 1
printf("*odd = %d\n", *odd); // 2
printf("*odd+1 = %d\n", *odd+1); //3
printf("*(odd+1) = %d\n\n", *(odd+1)); //4
return 0;
}
설명 : (각 번호는 주석에 적힌 번호이다)
1. 인덱스 이용 : 인덱스를 이용해 배열의 첫 요소를 반환한다.
2. 역참조 이용 : 역참조를 이용해 배열의 첫 요소를 반환한다. 앞서 배열의 이름은 배열에 담긴 첫 값의 첫 주소를 가리킨다고 한 바 있다. 즉 3의 주소를 가르킨다.
* 배열의 요소들은 특정 주소 값에 일정한 크기로 나열되어있다. 그러므로 배열 odd 그 자체는 주소값을 담고 있다.
3. 역참조 +1 : 역참조를 이용해 가져온 값 3에 1을 더한다. 즉 3 + 1이다.
4. 두 번째 주소 값을 역참조 : *(odd+1)은 배열 주소의 다음 값의 첫 주소를 역연산한다.
즉, *odd == odd[0] 이라면, *(odd+1) == odd[1]이다.
이와 마찬가지로 *(odd+2) == odd[2]일 것이다.
코드 3.
#include <stdio.h>
int main(){
int arr1[] = {1, 2, 3, 4, 5};
int *p1 = arr1; //1
*p1 = 999; //2
printf("arr1[0] : %d\n", arr1[0]);
printf("*arr : %d", *arr1);
return 0;
}
1. p1이라는 포인터 변수는 arr1이라는 배열의 값을 받는다. 즉, arr1이라는 배열의 첫 주소값을 받는다.
2. *p1 = 999 : 역참조 연산자를 이용해 p1의 주소값의 데이터 값을 999로 할당한다. 앞서 이 주소값은 배열의 첫 요소의 주소값임을 밝혔다.
출력해보면 배열의 첫 데이터 값이 바뀐 것을 알 수 있다. 즉, 포인터를 이용하여 주소값을 이용하면 데이터 값을 변경 가능하다.
'컴퓨터 > C, C++' 카테고리의 다른 글
[C++] C++에서 클래스 생성자 재사용(상속, 부모 생성자, 자식 생성자) (0) | 2022.06.06 |
---|---|
[C]c언어 배열과 문자열, 배열에 문자열 대입하기 (0) | 2022.05.07 |
[C언어] CSV 컴마 < , >로 구별된 숫자를 출력하는 프로그램 (0) | 2021.12.08 |
[C언어] 단어 개수를 세는 프로그램, 공백으로 단어 구별 (0) | 2021.12.08 |
[C언어] 아스키 코드 활용 소문자를 대문자로 바꾸기 (0) | 2021.12.07 |
댓글