함수를 쓰는 이유는 재사용성 때문이다.
코드를 디자인하는 것. 33가지가 있다.
모듈화가 되어있지 않으면 유지보수가 힘들다.
이럴 때는 이런 형태로 코드를 짜니까 장점이 있다 하는 것을 패턴화 시킨 것.
대표적인 것이 MVC 패턴이다. (C# - MVV, 파이썬 - MTV)
모듈화 시키는것. Model View Vontroller를 나눠놓는 것.
왜 나눌까?
서버 코드와 UI 코드가 있다고 예시를 들면 서버 개발자는 서버 코드만, 디자이너는 UI코드만 볼 수 있게 분리하여 서로의 코드를 간섭할 수 없게 하는 것.
기능을 함수화 시키고 코드의 입력과 출력 파트를 나누는 것부터가 프로그래밍 사고를 하는 것이다.
최소화의 모듈로 분리를 하는 것.
코드는 최대한 간결하게, 모듈화 시키는 것! 무조건 작게, 함수화 한다.
해당 메모리의 주소값을 가진 변수
메모리에 주소값이 있는 이유?
해당 메모리에 접근하기 위해서는 주소값이 반드시 필요하기 때문에!
해당 메모리의 주소값!
선언을 제외한 나머지에서 사용되는 *은 메모리의 실제 값을 나타낸다.
주소 + 1 = 다음 주소
주소 + 2 = 다음 다음 주소
포인터는 주소값이기 때문에 char, int, double로 할당해도 4바이트로 저장된다.
주소값의 본질은 정수이다.
정수를 저장하기 위한 메모리는 무조건 4바이트다.
따라서 4바이트를 할당한다.
주소값은 어떤 타입이든 상관없이 포인터 변수에 대입이 가능하지만 포인터 변수가 실제 값을 참조할 때 문제가 생긴다.
포인터 변수와 메모리 주소의 타입이 맞아야 접근하려는 메모리의 실제 값을 읽을 수가 있는 것이다.
따라서 포인터 변수의 앞에 붙는 타입은 포인터변수가 가르키고 있는 메모리의 실제 값을 참조하기 위한 메모리 타입이다! 포인터 자신의 타입이 아니다.
char a = 10;
int *ptr;
ptr = &a;
메모리 주소값을 포인터 변수에 대입은 가능하나
*ptr
실제값 참조는 불가능하다.
#include<stdio.h>
int main(void)
{
char a = 'A';
char* pA = &a;
int b = 100;
int* pB = &b;
double c = 3.14;
double* pC = &c;
printf("pA의 크기 : %dbyte\n", sizeof(pA));
printf("pB의 크기 : %dbyte\n", sizeof(pB));
printf("pC의 크기 : %dbyte\n\n", sizeof(pC));
printf("*pA의 크기 : %dbyte\n", sizeof(*pA));
printf("*pB의 크기 : %dbyte\n", sizeof(*pB));
printf("*pC의 크기 : %dbyte\n", sizeof(*pC));
return 0;
}
출력결과
pA의 크기 : 8byte
pB의 크기 : 8byte
pC의 크기 : 8byte
*pA의 크기 : 1byte
*pB의 크기 : 4byte
*pC의 크기 : 8byte
32비트 기준으로 4바이트이기 때문에 64비트 환경에서는 8바이트, 32비트 환경에서 4바이트가 나온다.
pA, pB,pC는 각각 변수 a,b,c를 가리키고 있다.
저장 공간이 4바이트인 포인터 변수에 왜 char형, int형, double형으로 나누었는가?
그 이유는 각각의 포인터 변수가 가리키는 변수들의 실제값을 참조하기 위함이다.
#include<stdio.h>
int main(void)
{
int arr[] = { 1,2,3,4,5 };
int i;
printf("배열의 요소 출력 : ");
for (i = 0; i < 5; i++)
{
printf("%d", arr[i]);
}
printf("\n\n");
printf("배열의 주소 출력 :");
for (i = 0; i < 5; i++)
{
printf("%d", &arr[i]);
}
printf("\n\n");
printf("배열의 이름 출력 : %d\n", arr);
return 0;
}
출력결과
배열의 요소 출력 : 12345
배열의 주소 출력 :-707789800-707789796-707789792-707789788-707789784
배열의 이름 출력 : -707789800
배열의 이름이 배열의 첫 번째 주소 값과 같다는 것의 의미는 무엇인가?
배열의 이름은 이 배열의 첫 번째 요소의 주소값을 가리키는 포인터라는 의미이다.
arr은 이 배열을 가리키고 있는 포인터임을 알 수 있다.
arr은 &arr[0]와 값이 일치한다고 할 수 있다.
배열의 이름 == 포인터
일반 포인터와 결정적인 차이점이 있다?
int main(void)
{
int arr[] = { 1,2,3,4,5 };
int temp = 10;
arr = &temp;
return 0;
}
배열의 이름은 주소를 가리키는 포인터이되, 상수이므로 주소값을 변경할 수 없다.
arr는 상수(변하지 않는 수)이면서 포인터인 포인터 상수다.
배열은 컴퓨터가 임의로 주소값을 정해준 상수이기 때문에.
#include<stdio.h>
int main(void)
{
int arr[] = { 1,2,3,4,5 };
printf("배열의 요소 출력 : ");
for (int i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
printf("배열의 이름을 이용한 출력 : \n");
for (int i = 0; i < 5; i++)
{
printf("%d ", *(arr + i));
}
return 0;
}
출력결과
배열의 요소 출력 : 1 2 3 4 5
배열의 이름을 이용한 출력 : 1 2 3 4 5
메모리에 저장은 안되지만 일반 포인터와 동일하게 사용할 수 있다.
arr은 배열의 첫 번째 요소의 주소값을 나타내므로 1씩 증가시키면 다음 주소값으로 넘어간다.
#include<stdio.h>
int main(void)
{
int arr[] = { 1,2,3,4,5 };
int *pTemp;
pTemp = arr;
printf("배열의 요소 출력 : ");
for (int i = 0; i < 5; i++)
{
printf("%d ", pTemp[i]);
}
return 0;
}
출력결과
배열의 요소 출력 : 1 2 3 4 5
pTemp는 배열의 첫 번째 요소를 가리키게 된다.
pTemp 포인터를 배열처럼 사용할 수 있다.
2차원 배열만 충분히 이해하고 있으면 3, 4차원 배열 또한 이해가 수월하다.
2차원 배열의 메모리 구조
위의 1차원 배열과 똑같은 메모리 구조를 갖는 녀석을 하나 더 갖고 싶다면?
1차원 배열에서 길이가 3인 배열을 또 하나 만든 것이다.
수학의 행렬, 데이터베이스의 Row/Col 개념과 비슷하다.
배열로 표현하면 int arr[2][3]이다.
2차원 배열을 선언하는 형태는 다음과 같다.
int arr[i][j];
길이가 j인 int형 배열을 i개 모아놓은 배열을 생성하라.
마트의 야쿠르트 묶음을 생각하라.
j값이 늘어나면 배열의 길이가 커지는 것이고, i가 늘어나면 배열 자체가 늘어나는 것이다.
#include<stdio.h>
int main(void)
{
int i, j;
int arr[2][3];
arr[0][0] = 1;
arr[0][1] = 2;
arr[0][2] = 3;
arr[1][0] = 4;
arr[1][1] = 5;
arr[1][2] = 6;
printf("2차원 배열 값의 출력 결과\n");
printf("====================\n");
for (i = 0; i < 2; i++)
{
for (j = 0; j < 3; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
printf("====================\n");
return 0;
}
출력결과
2차원 배열 값의 출력 결과
====================
1 2 3
4 5 6
====================
2차원 배열 int[2][3] 선언 시 총 2 x 3 개의 메모리 공간이 생성
int array[2][3] = {
{ 1,2,3 },
{ 4,5,6 }
};
1차원 배열처럼 중괄호 구분 없이 초기화 해주어도 결과는 같다.
int array[2][3] = { 1,2,3,4,5,6 };
int array[2][3] = {
{ 1, },
{ 4,5,6 }
};
1행의 경우 1열만 초기값이 존재하고, 2,3열은 생략된 형태이다.
특정 행의 나머지 요소들을 0으로 초기화하고 싶을 때는 굳이 0이라고 표기하지 않고 생략하면 0으로 초기화된다.
1행 초기값 1의 끝의 콤마는 1 다음의 나머지 요소는 모두 0으로 초기화하겠다는 것을 분명히 표시하는 것.
int array[2][3] = {
{ 1,2,3 },
{ 4,5,6,7 }
};
1차원 배열과 마찬가지로 컴파일 오류가 난다.
int array[][3] = {
{ 1,2,3 },
{ 4,5,6 }
};
첨자 길이를 생략해도 초기식에서 행이 두 개이므로 첨자의 길이가 2라는 것을 알 수 있다.
헷갈리는 부분
이중 반복문에서 arr[i][j]는 가로로 진행 ⇒ (0,1) (0,2) (0,3)...
arr[j][i]는 세로로 진행 ⇓ (1,0) (2,0) (3,0)...
포인터 배열은 포인터 변수가 배열 형태로 존재.
배열 포인터는 배열의 번지를 담고 있는 포인터를 의미.
포인터가 가리키는 대상은 배열 형태로 구성.
포인터가 가리키는 배열의 요소는 임의의 자료형을 갖는다.
배열의 번지를 담고 있는 포인터.
참고
두 단어를 합친 용어의 경우 앞 단어는 특징, 뒷 단어는 실체라고 이해하면 된다.
포인터 배열 : 포인터는 특징, 배열이 실체 (아~ 배열이구나)
배열 포인터 : 배열이 특징, 포인터가 실체 (아~ 포인터구나)