함수
함수를 사용하는 이유
함수는 긴 코드를 분리하여 처리하기 위한 목적으로 가독성을 향상시킬 수 있고, 유지 보수나 확장에 용이합니다. 때문에 함수는 코드에서 없어서는 안되는 존재가 됐는데요, 요즘 클래스형 코드에서 함수형 코드로 많이 바뀐 이유도 그 이유입니다. 대부분의 프로그래밍 언어들은 함수의 집합체라고도 할 수 있는데요. 함수는 서로 연동되기도 유기적으로 동작하기도 합니다.
함수의 종류
함수는 크게 표준함수와 사용자 정의함수로 나뉩니다.
표준 함수는 언어에서 기본적으로 제공되는 함수를 말하는데요. 우리가 사용하는 console.log, printf와 같은 것을 말합니다. 표준함수는 사용하다보면 기능적으로 한계가 존재하는데요 이때 사용자 정의 함수를 사용하기도 합니다. 사용자 정의함수는 표준함수처럼 기본적으로 제공되는게 아닌 사용자가 원하는 기능을 위해 직접 만드는 함수를 말합니다.
함수의 형태
자료형 함수이름 (매개변수) {
만들고자 하는 코드
}
int Add( int a, int b ) {
return a + b;
}
자료형 : 함수가 return 하고자 하는 값의 타입 (cf. void 타입 = 결과값을 리턴하지 않는 함수)
매개변수 : 매개변수는 인수라고도 불리고 파라미터라고도 불리며 여러개가 올수도 있습니다.
배열
배열이란?
같은 속성을 가진 요소들이 순서대로 들을 나열해 놓은것을 배열이라고 말합니다.
그렇다면 왜 배열을 사용을 하는걸까요? 그 이유는 정수형 변수 10개를 생성한다고 합니다.
int a;
int b;
...
int j;
int a[10];
코드를 보면 어떤게 더 잘 보이나요? 정수형 변수 10개를 만들게되면 배열을 사용하지 않으면 직접 하나하나 다 선언을 해줘야 했지만 배열을 사용하게 되면 한줄로 변수 10개를 만들수 있게 됩니다. 배열을 사용하면 변수가 10개, 100개 갯수가 늘어나더라도 간단하게 선언이 가능합니다.
배열의 구조
int array[10];
배열의 타입, 배열이름, 배열길이 이 3가지를 이용하여 배열을 생성할수 있는것입니다.
배열의 타입 | 배열 요소들의 타입을 지정합니다. |
배열의 이름 | 배열 요소에 접근할수 있는 이름을 말합니다. |
배열의 길이 | 변수의 갯수 |
이렇게 했을때, 예시를 보면 int 타입의 데이터고, 배열의 이름은 array며 배열의 갯수는 총 10개 인것을 알 수 있습니다.
이렇게 만든 배열은 어떻게 메모리에 저장이 될까요?
array[0] | array[1] | array[2] | array[3] | array[4] | array[5] | array[6] | array[7] | array[8] | array[9] |
이와 같은 형태로 저장이 되게 됩니다. 이 배열 요소의 타입은 int이기 때문에 각각의 메모리는 4byte를 갖고 배열 전체의 메모리 할당은 40byte가 됩니다.
배열에 접근하기 위해서는 배열의 인덱스를 이용하는데요, 배열이름[인덱스] 이렇게 해서 접근을 할수 있습니다.
array[5] 라고 했을때 배열을 선언하는건지, 6번째 array를 말하는건지 알수있는건 배열 타입 유무라고 생각하시면 조금 더 편하실것 같습니다.
int array[10];
array[0] = 1;
array[1] = 2;
...
array[9] = 10;
이와같이 배열에 접근을 하여 값을 할당할수 있습니다. 그렇다면 초기값을 할당을 하려면 일일이 하나하나 접근해서 하나하나 값을 넣어주어야 할까요? 답은 아래처럼 초기값을 할당할수 있습니다.
int array[10] = {1,2,3,4,5,6,7,8,9,10};
이렇게 설정을 하게 되면 array[0]에는 1, array[1]에는 2, ... , array[9]에는 10이 들어가있는걸 알수있습니다.
그렇다면 몇개의갯수가 들어올지 모르는 배열을 생성한다고 했을 때 어떻게 배열을 생성해야 할까요?
정답은 바로 배열의 갯수를 입력하지 않고, 초기값의 갯수로 컴파일러가 자동으로 배열의 길이를 계산하게 하는것입니다.
int array[] = {1,2,3}; // 배열의 길이 3
char array2[] = {h,e,l,l,o, ,w,o,r,l,d}; // 배열의 길이 11
배열의 복사
int arr1[5] = {1,2,3,4,5};
int arr2[5];
arr2 = arr1;
배열도 변수이기 때문에 복사가 가능합니다. 하지만 변수처럼 복사를 하게되면 에러가 발생하게 됩니다.
그 이유는 배열을 선언을 하게되면 배열은 상수이기 때문에 대입연산자로 값을 넘겨줄수 없습니다.
그래서 배열의 복사는 배열의 "요소"끼리 복사해야 복사가 가능합니다.
int arr1[5] = {1,2,3,4,5};
int arr2[5];
for(int i = 0; i<5; i++)
arr2[i] = arr1[i];
문자열 변수
문자열에 이름을 붙혀주면 변수로도 사용이 가능한데요.
char str[] = "hello world";
위 변수를 확인하면 11개의 문자열이 확인 이 되는데요 실제 배열의 길이는 총 12개가 나오게 됩니다. 그 이유는 문자열의 맨 마지막에는 null값이 존재하기 때문입니다.
null값이 존재하는 이유
사람은 문자와 빈값을 구분을 하지만 컴퓨터는 구분을 할 수 없습니다. 이때 문자열의 끝에 null을 넣어 컴퓨터가 문자의 끝을 인식을 하는데요 이 때문에 문자열 마지막에는 null값이 들어가게 되는것입니다.
포인터
포인터는 포인터 변수의 줄임말로 쉽게말해 메모리에 주소값을 저장하고 있는 변수를 말합니다.
포인터 사용하기
포인터 변수를 선언시에는 변수 앞에 포인터기호(*)를 붙혀주면 해당변수에 주소값만 저장하겠다는 의미를 가집니다.
또한 & 기호를 통해 변수의 주소값을 얻을수도 있습니다.
int num = 10;
int *pNum = #
위 코드를 이야기 해봅시다.
우리가 num이라는 변수를 선언을 했고 해당값이 10이라고 할당되어있습니다.
이때 메모리에 0X100이라는 주소값에 4바이트 크기로 변수가 저장이 되어있는데요.
*pNum이라는 변수는 num변수의 주소값을 저장을 했다라는 뜻이 됩니다. 즉, *pNum이라는 변수는 0X100이라는 주소값을 갖게된것입니다.
그렇다면 만약에 주소값이 아닌 그냥 num을 할당하면 어떻게될까요? 우리는 이미 *기호를 통해서 주소값을 저장하겠다 하였는데 일반 값이 넘어오게되면 에러가 발생하게 됩니다.
포인터와 배열
배열을 선언을 하면 배열의 주소값은 배열[0]의 주소값과 일치합니다. 즉, 배열은 첫번째 요소의 주소값을 저장하는 포인터라는 뜻입니다.
배열이 포인터라고 했는데 일반 포인터와 차이가 존재하는데요. 위 배열의 복사에서 배열을 복사 하려면 상수기 때문에 대입연산자로 값을 넣어줄수 없다 라고 했는데요. 이게 포인터와도 연관이 있습니다. 바로 배열은 그냥 상수가 아니라 포인터 상수이기 때문에 주소값을 변경할수 없어서 다른 값을 변경할 수 없게 되는 것입니다.
포인터와 함수
void main() {
temp(a);
}
void tmep(b) {
}
위 함수에 서 a와 b는 같은 메모리에 위치한 값을 쓰는걸까요? 아닙니다. a와 b는 같은 값이지만 메모리에 따로 할당이 되는데요. 이는 바로 포인터와 관련이 있습니다.
함수에서 매개변수를 전달하는 형태는 두가지로 나뉘는데 첫번째 ,값에 의한 복사, 두번째, 참조에 의한 복사 이렇게 나뉘게 됩니다.
값에 의한 복사는 말그대로 값을 복사하기 때문에 a와 동일한 메모리가 아니라 b라는 새로운 메모리를 생성을 합니다. 때문에 해당 값을 변경을하게되면 b라는 새로운 메모리의 값에는 변경이 되고, a 메모리에 저장된 값이 변경이 되지 않는 것입니다.
참조에 의한 복사는 주소값을 복사하기 때문에 a와 동일한 메모리를 사용합니다. 그래서 메모리의 값을 변경을 하면 a 메모리가 변경이 되는것을 확인할 수 있습니다.
'프로그래밍📚' 카테고리의 다른 글
사용자 정의 타입이란? - 공용체와 enum (0) | 2024.02.05 |
---|---|
사용자 정의 타입이란? - 구조체 (0) | 2024.02.05 |
함수포인터란? (0) | 2024.02.04 |
연산자와 조건문, 반복문 (0) | 2024.01.28 |
변수와 상수 그리고 자료형 (0) | 2024.01.22 |