C 언어 포인터
C언어 포인터 이해하기
◼︎ C언어 포인터
2024년 4월 2일 2024년 4월 12일
C언어 포인터를 알고는 있으나, 정확히 숙지되지 않은 것 같아서 다시 정리 해본다.
C언어 포인터와 배열
배열의 요소에 접근하는 여러 방법들이다.
1
2
3
4
5
6
7
8
char ary[100];
ary[0] = 'a'; // 배열의 첫 번째 요소에 'a'를 저장
1[ary] = 'b'; // 배열의 두 번째 요소에 'b'를 저장
*(ary+2) = 'c'; // 배열의 세 번째 요소에 'c'를 저장
*(&ary[0]+3) = '\\0'; // 배열의 네 번째 요소에 NULL 문자를 저장
printf( "%s\\n", ary); // 배열에 저장된 문자열을 출력
내가 처음에 이해가 안 되었던 것은
ary[0] , arr, *arr+0, 0[ary] 등 C 언어에서 동일한 값을 표현하는 방식이었다.
C언어 배열 특성
포인터를 이해하려면 변수의 주소부터 이해해야 한다.
주소를 쉽게 설명하면, 컴퓨터는 메모리의 집합체이고, 변수는 특정 메모리에 자리를 잡은 위치이며 그 위치를 주소라고 한다.
인덱스 | 0 | 1 | 2 | 3 |
---|---|---|---|---|
값 | a | b | c | \0 |
값 | &a[0] | &a[1] | &a[2] | &a[3] |
주소 | 100 | 101 | 102 | 103 |
ary를 주소로만 따진다면, 다음과 같이 해석할 수 있다.
- ary[0]은 첫 번째 요소의 주소 100에 0을 더한 100
- ary[1]은 두 번째 요소의 주소 100에 1을 더한 101
C언어 포인터 특성
1
2
char ary[] = "abc";
char *ptr = ary;
먼저, char ary[] = "abc";
라는 배열이 있다고 가정했을 때,
인덱스 | 0 | 1 | 2 | 3 |
---|---|---|---|---|
값 | ‘a’ | ‘b’ | ‘c’ | ‘\0’ |
주소 | 1000 | 1001 | 1002 | 1003 |
이렇게 메모리에 저장된다.
여기서 ‘a’, ‘b’, ‘c’, ‘\0’는 ary 배열의 각 원소이고, 1000, 1001, 1002, 1003은 각 원소의 메모리 주소이다.
그 다음으로, char *ptr = ary;
라는 포인터 변수가 있을 때 포인터 *ptr은 무엇을 가리키는가?
포인터 변수 | 값 (가리키는 주소) | 값(메모리 주소) |
---|---|---|
ptr | 1000 | 999 |
이렇게 메모리에 저장된다.
여기서 ptr는 포인터 변수의 이름이고, 1000은 ptr이 가리키는 주소, 즉 ary 배열의 첫 번째 원소 ‘a’의 주소이다.
포인터가 가리키는 값
처음 오히려 어렵게 생각해서 더 이해가 안 갔는데, 단순히 생각했다.
- 메모리에서 주소는 숫자이므로, 포인터 변수는 단지 숫자가 들어가는 정수 변수일 뿐이다. 포인터 변수 ptr은, ary라는 배열의 주소값 자체를 저장하고 있는 것 뿐이다.
- 그렇다면, ptr의 주소(자기 주소)는 999고, ptr이 가리키는 값은 1000인 것이다.
- 그리고 조금 더 생각해보자면, 포인터 변수 ptr 앞에 * 연산자를 놓으면 ptr 변수가 갖고있는 정수 값의 → 값을 구할 수가 있다.
- ptr 변수 값은 1000이고, 그 주소의 값을 구하면 ‘b’가된다.
풀어서 설명하자면
- ptr의 값은 ary[0]의 주소이며, *ptr은 ary[0]의 값이다.
- ptr+2의 값은 ary[2]의 주소이며, *(ptr+2)는 ary[2]의 값이다.
아직도 포인터를 보면 조금은 생각해야 하지만, 메모리에 어떻게 할당 되는지에 대한 이해가 있으면 조금은 쉽다. 그리고 메모리를 조금 더 공부해보면 좋을 듯 하다.
‘**’ ? 더블 포인터
1
2
3
4
5
6
char* st[] ={1,2,3,4};
char** q;
q = st;
printf("%c", **q);
printf("%c", *(*(q+1)));
더블 포인터는 포인터의 포인터로, 포인터 변수가 가리키는 주소 자체가 또 다른 포인터의 주소인 경우 사용된다. 기본적인 포인터가 변수의 주소를 저장하는 것과 달리, 더블 포인터는 다른 포인터의 주소를 저장한다.
예를 들어, char* st[] ={1,2,3,4};
char** q;
q = st;
라는 코드에서, q
는 st
배열의 첫 번째 요소인 st[0]
의 주소를 가리키는 더블 포인터이다.
따라서 **q
를 출력하면 st[0]
의 값인 1이 출력된다.
► 추가 포인터 예제
포인터와 문자열 예제로 조금 더 이해해보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
int main()
{
char *p = "KOREA"; // 포인터 p 선언, 문자열 KOREA로 초기화
// p는 전체 문자열 "KOREA"의 시작 주소를 가리키게 됨
printf("%s\n", p); // p는 KOREA 문자열의 시작 주소를 가리키는 포인터
printf("%s\n", p + 3);
printf("%c\n", *p); //%c로 출력한다면 출력 불가
printf("%c\n", *(p + 3));
printf("%c\n", *p + 2);
}
// p, *p
// *p는 p가 가리키는 주소에 있는 값
// p가 가리키는 문자열 "KOREA"의 시작 주소이므로, *p는 KOREA의 첫 번째 문자 K를 나타냄
C언어에서 문자열이 어떻게 메모리 저장 되는지?
- 마지막의 \0 Null 문자.
- C 언어에서 문자열은 문자의 배열로 표현되며, 문자열의 끝을 나타내기 위해 마지막 문자에 NULL 문자(‘\0’)를 추가한다.
- 이 NULL 문자는 문자열의 끝을 가리키며, 문자열을 처리하는 함수들이 어디까지가 유효한 문자열인지를 파악할 수 있게한다.
인덱스 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
값 | ‘K’ | ‘O’ | ‘R’ | ‘E’ | ‘A’ | ‘\0’ |
주소 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 |
printf("%s\\n", p)
는 p가 가리키는 문자열, 즉 “KOREA”를 출력한다.printf("%s\\n", p + 3)
는 p가 가리키는 주소에서 3을 더한 위치에 있는 문자열을 출력한다. C 언어에서 문자는 1바이트를 차지하므로 p + 3은 문자열 “KOREA”의 네 번째 문자 ‘E’를 가리킨다. 따라서 “EA”가 출력된다.printf("%c\\n", *p)
는 p가 가리키는 주소에 있는 문자를 출력한다. 여기서 p는 “KOREA”의 시작 주소를 가리키므로 ‘K’가 출력된다.printf("%c\\n", *(p + 3))
는 p가 가리키는 주소에서 3을 더한 위치에 있는 문자를 출력한다. 이는 “KOREA”의 네 번째 문자인 ‘E’를 출력한다.- 마지막으로
printf("%c\\n", *p + 2)
는 p가 가리키는 문자에 2를 더한 문자를 출력한다. ‘K’의 ASCII 값은 75이므로 2를 더하면 77이 되며, 이는 ASCII에서 ‘M’을 나타낸다. 따라서 ‘M’이 출력된다.