220901 C언어#10

김혜진·2022년 9월 1일
0

C언어

목록 보기
10/13
post-thumbnail

C언어 #10

함수를 쓰는 이유는 재사용성 때문이다.

디자인 패턴

코드를 디자인하는 것. 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차원 배열

2차원 배열의 이해

2차원 배열만 충분히 이해하고 있으면 3, 4차원 배열 또한 이해가 수월하다.

2차원 배열의 메모리 구조

위의 1차원 배열과 똑같은 메모리 구조를 갖는 녀석을 하나 더 갖고 싶다면?

1차원 배열에서 길이가 3인 배열을 또 하나 만든 것이다.
수학의 행렬, 데이터베이스의 Row/Col 개념과 비슷하다.
배열로 표현하면 int arr[2][3]이다.

2차원 배열의 선언

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 개의 메모리 공간이 생성

2차원 배열의 초기화

배열의 모든 요소들을 초기화 하는 경우

  • 배열의 길이와 초기값의 개수가 일치하는 경우
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)...


배열 포인터

  • 포인터 배열은 포인터 변수가 배열 형태로 존재.

  • 배열 포인터는 배열의 번지를 담고 있는 포인터를 의미.
    포인터가 가리키는 대상은 배열 형태로 구성.
    포인터가 가리키는 배열의 요소는 임의의 자료형을 갖는다.
    배열의 번지를 담고 있는 포인터.

  • 참고
    두 단어를 합친 용어의 경우 앞 단어는 특징, 뒷 단어는 실체라고 이해하면 된다.
    포인터 배열 : 포인터는 특징, 배열이 실체 (아~ 배열이구나)
    배열 포인터 : 배열이 특징, 포인터가 실체 (아~ 포인터구나)

profile
알고 쓰자!

0개의 댓글