티스토리 뷰
C에 입문하면 포인터라는 장벽이 있다. 거기에 2차원 포인터 배열 들어가면 머리 속이 뒤죽박죽이다. 그래서 나의 머리를 정리하게 위해 포인터 배열을 정리해보았다.
포인터의 산술연산을 사용하는 경우 포인터가 배열인 것 처럼 사용할 수 있고 연산도 포인터와 배열이 비슷하다. 이 두가지의 공통점과 차이점을 살펴보자
1차원 배열과 포인터
우선 아래 예제를 살펴보자.
#include <stdio.h>
int main()
{
int arr[10];
int num = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < num; ++i)
arr[i] = (i + 1) * 100;
int* ptr = arr;
printf("%p %p %p\n", ptr, arr, &arr[0]); // 0x7ffdad519460 0x7ffdad519460 0x7ffdad519460
ptr += 2;
printf("%p %p %p\n", ptr, arr + 2, &arr[2]); // 0x7ffdad519468 0x7ffdad519468 0x7ffdad5194608
// Note: arr += 2 // invalid // 배열의 이름이 포인터처럼 사용될 수 있지만 포인터와 완전히 동일하지 않는다.
// ()를 사용하는 경우 포인터 주소값으로 계산하여 출력한다..
printf("%d %d %d\n", *(ptr + 1), *(arr + 3), arr[3]); // 400 400 400
// Warning
// ()를 사용하지 않으면 *변수 계산 후 연산을 진행한다.
printf("%d %d %d\n", *ptr + 1, *arr + 3, arr[3]); // 301 103 400
for (int i = 0, *ptr = arr; i < num; ++i)
{
printf("%d %d %d\n", *ptr++, arr[i], *(ptr+i)); // 100 100 200 200 300 300 ... 1000 1000
printf("%d %d\n", *ptr + i); // 101 102 103 ...
}
}
우선 arr 배열을 초기화 하자.
// arr 배열을 초기화
int arr[10];
int num = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < num; ++i)
arr[i] = (i + 1) * 100;
// arr[10] = {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000}
초기화한 arr 배열을 ptr 포인터 변수에 대입한다.(포인터 변수 선언 int* ptr;)
여기서 arr 자체는 arr의 주소를 가리키기 때문에 ptr 포인터 변수에 대입이 된다.
int* ptr = arr;
그런 다음 포인터 배열의 주소와 값을 가져오는 코드를 분석해보자.
1차원 포인터 배열 주소를 조회하는 코드는 아래와 같다.
printf("%p %p %p\n", ptr, arr, &arr[0]); // 0x7ffdad519460 0x7ffdad519460 0x7ffdad519460
역참조(dereferencing))
포인터의 값을 조회하기 위해 역참조를 해야한다. 그래서 * 연산자를 사용해 역참조를 한다.
1차원 포인터 배열 값를 조회하는 코드는 아래와 같다.
ptr+=2;
printf("%d %d %d\n", *(ptr + 1), *(arr + 3), arr[3]); // 400 400 400
포인터 주소에서 값을 가져오려면(역참조(dereferencing)) *을 사용하야 한다.
ptr +=2를 해주면 ptr 포인터 변수가 참조하고 있는 주소는 포인터 주소 크기의 2칸만큼 이동하고 *(ptr+1)까지 해주면 포인터 주소 크기의 3칸을 이동하게 되어 *(ptr + 1) == *(arr + 3) == arr[3] 된다.
포인터 배열 값을 조회할 때 주의해야할 점은
*ptr + 1 과 *(ptr+1) 은 다르다는 것이다. *ptr + 1는 포인터 주소에서 역참조한 값에 +1을 하는 것이고 (*ptr+1)은 포인터 배열의 주소 크기만큼 한 칸 이동하여 값을 역참조하여 가져온다는 점이다.
// ()를 사용하는 경우 포인터 주소값으로 계산하여 출력한다..
printf("%d %d %d\n", *(ptr + 1), *(arr + 3), arr[3]); // 400 400 400
// ()를 사용하지 않으면 *변수 계산 후 연산을 진행한다.
printf("%d %d %d\n", *ptr + 1, *arr + 3, arr[3]); // 301 103 400
주의!! 여기서 배열 이름 자체만 가지고 연산은 안된다.
arr += 2 //Error
1차원 배열까지는 괜찮은데 2차원 배열이 나오면 머리가 아프다. 이해해야될 부분도 있고 외워야할 부분이 있다.
2차원 배열과 포인터
#include <stdio.h>
int main()
{
int arr[2][3] = {{1,5,6}, {7,3,8}};
printf("arr[0][0] 주소 가져오기\n");
printf("arr address is %p\n", arr); // arr address is 0x7fff789b4d80
printf("arr address is %p\n", *arr); // arr address is 0x7fff789b4d80
printf("arr address is %p\n", arr[0]); // arr address is 0x7fff789b4d80
printf("arr address is %p\n", &arr[0]); // arr address is 0x7fff789b4d80
printf("arr address is %p\n", &arr[0][0]);// arr address is 0x7fff789b4d80
printf("arr[0][0] 값 가져오기\n");
printf("arr value is %d\n", **arr); // arr value is 1
printf("arr value is %d\n", arr[0][0]); // arr value is 1
printf("arr[1][0] 주소 가져오기\n");
printf("arr address is %p\n", arr+1); // arr is 2 0x7fff789b4d8c
printf("arr address is %p\n", &arr[1]); // arr is 2 0x7fff789b4d8c
printf("arr address is %p\n", arr[1]); // arr is 2 0x7fff789b4d8c
printf("arr address is %p\n", &arr[0]+1);// arr is 2 0x7fff789b4d8c
printf("arr address is %p\n", &arr[1][0]); // arr is 2 0x7fff789b4d8c
printf("arr[1][0] 값 가져오기\n");
printf("arr value is %d\n", *(*(arr+1))); // arr value is 7
printf("arr value is %d\n", arr[1][0]); // arr value is 7
printf("arr[1][2] 값 가져오기\n");
printf("arr value is %d\n", arr[1][2]); // arr value is 8
printf("arr value is %d\n", *(*(arr+1)+2)); // arr value is 8
printf("arr[0][0] +1 값 가져오기\n");
printf("arr value is 2 %d\n", **arr+1); // arr value is 2
}
위 예제에서
arr == *arr == arr[0] == &arr[0] == &arr[0][0] 이다
특이한 점은 arr[0]이 arr와 같으니 &arr[0] 은 arr의 주소로 헷갈릴 수 있는데 C 언어에서 arr == &arr[0] 로 작동한다.
2차원 배열 포인터 주소 변수에서 역참조하여 값을 가져오는 방법은 이중 포인터를 사용할 수 있다.
**arr == arr[0][0] 이다.
1차원 배열 포인터 주소 변수에서는 *arr = arr[0]이고 2차원 배열에서는 **arr == arr[0][0]이다.
즉, 이중포인터를 사용하여 2차원 배열처럼 활용할 수 있다.
만약 arr[1][2]를 가져오고 싶다면 *(*(arr + 1) + 2)를 사용하면 된다.
arr[i][j] == *(*(arr + i) + j) 처럼 사용해야한다.
여기서 주의해야할 점은 **arr + 1은 arr[0][0] + 1 로 1+1 = 2가 되는 점이다.
1차원 포인터 배열과 2차원 포인터 배열를 살펴보았다.
외워야할 부분도 있고 이해해야하는 부분도 있다.
잘 정리해서 C언어를 잘 사용해보자.
'프로그래밍 정보 > C' 카테고리의 다른 글
[C언어]문자열, scanf, gets, fgets로 입력 받기 (0) | 2023.09.05 |
---|---|
[C언어] sizeof 연산자를 활용한 자료형, 배열, 포인터, 이차원 배열 크기 확인하기 (0) | 2023.06.16 |
[C++] 버퍼와 표준출력 시 endl과 '\n'의 차이 (2) | 2023.04.25 |
C언어 - 포인터 이해하기 (0) | 2023.04.24 |
- Total
- Today
- Yesterday
- Kernel
- variable
- 20.03.11.(목)
- JS
- SQL
- 미라클모닝
- 라즈베리파이
- tailwindcss
- 자바스크립트
- 포인터
- 오늘의 공부
- Python
- JSON
- Linux
- yocto
- 재미있는
- 스타벅스
- opencv
- 모닝독서
- js syntax
- CSS
- raspberrypi
- var
- QT
- 초아
- NestJS
- JavaScript
- tailwind
- C
- Til
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 |