4. 포인터 - 배열
1. 필수 기반 지식
더보기
필수 기반 지식
- 2진수, bit, Byte, 16진수
- 프로그래밍) 빌드, 컴파일, 링킹(함수)
- 컴퓨터) 실행 개념과 메모리, CPU 동작구조
- 프로그래밍 언어, 어셈블리, 기계어
- C 언어의 자료형과 함수
- 스텍 메모리 동작 구조
배열과 포인터는 연산자 & * [ ] 만 이해하면 된다.
그리고, 배열과 포인터는 다르다.
하지만, 학습 초기에는 "같다" 고 가정하고 접근하여 익숙해지는게 먼저다.
연산자 우선순위
우선순위 | 연산자 유형 | 연산자 | 결합방향 | ||
높다 | 1 | 괄호, 배열, 구조체 | ( ) [ ] . → | 좌 → 우 | |
2 | 단항연산 | - ! ~ ++ -- (type) & * sizeof | 좌 ← 우 | ||
3 | 산술 연산 | 승제 | * / % | 좌 → 우 | |
4 | 가감 | + - | |||
5 | 비트 이동 연산 | >> << | |||
6 | 관계 연산 | 비교 | < <= > => | ||
7 | 등가 | == != | |||
8 | 비트 논리 연산 | & | ^ | |||
9 | 논리 연산 | && || | |||
10 | 삼항(조건) 연산 | ? : | 좌 ← 우 | ||
11 | 대입 연산 | 대입 | = | ||
복합 대입 | += -= /= %= | ||||
축약 비트 대입 | >>== <<== | ||||
낮다 | 12 | 나열 연산 | 좌 → 우 |
2. 포인터 연산자 사용법
더보기

2.1 포인터 변수 선언, 메모리 주소값 할당

#include <stdio.h>
void main()
{
int iNum = 90;
printf("iNum 변수의 정수값: %d\n", iNum); // num 변수에 담긴 "정수 값 90" 출력
printf("iNum 변수의 주소값: %p\n", &iNum); // num 변수가 참조하는 첫번째 Byte "메모리 주소" 출력
int* p_iNum = &iNum; // 실제로 p_iNum 같은 형태로 선언하지 않는다. 하나의 예시다.
printf("p_iNum 포인터 변수 값: %p\n", p_iNum); // num 변수에 담긴 "정수 값 90" 출력
printf("p_iNum 포인터 변수 역참조 값: %d\n", *p_iNum); // num 변수가 참조하는 첫번째 Byte "메모리 주소 값" 출력
}
2.2 포인터는 포인터다.
메모리 주소값은 주소값일 뿐이다. 변수는 변수다.
#include <stdio.h>
int main(void)
{
int iNum2 = 89; // int형 변수를 선언
int* p_iNum2 = &iNum2; // &iNum2 int형 포인터 주소값을, char형 포인터로 변환
printf("p_iNum: %p\n", p_iNum2); // 포인터 변수명 p_iNum 만 사용한다. "변수명"만 사용한다.
printf("p_iNum: %d\n", *p_iNum2); // 변수명 p_iNum 에 *역참조 연산자를 사용한다. -1, +10
return 0;
}
2.3 포인터 관련 연산자 상관관계 정리
포인터는 역참조를 위해 존재한다. 포인터의 역참조는 변수의 사용과 동일한 의미다.
개념 | 예시 | 포인터는 "가르킨다"는 의미다. |
변수 → 데이터 | iNum → 90 | 일반 변수 사용 방법 |
변수 + & → 포인터 + * → 데이터 | iNum + & → p_iNum + * → 90 | |
&변수 → 포인터 | (&iNum) → p_iNum | 메모리 주소 |
*포인터 → 데이터 | *p_iNum → 90 | 메모리 주소의 데이터를 포인터한다. |
*(&변수) → 데이터 | *(&iNum) → 90 | 포인터 역참조는 변수의 사용과 동일한 방법이다. |
변수 → 데이터 | iNum → 90 |
3. "변수의 포인터 연산"과 메모리 주소
더보기


*포인터 선언할 때 사용한 자료형 크기만큼, (자료형*숫자) Byte 만큼 연산한다.
*예시의 변수들은 순차적으로 저장된다는 가정일 뿐이고, 배열은 연속되고 순차적인 특성을 가진다.
3.1 예시 1

#include <stdio.h>
int main(void)
{
char cNum1 = 70, cNum2 = 71, cNum3 = 72;
char *p_cNum1 = &cNum1, *p_cNum2 = &cNum2, *p_cNum3 = &cNum3;
printf("cNum1 : %p %d\n", p_cNum1, *p_cNum1); //cNum1 : 0x7fff6813cbad 70
printf("cNum2 : %p %d\n", p_cNum2, *p_cNum2); //cNum2 : 0x7fff6813cbae 71
printf("cNum3 : %p %d\n", p_cNum3, *p_cNum3); //cNum3 : 0x7fff6813cbaf 72
printf("\n");
// 메모리에 변수 값이 위 주석처럼, 순차적으로 저장된다고 가정할 때
// (참고) 위의 메모리 주소값을 확인해서 연산을 수정해야 할 수 있다.
printf("p_cNum1 : %p %d\n", p_cNum1, *p_cNum1 ); // p_cNum1 : 0x7fff6813cbad 70
printf("p_cNum1+1 : %p %d\n", p_cNum1+1, *(p_cNum1+1)); // p_cNum1+1 : 0x7fff6813cbae 71
printf("p_cNum1+2 : %p %d\n", p_cNum1+2, *(p_cNum1+2)); // p_cNum1+2 : 0x7fff6813cbaf 72
printf("p_cNum1 : %p %d\n", &p_cNum1, *p_cNum1 ); // p_cNum1 : 0x7fff6813cbad 70
printf("p_cNum1+1 : %p %d\n", &p_cNum1+1, *(p_cNum1+1)); // p_cNum1+1 : 0x7fff6813cbae 71
printf("p_cNum1+2 : %p %d\n", &p_cNum1+2, *(p_cNum1+2)); // p_cNum1+2 : 0x7fff6813cbaf 72
return 0;
}
3.2 예시 2

#include <stdio.h>
int main(void)
{
int iNum1 = 81, iNum2 = 82, iNum3 = 83;
int *p_iNum1 = &iNum1, *p_iNum2 = &iNum2, *p_iNum3 = &iNum3;
printf("p_iNum1: %p %d\n", p_iNum1, *p_iNum1); // p_iNum1: 0x7ffd68a660b4 81
printf("p_iNum2: %p %d\n", p_iNum2, *p_iNum2); // p_iNum2: 0x7ffd68a660b8 82
printf("p_iNum3: %p %d\n", p_iNum3, *p_iNum3); // p_iNum3: 0x7ffd68a660bc 83
printf("\n");
// 메모리에 변수 값이 위 주석처럼, 순차적으로 저장된다고 가정할 때
// (참고) 위의 메모리 주소값을 확인해서 연산을 수정해야 할 수 있다.
printf("p_iNum1 : %p %d\n", p_iNum1, *p_iNum1); // p_iNum1 : 0x7ffd68a660b4 81
printf("p_iNum1+1: %p %d\n", p_iNum1+1, *(p_iNum1+1)); // p_iNum1+1: 0x7ffd68a660b8 82
printf("p_iNum1+2: %p %d\n", p_iNum1+2, *(p_iNum1+2)); // p_iNum1+2: 0x7ffd68a660bc 83
return 0;
}
4. 배열과 메모리 주소
더보기


4.1 "변수의 포인터 연산"과 배열 비교

#include <stdio.h>
int main(void)
{
char cNum1 = 70, cNum2 = 71, cNum3 = 72;
char *p_cNum1 = &cNum1, *p_cNum2 = &cNum2, *p_cNum3 = &cNum3;
printf("cNum1 : %p %d\n", p_cNum1, *p_cNum1); //cNum1 : 0x7fff6813cbad 70
printf("cNum2 : %p %d\n", p_cNum2, *p_cNum2); //cNum2 : 0x7fff6813cbae 71
printf("cNum3 : %p %d\n", p_cNum3, *p_cNum3); //cNum3 : 0x7fff6813cbaf 72
printf("\n");
char arr[3] = {70, 71, 72};
printf("arr[0] : %p %d\n", arr, arr[0]); // arr[0] : 0x7ffe463f02a5 70
printf("arr[1] : %p %d\n", arr+1, arr[1]); // arr[1] : 0x7ffe463f02a6 71
printf("arr[2] : %p %d\n", arr+2, arr[2]); // arr[2] : 0x7ffe463f02a7 72
printf("\n");
return 0;
}
4.2 배열의 주소값 연산

#include <stdio.h>
int main(void)
{
char arr[3] = {70, 71, 72};
printf("arr[0] : %p %d\n", arr+0, arr[0]); // arr[0] : 0x7ffe463f02a5 70
printf("arr[1] : %p %d\n", arr+1, arr[1]); // arr[1] : 0x7ffe463f02a6 71
printf("arr[2] : %p %d\n", arr+2, arr[2]); // arr[2] : 0x7ffe463f02a7 72
printf("\n");
printf("arr+0 : %p %d\n", arr+0, *(arr + 0)); // arr[0] : 0x7ffe463f02a5 70
printf("arr+1 : %p %d\n", arr+1, *(arr + 1)); // arr[1] : 0x7ffe463f02a6 71
printf("arr+2 : %p %d\n", arr+2, *(arr + 2)); // arr[2] : 0x7ffe463f02a7 72
return 0;
}
*(arr+ 0) == arr[0]
*(arr+ 1) == arr[0+1]
*(arr+ 2) == arr[0+2] // 배열로 사용하면 * 역참조 연산자가 생략되고, [ ] 연산자가 사용된 형태다.
arr <<<< 포인터, 주소값이 들어있는 배열명인 주소 값은 변경이 불가능한, 상수다.
arr 포인터 상수, 즉 포인터 변수처럼 메모리 주소가 들어있다.
arr[0] 의 주소값(첫번째 바이트 메모리 주소)이다.
5. 다차원 배열
더보기


#include <stdio.h>
int main(void)
{
char arr[6] = {'A', 'B', 'C', 'D'};
printf(" arr+1 : %p %c\n", arr, *arr);
printf(" arr+2 : %p %c\n", arr+1, *arr+1);
printf(" arr+3 : %p %c\n", arr+2, *arr+2);
printf(" arr+4 : %p %c\n", arr+3, *arr+3);
char *pVar = arr;
printf(" pVar+1 : %p %c\n", pVar, *pVar);
printf(" pVar+2 : %p %c\n", pVar+1, *pVar+1);
printf(" pVar+3 : %p %c\n", pVar+2, *pVar+2);
printf(" pVar+4 : %p %c\n", pVar+3, *pVar+3);
char arr2D[3][2] = {'A', 'B', 'C', 'D', 'E', 'F'};
printf(" *arr2D+0 : %p %c\n", *arr2D, **arr2D);
printf(" *arr2D+1 : %p %c\n", *arr2D+1, **arr2D+1);
printf(" *arr2D+2 : %p %c\n", *arr2D+2, *(*arr2D)+2);
printf(" *arr2D+3 : %p %c\n", *arr2D+3, *(*arr2D)+3);
printf(" *arr2D+4 : %p %c\n", *arr2D+4, *(*arr2D)+4);
printf(" *arr2D+5 : %p %c\n", *arr2D+5, *(*arr2D)+5);
// char *pVar = *arr2D;
// char *pVar = arr2D[0];
pVar = &(arr2D[0][0]);
printf(" pVar+0 : %p %c\n", pVar, *pVar);
printf(" pVar+1 : %p %c\n", pVar+1, *pVar+1);
printf(" pVar+2 : %p %c\n", pVar+2, *pVar+2);
printf(" pVar+3 : %p %c\n", pVar+3, *pVar+3);
printf(" pVar+4 : %p %c\n", pVar+4, *pVar+4);
printf(" pVar+5 : %p %c\n", pVar+5, *pVar+5);
char arr3D[2][2][2] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'};
printf(" *arr3D+0 : %p %c\n", **arr3D, ***arr3D);
printf(" *arr3D+1 : %p %c\n", **arr3D+1, ***arr3D+1);
printf(" *arr3D+2 : %p %c\n", **arr3D+2, *(**arr3D)+2);
printf(" *arr3D+3 : %p %c\n", **arr3D+3, *(**arr3D)+3);
printf(" *arr3D+4 : %p %c\n", **arr3D+4, *(**arr3D)+4);
printf(" *arr3D+5 : %p %c\n", **arr3D+5, *(**arr3D)+5);
printf(" *arr3D+6 : %p %c\n", **arr3D+6, *(**arr3D)+6);
printf(" *arr3D+7 : %p %c\n", **arr3D+7, *(**arr3D)+7);
// char *pVar = **arr3D;
// char *pVar = arr3D[0][0];
pVar = &(arr3D[0][0][0]);
printf(" pVar+0 : %p %c\n", pVar, *pVar);
printf(" pVar+1 : %p %c\n", pVar+1, *pVar+1);
printf(" pVar+2 : %p %c\n", pVar+2, *pVar+2);
printf(" pVar+3 : %p %c\n", pVar+3, *pVar+3);
printf(" pVar+4 : %p %c\n", pVar+4, *pVar+4);
printf(" pVar+5 : %p %c\n", pVar+5, *pVar+5);
printf(" pVar+6 : %p %c\n", pVar+6, *pVar+6);
printf(" pVar+7 : %p %c\n", pVar+7, *pVar+7);
return 0;
}
6. 함수와 1차원 배열
#include <stdio.h>
int main(void)
{
int arr_1d[4] = {
0,
};
//1
printf("arr_1d : %p\n", arr_1d);
printf("arr_1d : %p\n", *arr_1d);
//2
printf("sizeof(arr): %d\n", sizeof(arr_1d));
//3
// int *ptr = *arr_1d;
int *ptr = &(arr_1d[0]);
printf("*ptr = &(arr1d[0]): %p\n", ptr);
return 0;
}
#include <stdio.h>
int change_Arr_1d(int *arr)
{
printf("%p\n", arr);
printf("입력하세요 >> ");
scanf("%d", arr +0); // arr[0]
printf("입력하세요 >> ");
scanf("%d", arr + 1); // arr[1]
printf("입력하세요 >> ");
scanf("%d", arr + 2); // arr[2]
printf("입력하세요 >> ");
scanf("%d", arr + 3); // arr[3]
return 0;
}
int print_Arr_1d(int *arr_1d)
{
for (size_t j = 0; j < 4; j++)
{
printf("%d ", arr_1d[j]);
}
printf("\n");
return 0;
}
int main(void)
{
int arr_1d[4] = {
0,
};
printf("%p\n", arr_1d);
change_Arr_1d(arr_1d);
print_Arr_1d(arr_1d);
return 0;
}
8. 함수와 다차원 배열
#include <stdio.h>
int main(void)
{
int arr_2d[4][4] = {
0,
};
//1
unsigned char ucSize = sizeof(arr_2d); // arr_2d 메모리 크기 Byte
unsigned char ucRow = sizeof(*arr_2d); // arr_2d[0] 1차원 크기 Byte
unsigned char ucCol = ucSize/ucRow; // arr_2d 메모리 크기 전체를 1차원 크기로 나누면 컬럼 개수
printf("%d %d %d\n", ucSize, ucRow, ucCol);
//2
printf(" arr_2d: %p %2d\n", arr_2d, sizeof(arr_2d)); //arr_2d[0][0] 의 포인터의 포인터
printf(" *arr_2d: %p %2d\n", *arr_2d, sizeof(*arr_2d)); //arr_2d[0][0] 의 포인터
printf("**arr_2d: %d %2d\n", **arr_2d, sizeof(**arr_2d)); //arr_2d[0][0]
return 0;
}
#include <stdio.h>
int change_Arr_2d(int *arr)
{
printf("%p\n", arr);
printf("입력하세요 >> ");
scanf("%d", arr + 0); // arr[0]
printf("입력하세요 >> ");
scanf("%d", arr + 1); // arr[1]
printf("입력하세요 >> ");
scanf("%d", arr + 2); // arr[2]
printf("입력하세요 >> ");
scanf("%d", arr + 3); // arr[3]
return 0;
}
int print_Arr_2d(int *arr_2d)
{
unsigned char ucSize = sizeof(arr_2d);
unsigned char ucRow = sizeof(*arr_2d);
unsigned char ucCol = ucSize/ucRow;
printf("%d %d %d\n", ucSize, ucRow, ucCol);
unsigned char cnt = 0;
for (size_t i = 0; i < 4; i++)
{
for (size_t j = 0; j < 4; j++)
{
printf("%d ", *(arr_2d+cnt));
// printf("%d ", (*arr_2d)[j]);
cnt+=1;
}
printf("\n");
}
return 0;
}
int main(void)
{
int arr_2d[4][4] = {
0,
};
//3
int *ptr = &(arr_2d[0][0]);
change_Arr_2d(*arr_2d);
//4
// print_Arr_2d(*arr_2d);
print_Arr_2d(ptr);
return 0;
}