배열과 포인터는 연산자 & * [ ] 만 이해하면 된다.
그리고, 배열과 포인터는 다르다.
1. 필수 기반 지식
- 프로그래밍과 빌드, 실행의 이해
- bit, Byte, 2진수, 16진수, 기본 자료형 이해
- 컴퓨터 실행의 기본 동작과 장치(cpu, memory, storage) 이해
- 함수와 스텍 메모리 이해
2. 포인터의 선언, 변수 사용, 역참조 구분
#include <stdio.h>
int main(void)
{
char cNum = 65; // char형 변수 cNum 선언
char *pVar1 = &cNum; // 포인터 변수 pVar 선언, char형 포인터 연산가능한
printf(" pVar1: %p\n", pVar1); // 포인터 사용, 변수명만 사용, 주소값 의미
printf("*pVar1: %d\n", *pVar1); // 포인터 역참조, 포인터(주소)가 가르키는 공간에 저장된 값 의미
int iNum = 90;
int *pVar2 = &iNum;
printf(" pVar2: %p\n", pVar2);
printf("*pVar2: %d\n", *pVar2);
return 0;
}
2.1 변수 선언 이해
- char 자료형 크기만큼 메모리를 사용하고, 65 라는 값을 저장하여, cNum이라는 변수명으로 참조하겠습니다.
- int 자료형 크기만큼 메모리를 사용하고, 90 라는 값을 저장하여, iNum이라는 변수명으로 참조하겠습니다.
- 컴파일러는 변수명이 필요없다.
2.2 포인터의 자료형 선언 부분 이해
- 포인터가 의미하는 값, 모든 메모리 주소 크기는 일정하다. == 메모리 주소 저장에 필요한 크기는 일정하다.
기본 자료형을 사용할때 처럼, 데이터 타입을 명시할 이유가 없다.
Q. 자료형을 명시하여 사용하는 이유는?
Q. 포인터 선언시, 자료형을 명시하여 사용하는 이유는?
- 포인터 변수의 사용은, 포인터 변수가 가지는 공간의 값, 메모리 주소값을 의미한다.
printf(" pVar1: %p\n", pVar1); // 포인터 사용, 변수명만 사용, 포인터 변수가 저장한 주소값 의미한다.
- 표인터 변수 선언
int *pVar = &iNum; // 주소값을 담는 포인터 선언, 변수명 앞에 * 에스터리스크 사용한다.
- 표인터 변수 사용, 역참조 구분
pVar // 포인터 변수를 사용한 때는 변수만 사용한다.
*pVar // 포인터 변수에, 역참조 연산자가 추가되면, 주소값을 참조하여 포인터 변수 선언시 사용한 자료형의 크기만큼 참조한다.
2.3 정리
변수 → & → 포인터(주소) → * → 데이터
(&변수 ) → (* 포인터(주소)) → 데이터
*(&변수 ) → 데이터
*&변수 → 데이터
변수 → 데이터
iNum → & → pVar → * → 90
(&iNum ) → (* pVar) → 90
*(&iNum ) → 90
*&iNum → 90
iNum → 데이터
3. 주소값은 주소값일 뿐이다.
포인터는 포인터다.
#include <stdio.h>
int main(void)
{
int iNum2 = 89; // int형 변수를 선언
char* pVar3 = (char*)&iNum2; // &iNum2 int형 포인터 주소값을, char형 포인터로 변환
printf(" pVar3: %d\n", *pVar3); // char형 크기만큼만 연산한다. 주소값은 주소값일 뿐이다.
return 0;
}
4. 포인터 연산
포인터 선언시 사용한 자료형의 크기만큼, (자료형*숫자)Byte 연산한다.
예시의 변수는 순차적으로 저장된다는 가정이지만, 배열은 연속되고 순차적인 특성을 가진다.
#include <stdio.h>
int main(void)
{
char cNum2 = 70, cNum3 = 71, cNum4 = 72;
char *pVar2 = &cNum2, *pVar3 = &cNum3, *pVar4 = &cNum4;
printf(" pVar2: %p %d\n", pVar2, *pVar2); // pVar2: 0061FF13 70
printf(" pVar3: %p %d\n", pVar3, *pVar3); // pVar3: 0061FF12 71
printf(" pVar4: %p %d\n", pVar4, *pVar4); // pVar4: 0061FF11 72
// 메모리에 값이 위 주석처럼, 순차적으로 저장된다고 가정할 때
// (참고) 위의 메모리 주소값을 확인해서 연산을 수정해야 할 수 있다.
// printf(" pVar4+2: %p %d\n", pVar4+2, *(pVar4+2)); // pVar4+2: 0061FF13 70
// printf(" pVar4+1: %p %d\n", pVar4+1, *(pVar4+1)); // pVar4+1: 0061FF12 71
// printf(" pVar4 : %p %d\n", pVar4, *pVar4); // pVar4 : 0061FF11 72
return 0;
}
#include <stdio.h>
int main(void)
{
int iNum2 = 81, iNum3 = 82, iNum4 = 83;
int *pVar2 = &iNum2, *pVar3 = &iNum3,*pVar4 = &iNum4;
printf(" pVar2: %p %d\n", pVar2, *pVar2); // pVar2: 0061FF10 81
printf(" pVar3: %p %d\n", pVar3, *pVar3); // pVar3: 0061FF0C 82
printf(" pVar4: %p %d\n", pVar4, *pVar4); // pVar4: 0061FF08 83
// 메모리에 값이 저장될 때, 위 주석처럼 순차적으로 저장되었다고 가정할 때
// (참고) 위의 메모리 주소값을 확인해서 연산을 수정해야 할 수 있다.
// printf(" pVar4+2: %p %d\n", pVar4+2, *(pVar4+2)); // pVar4+2: 0061FF10 81
// printf(" pVar4+1: %p %d\n", pVar4+1, *(pVar4+1)); // pVar4+1: 0061FF0C 82
// printf(" pVar4 : %p %d\n", pVar4, *pVar4); // pVar4 : 0061FF08 83
return 0;
}
5. 포인터와 1차원 배열
같다며?
*(ptr_num + 0) == ptr_arr[0]
*(ptr_num + 1) == ptr_arr[0+1]
*(ptr_num + 2) == ptr_arr[0+2] // 배열로 사용하면 * 역참조 연산자가 생략되고, [ ] 연산자가 사용된 형태다.
ptr_num + 0
ptr_num + 1
ptr_num + 2
ptr_num + 3
char ptr_arr[5] = {'A', 'B', 'C', 'D', 'E'};
#include <stdio.h>
int main(void)
{
char arr[5] = {'A', 'B', 'C', 'D', 'E'};
printf(" arr+0 : %p %c\n", arr, *arr);
printf(" arr+1 : %p %c\n", arr+1, *arr+1);
printf(" arr+2 : %p %c\n", arr+2, *arr+2);
printf(" arr+3 : %p %c\n", arr+3, *arr+3);
printf(" arr+4 : %p %c\n", arr+4, *arr+4);
char *pVar = arr;
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);
return 0;
}
arr <<<< 포인터, 주소값이 들어있는, 포인터 변수, 값인 주소는 변경이 불가능한, 상수 포인터
arr 포인터 상수, 즉 포인터 변수처럼 메모리 주소가 들어있다.
arr[0] 의 주소값이다.
6. 포인터와 다차원 배열, 그리고 자료형 연산
#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;
}
7. 함수와 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;
}
--
2차원 배열
char arr[2][3]
char arr[6]
함수 원형에서, 매개변수 자리는 그냥 주소값만 넘기는거다.
int func(int *arr){
}
&(arr_1d[0]) // == arr_1d // int *arr 크기만큼의 주소값을 넘긴다는 의미다,
&(arr_2d[0][0]) // == arr_2d[0] // int *arr 크기 연산 가능한 주소값
배열의 대표 주소와
행의 대표 주소 carr2[0][0] carr2[1][0]