반환값자료형 함수이름()
{
코드;
}
/*--------------------------*/
void hello()
{
printf("hello");
}
hello();
void hello(); // 함수 원형 선언
int main()
{
hello();
return 0;
}
void hello() // 함수 정의
{
printf("hello");
}
int num()
{
return 1;
}
num1 = num(); // num1에 1이 들어감
포인터도 반환 가능
반환값자료형 *함수이름()
{
return 반환값;
}
잘못된 예시
int *num()
{
int num1 = 10; // num1은 지역 변수라서 함수가 끝나면 사라진다.
return &num1; // 함수가 끝나면 사라지는 지역 변수의 주소를 반환하는 것은 잘못되었다.
}
int *numPtr;
numPtr = num();
printf("%d", *numPtr); // 이미 사라진 변수를 출력하고 있다.
올바른 사용
int *num()
{
int *numPtr = malloc(sizeof(int));
*numPtr = 10;
return numPtr; // 포인터 반환, malloc으로 메모리를 할당하면 함수가 끝나도 사라지지 않는다.
}
int *numPtr;
numPtr = num();
printf("%d", *numPtr); // 10 출력
free(numPtr); // 다른 함수에서 할당한 메모리라도 반드시 해제를 해주어야 한다.
문자열의 경우 코드에서 입력한 문자열 리터럴은 실행될 때 메모리에 저장되어 함수가 종료되더라도 계속 사용이 가능하다.
char *chLiteral()
{
char *s1 = "hello";
return s1;
}
char *chDynamic()
{
char *s1 = malloc(sizeof(char) * 20);
strcpy(s1, "Dynamic");
return s1;
}
char *s1;
char *s2;
s1 = chLiteral();
s2 = chDynamic();
free(s2);
자료형에 상관없이 값을 꺼내오고 싶을 때 사용
void *a()
{
void *ptr = malloc(100);
return ptr;
}
char *s1 = a(); // void 포인터를 char 포인터에 넣어 문자열처럼 사용
strcpy(s1, "hello");
free(s1);
int *numPtr1 = a(); // void 포인터를 int 포인터에 넣어서 정수 배열처럼 사용
numPtr1[0] = 10;
free(numPtr1);
구조체 반환
struct 구조체이름 함수이름()
{
return 구조체변수;
}
/*---------------------------------*/
struct Person {
char name[20];
};
struct Person getPerson()
{
struct Person p;
strcpy(p.name, "Jack");
return p;
}
struct Person p1;
p1 = getPerson();
구조체 포인터 반환
struct Person *a()
{
struct Person *p = malloc(sizeof(struct Person));
strcpy(p->name, "Jack");
return p;
}
struct Person *p1;
p1 = a(); // 포인터를 반환하여 p1에 메모리 주소 저장
free(p1);
별칭을 정의했다면 반환값 자료형에 별칭을 지정해주면 됨
typedef struct _Person {
char name[20];
int age;
char address[100];
} Person, *PPerson; // 구조체 별칭 Person, 구조체 포인터 별칭 PPerson
PPerson a() // Person 구조체 포인터의 별칭을 반환값 자료형으로 지정
{
PPerson p = malloc(sizeof(Person)); // 구조체 포인터에 동적 메모리 할당
strcpy(p->name, "Jack");
return p; // 구조체 포인터 반환
}
union Box {
int num;
};
enum BOX_TYPE {
BOX_PAPER = 0,
BOX_PLASTIC
};
union Box getBox()
{
union Box b;
b.num = 10;
return b;
}
enum BOX_TYPE getBoxType()
{
return BOX_PLASTIC;
}
union Box box;
enum BOX_TYPE boxType;
box = getBox();
boxType = getBoxType();
반환값자료형 함수이름(자료형 매개변수)
{
}
/*---------------------------------------*/
void whatnum(int num1)
{
printf("%d", num1);
}
int add(int a, int b)
{
return a + b;
}
whatnum(10); // 10 출력
num = add(10, 20); // add함수에 10과 20을 전달, 30이 반환되어 nu에 저장
매개변수, 파라미터, 형식 매개변수, 인자 : int add(int a, int b)에서 int a와 int b부분을 말함, 함수 바깥에서 전달된 값이 저장되는 변수를 뜻함
인수, 전달인자, argument, actual argument : add(10, 20)에서 10과 20을 말함, 함수를 호출할 때 전달하는 값이나 변수를 뜻함
잘못된 예
void swap(int a, int b)
{
int temp;
temp = a;
a = b;
b = temp;
}
int num1 = 10;
int num2 = 20;
swap(num1, num2);
printf("%d %d", num1, num2); // 10 20 출력
매개변수는 값을 전달하는 역할만 하고 함수 밖의 변수와는 상관이 없어서 실제로 바뀌지 않는 것
포인터 매개변수를 사용한 정답
void swap(int *a, int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
int num1 = 10;
int num2 = 20;
swap(&num1, &num2);
printf("%d %d", num1, num2); // 20 10 출력, 함수에서 메모리 주소로 접근해서 값을 저장했기 때문에 밖에서도 바뀜
enum TYPE {
TYPE_CHAR,
TYPE_INT,
TYPE_FLOAT
};
void swapValue(void *ptr1, void *ptr2, enum TYPE t) // 반환값 없음, void 포인터 매개변수 두 개와
{ // 변수의 자료형을 알려줄 열거형을 받음
switch (t)
{
case TYPE_CHAR: // 문자면 char *로 변환한 뒤 역참조하여 값을 서로 바꿈
{
char temp;
temp = *(char *)ptr1;
*(char *)ptr1 = *(char *)ptr2;
*(char *)ptr2 = temp;
break;
}
case TYPE_INT: // 정수면 int *로 변환한 뒤 역참조하여 값을 서로 바꿈
{
int temp;
temp = *(int *)ptr1;
*(int *)ptr1 = *(int *)ptr2;
*(int *)ptr2 = temp;
break;
}
case TYPE_FLOAT: // 실수면 float *로 변환한 뒤 역참조하여 값을 서로 바꿈
{
float temp;
temp = *(float *)ptr1;
*(float *)ptr1 = *(float *)ptr2;
*(float *)ptr2 = temp;
break;
}
}
}
char c1 = 'a';
char c2 = 'b';
swapValue(&c1, &c2, TYPE_CHAR);
잘못된 예
void allocMemory(void *ptr, int size) // 반환값 없음, void 포인터 매개변수 지정
{
ptr = malloc(size); // ptr은 allocMemory를 벗어나면 사용할 수 없음
}
long long *numPtr = NULL;
allocMemory(numPtr, sizeof(long long));
free(numPtr);
ptr에 메모리를 할당해봤자 allocMemory를 벗어나면 사용할 수 없어 메모리 누수 발생
이중포인터 사용
void allocMemory(void **ptr, int size)
{
*ptr = malloc(size); // void **ptr을 역참조하여 void *ptr에 메모리 할당
}
long long *numPtr;
allocMemory((void**)&numPtr, sizeof(long long));
함수 안에서 void **ptr을 역참조하여 void *ptr이 되도록 만들고 메모리 할당
void ptrString(char *s1)
{
printf("%s", s1);
}
void arrString(char s1[])
{
printf("%s", s1);
}
반환값자료형 함수이름(자료형 매개변수[])
{
}
반환값자료형 함수이름(자료형 *매개변수)
{
}
/*-------------------------------------------*/
void printArr(int arr[], int count)
{
for (int i = 0; i < count, i++)
{
printf("%d", arr[i]);
}
}
int numArr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
printArr(numArr, sizeof(numArr) / sizeof(int)); // 배열과 요소의 개수를 넣는다.
매개변수를 arr[] 같이 지정했다면 arr은 포인터이고, 그렇기에 함수 안에서 배열의 요소를 변경하면 밖의 배열 요소도 변경된다.
void change(int arr[])
{
arr[2] = 10;
}
change(numArr);
printf("%d", numArr[2]); // 10이 출력 된다.
매개변수를 포인터로 지정
void printArr(int *arr, int count)
{
printf("%d", arr[1]);
}
복합 리터럴 사용하면 함수에 배열을 넘겨줄 때 배열을 따로 선언하지 않아도 된다.
(자료형[]) {값1, 값2, ...}
(자료형[크기]) {값1, 값2, ...}
/*------------------------------*/
printArr((int[]) { 1, 2, 3, 4, 5});
함수의 배열 매개변수에서 요소의 최소 개수도 지정해줄 수 있다. 매개변수로 들어온 배열의 요소 개수가 지정된 개수보다 작을 때 경고가 발생한다.
반환값자료형 함수이름(자료형 매개변수[static 최소개수])
{
}
/*--------------------------------------------------------*/
void printArr(int arr[static 5], int count)
{
}
반환값자료형 함수이름(자료형 매개변수[][가로크기])
{
}
반환값자료형 함수이름(자료형 (*매개변수)[가로크기])
{
}
/*------------------------------------------------------*/
void print2dArr(int arr[][5], int col, int row) // 2차원 배열의 포인터와 가로, 세로 크기를 받음
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
2차원 배열을 매개변수로 사용할 때 포인터와 대괄호를 사용하는 것도 가능
void print2DArray(int (*arr)[5], int col, int row) // 매개변수를 포인터로 만든 뒤 가로 크기 지정
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
반환값자료형 함수이름(struct 구조체이름 매개변수)
{
}
/*-----------------------------------------------*/
struct Name {
char name[20];
};
void printName(struct Person p)
{
printf("%s", p.name);
}
구조체도 복합 리터럴 사용 가능
(struct 구조체이름) {.멤버이름 = 값, .멤버이름2 = 값2}
(struct 구조체이름) {값1, 값2}
/*----------------------------------------------------*/
printName((struct Person) {"Jack"});
반환값자료형 함수이름(struct 구조체이름 * 매개변수)
{
}
/*------------------------------------------------*/
void setName(struct Person *p)
{
strcpy(p->name, "Jack");
}
struct Person p1;
strcpy(p1.name, "John");
setName(&p1);
printf("%s", p1.name); // Jack 출력
복합 리터럴 사용
&(struct 구조체이름) {.멤버이름 = 값, .멤버이름2 = 값2}
&(struct 구조체이름) {값1, 값2}
/*---------------------------------------------------*/
printName(&(struct Person) {"Jack"});
별칭 사용
typedef struct _Person {
char name[20];
int age;
char address[100];
} Person, *PPerson; // 구조체 별칭 Person, 구조체 포인터 별칭 PPerson
void setPerson(PPerson p) // Person 구조체 포인터의 별칭을 매개변수로 지정
{
strcpy(p->name, "고길동");
p->age = 40;
strcpy(p->address, "서울시 서초구 반포동");
}
union A {
char first;
}
enum WEEK {
MON = 0;
TUE,
WED
};
void printA(union A a)
{
printf("%c", a.first);
}
void printWeek(enum WEEK weekday)
{
printf("%d", weekday);
}
union A a;
enum WEEK weekday;
a.first = 'a';
weekday = TUE;
printA(a);
printWeek(weekday);