Skip to content

Files

Latest commit

b3de089 · Oct 29, 2021

History

History
569 lines (463 loc) · 11.2 KB

211029.md

File metadata and controls

569 lines (463 loc) · 11.2 KB

Day 28

Review C

함수 만들기

반환값자료형 함수이름()
{
    코드;
}
/*--------------------------*/

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 포인터 반환

자료형에 상관없이 값을 꺼내오고 싶을 때 사용

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 출력, 함수에서 메모리 주소로 접근해서 값을 저장했기 때문에 밖에서도 바뀜

void 포인터 매개변수 사용

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)
 {
 }

2차원 배열 매개변수 사용

반환값자료형 함수이름(자료형 매개변수[][가로크기])
{
}

반환값자료형 함수이름(자료형 (*매개변수)[가로크기])
{
}
/*------------------------------------------------------*/

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);