함수 생성
<mallloc.h> // 동적할당에 관한 함수가 정의된 헤더파일
#include <stdlib.h> // -> malloc.h가 포함된 헤더파일
예시코드(1) 지난 시간에 봤던 코드
지난 시간에 짰던 코드를 기능 별로 구분해서 함수를 제작해보자.
#include<stdio.h>
#include <stdlib.h>
int main(void) {
int su;
printf("크기 입력 : ");
scanf_s("%d", &su);
int * pt = (int *)malloc(sizeof(int) * su);
for (int i = 0; i < su; i++) {
printf(" 숫자 입력 : ");
scanf_s("%d", pt + i);
}
printf("\n\n짝수\n");
for (int i = 0; i < su; i++) {
if (pt[i] % 2 == 0) {
printf("%d번방 : % d\n", i, pt[i]);
}
}
free(pt);
return 0;
}
코드(2) 함수 제작
함수를 크기입력 / data 입력 / 짝수구분 이렇게 나눠보자.
반환자료형이란 ? -> 함수를 호출하고 나서 내가 함수 안에서 만들어진 결과물을 이용해야할 때,정수값 입력.
#include<stdio.h>
#include <stdlib.h>
int main(void) {
#include<stdio.h>
#include <stdlib.h>
int input() {
int su;
printf("크기 입력 : ");
scanf_s("%d", &su);
return su;
}
void dataInput(int *pt , int su) {
for (int i = 0; i < su; i++) {
printf(" 숫자 입력 : ");
scanf_s("%d", pt + i);
}
}
void evenDisp(int *pt, int su) {
printf("\n\n짝수\n");
for (int i = 0; i < su; i++) {
if (pt[i] % 2 == 0) {
printf("%d번방 : % d\n", i, pt[i]);
}
}
}
int main(void) {
//크기 입력
int su = input();
//공간 생성
int * pt = (int *)malloc(sizeof(int) * su);
//data 입력
dataInput(pt, su);
//짝수 구분
evenDisp(pt, su);
//공간 해제
free(pt);
return 0;
}
쉽게 생각하면 각각의 기능대로 나눠서 함수를 만들고 반환값 여부를 위아래로 따져본다.
매개변수라는 건 함수를 호출했을 떄, 가지고 있어야 할 것. 즉 함수 호출 이전의 것을 신경써야하며
리턴값은 함수 호출 이후를 살펴봐야 한다.
익숙해지면 함수형태를 왠만큼 적어서 프로그램을 짜기 전에 함수를 만들어볼 수 있다.
함수명
함수명을 지을 때 자바는 첫글자 소문자, 달라지는 단어 대문자. 암묵적인 규칙이 있다. c 표준 함수는 모두 소문자.
포인터를 어려워 하는 이유
포인터는 변수다. 변수는 정수, 실수, 문자를 저장하고 포인터는 주소값이라는 걸 저장해서 사용한다.
사람들이 어려워 하는 이유 -> 변수를 배우고나서 포인터를 배우니까. 포인터와 변수가 다르다고 생각하는데 사실 같다.
변수와 배열의 차이 : 저장할 공간의 갯수
알고있는 거에서 추가로 더해가는 것. 이질감 느끼지마
각자 어떻게 사용해야 하는지, 왜 쓰는지에 대한 기준이 있어야 한다.
포인터 변수 -> 주소를 저장 포인터 배열 -> 주소를 저장하는 배열 이중 포인터 -> 포인터를 저장 (포인터 변수를 가리키는 포인터)
포인터는 주소에 해당하는 공간의 접근 및 제어를 위해서 사용한다.
포인터 -> 메인에서 쓰기 보다는 함수의 매개변수로써 주로 이용. 메인에서 쓴다면 목적있다. 위의 예제에서 int * pt가 없다면 동적할당 받은 것을 접근할 수 있는 공간이 없다. 각각의 주소가 필요한 상황이니까.
메인에서 포인터를 만드는 경우는 공간을 만들었는데 주소를 저장하고 싶을 때다. 그 공간을 주기적으로 접근하기 위한 용도로 포인터 변수에 저장을 한거다.
(매개변수는 함수 안에 있는 지역변수라고 보면 된다. 공간 자체가 아예 다르다.)
선언 / 초기화/ 호출
선언 -> 메모리 공간을 할당받고 그 공간에 이름을 붙이는 작업
초기화 -> 할당받은 공간에 값을 처음 넣는 작업ㅈ
호출 -> 힐당받은 공간을 불러오는 작업
a라는 변수에 ?가 담겨있고 b라는 변수에 10이 담겨있다고 가정.
호출을 한다는 건 ?가 아닌 a를 불러온다는 거다.
하지만 비교를 한다는 건 공간 안의 데이터를 비교한다는 거다. a< b, a > b 와 같다
a가 값이 복사되서 함수로 들어가니까 printf(“%d\n”, a); a공간 안의 데이터를 꺼내서 쓴다는 것을 의미. 즉 공간 호출.
※ 선언과 초기화 ( 입력을 받으려고 하면 초기화를 해줄 필요가 없고, sum이란 공간에 데이터를 넣으려고 하면 sum 안에 데이터가 없으므로 초기화를 시켜줘야 한다.)
int 와 int * 둘다 형식 지정. 즉 공간의 형식을 정의했다. 포인터 안에는 공간의 주소값이 들어가는 거고
Q) 그렇다면 포인터를 선언할 때 자료형을 왜 선언해줄까? 포인터를 선언한 공간의 자료형을 알아야하니까. a라는 변수에 ?이 담겨있고 그 변수의 주소값은 100이라고 하자 pt라는 포인터변수에 100이 담겨있다. 즉 pt -> A를 가리키고 있다. 저 상태에서 A 공간의 형식을 모르잖아. 따라서 시작주소 만큼의 4바이트라고 알려주고 있는 것. 그래야 간접참조 연산자를 사용해서 값을 불러올 수 있잖아.
Q)내가 질문한 것 *pt의 크기는 다 고정되냐. 다르다. 32비트, 64비트마다 다르다. 포인터의 크기는 시스템에 따라 다르다. 32비트일 때 4바이트, 64비트일 때는 8바이트의 주소체계를 가지게 된다. 포인터변수는 주소값을 저장하니까. 주소체계에 따라서 그 크기가 달라진다.
NULL 포인터
NULL이 정의되어 있는 곳에 가서 확인해보면
#define NULL (void*)\0
(예전에는 이렇게, 지금은 조금 다름)
NULL이란 값을 주로 0이라고 설명 했는데 아스키 코드 값이 0번이다. 그런데 앞에 void pointer가 붙었잖아. 결국은 주소값이란 이야기. 이게 포인터에 들어가면 0x00000000 주소. 컴퓨터 상에서 0번지 주소에는 어떤 값이 안들어가진다. 이게 바로 NULL 포인터다.
포인터 변수 안에는 쓰레기 값이 들어가 있다. if문에 조건없이 그냥 쓰레기 값이 들어가면 어떻게 될까?
c언어에서 입력되는 데이터에 0이 아닌 값은 모두 참이다. 따라서 쓰레기값이 들어가면 참이된다. 그래서 포인터는 초기화가 중요하다.
호출
*pt pt라는 공간을 호출하는 건 맞는데 *연산자는 뭘까. -> 간접참조연산자다. *의 역할은 주소값에 해당하는 공간을 참조하는 연산자다. 이 때 그 참조라는 말이 결국 호출을 의미한다.
// 포인터의 선언 내가 가리키고자 하는 공간의 자료형 * 포인터명; 즉 포인터 타입 포인터명; 이런 식.
// 포인터의 호출 포인터의 호출은 변수 호출과 같다. *(간접참조연산자): 주소값에 해당하는 공간을 참조(호출)하는 연산자
pt *pt &pt
위 세개. 딱딱 끊어서 생각해야한다. &는 주소연산자. &pt란 pt공간을 호출해서 &을 붙었으니, 주소. 즉 pt의 주소를 계산한다.
*&a 그냥 변수 a에 _a라고 하면 에러가 난다. 그런데 &를 붙이면 사라진다. _&a란 주소를 계산하고 거기에 간접참조연산자를 붙이니까. *는 주소가 있다면 다 쓰일 수 있는거다.
참고자료
kg아이티뱅크학원 (자료구조 수업)