Coding/C

[C언어] 함수 사용하기 - 코딩도장

GunP4ng 2024. 2. 2. 23:43

[코딩도장] Unit 60 ~ 62


1. 함수

1. 함수 정의하기

함수를 정의하는 방법은 반환값 자료형, 함수 이름, ( ) 순으로 적어준 뒤

{ } 안에 원하는 코드를 작성하면 된다

함수는 main 함수 바깥에서 작성해야 한다

void hello()    // 반환값이 없는 hello 함수 정의
{
    printf("Hello, world!\n");    // Hello, world! 출력
}

함수의 반환값 자료형 부분에 void 를 적어주면 함수의 반환값이 없다는 뜻이 된다

 

int main()
{
    hello();    // hello 함수 호출

    return 0;
}

main 함수 안에서 함수이름을 적어준 뒤 ( ) 를 붙이면 함수를 사용할 수 있다

이렇게 함수를 사용하는 방법을 

함수를 호출(call) 한다 라고 부른다

 

main 함수와 hello 함수의 실행순서는 아래 그림과 같다

main함수와 hello함수의 실행 순서

 

함수 안에 선언된 변수는 지역 변수라고 한다

※ 지역 변수는 함수가 끝나면 사라지기 때문에 함수 바깥에선 사용할 수 없다

 

2. 함수 선언과 정의 분리하기

C언어는 함수를 선언하고 정의할 수 있다

함수 형태만 선언하기에 함수 원형이라 부른다

void hello();    // 반환값이 없는 hello 함수 원형 선언

int main()
{
    hello();    // hello 함수 호출

    return 0;
}

void hello()    // 반환값이 없는 hello 함수 정의
{
    printf("Hello, world!\n");    // Hello, world! 출력
}

main 함수 위에 함수 선언을 먼저 한 뒤

main 함수 아래에 hello 함수를 정의해주면 된다.

※ 함수 선언만 있고 정의가 없다면 링크 에러가 발생한다

 

 

2. 함수에서 반환값 사용하기

1. 정수, 실수, 불 반환하기

반환값은 함수를 정의할 때 반환값의 자료형을 지정하고

함수 안에서 return 으로 반환하면 된다

반환값자료형 함수이름()
{
    return 반환값;
}

※ 반환값과 반환값 자료형이 일치해야 한다

 

int one()    // 반환값이 int형인 one 함수 정의
{
    return 1;    // 1을 반환
}

int main()
{
    int num1;

    num1 = one();    // int형을 반환했으므로 int형 변수에 저장

    printf("%d\n", num1);    // 1

    return 0;
}

반환값을 저장하려면 반환값을 저장할 변수에

할당 연산자를 사용한 뒤 함수를 호출해주면 된다

반환값의 저장과정

반환값이 변수에 저장되는 과정은 다음과 같다

반환값을 변수에 저장하지 않고 바로 사용하려면 printf 안에 넣어서 실행해도 된다

printf("%d\n", one());    // one 함수의 반환값을 바로 사용

 

실수, 불 자료형도 마찬가지로

반환값 자료형을 float, bool 로 설정한 뒤

return 으로 float, bool 값을 반환하면 된다

 

2. 포인터 반환하기

포인터를 반환하려면 반환값 자료형과 함수 이름 사이에 * 를 붙여주면 된다

반환값자료형 *함수이름()
{
    return 반환값;
}

지역 변수는 함수가 끝나면 사라지기 때문에

함수의 반환값으로 사용할 수 없다

 

포인터를 반환하려면 malloc 함수로 메모리를 할당한 뒤 반환해야 한다

int *ten()    // int 포인터를 반환하는 ten 함수 정의
{
    int *numPtr = malloc(sizeof(int));    // int 크기만큼 동적 메모리 할당

    *numPtr = 10;    // 역참조로 10 저장

    return numPtr;   // 포인터 반환. malloc으로 메모리를 할당하면 함수가 끝나도 사라지지 않음
}

int main()
{
    int* numPtr;

    numPtr = ten();    // 함수를 호출하고 반환값을 numPtr에 저장

    printf("%d\n", *numPtr);    // 10: 메모리를 해제하기 전까지 안전함

    free(numPtr);    // 다른 함수에서 할당한 메모리라도 반드시 해제해야 함

    return 0;
}

다른 함수에서 할당한 메모리라 하더라도 해제하기 전까지는 그대로 사용할 수 있다

※ 메모리가 main 이 아닌 다른 함수에서 할당되었다 하더라도 free 함수로 해제해준다

 

3. void 포인터 반환하기

void 포인터를 활용하면 어떤 자료형으로 된 포인터도 넣을 수 있다

void *allocMemory()    // void 포인터를 반환하는 allocMemory 함수 정의
{
    void *ptr = malloc(100);    // 100바이트만큼 동적 메모리 할당

    return ptr;    // void 포인터 반환
}

int main()
{
    char *s1 = allocMemory();       // void 포인터를 char 포인터에 넣어서 문자열처럼 사용
    strcpy(s1, "Hello, world!");    // s1에 Hello, world! 복사
    printf("%s\n", s1);             // Hello, world!
    free(s1);                       // 동적 메모리 해제

    int *numPtr1 = allocMemory();   // void 포인터를 int 포인터에 넣어서 정수 배열처럼 사용
    numPtr1[0] = 10;                // 첫 번째 요소에 10 저장
    numPtr1[1] = 20;                // 두 번째 요소에 20 저장
    printf("%d %d\n", numPtr1[0], numPtr1[1]); // 10 20
    free(numPtr1);                  // 동적 메모리 해제

    return 0;
}

void *allocMemory 와 같이 반환값 자료형을 void 포인터로 지정한다

함수 안에서는 void 포인터에 malloc 으로 100바이트만큼 메모리를 할당한 뒤 반환한다

 

main 함수에서는 allocMemory 함수 안에서 malloc 을 사용하고 있으므로

allocMemory 에서 반환된 포인터를 사용할 때마다 free 함수로 메모리를 해제해줘야 한다

 

void *allocMemory()
{
    return malloc(100);    // malloc 함수를 호출하면서 바로 반환
}

ptr 변수를 선언하지 않고, malloc 함수를 호출하면서 바로 반환하면

코드를 줄일 수 있다

 

4. 구조체, 구조체 포인터 반환하기

C언어는 구조체를 반환하여 데이터를 묶어서 한번에 가져올 수 있다

struct 구조체이름 함수이름()
{
    return 구조체변수;
}

 

구조체를 반환하는 함수를 정의하려면

struct 키워드와 구조체 이름을 함수 이름 앞에 붙여주면 된다

struct Person {
    char name[20];
    int age;
    char address[100];
};

struct Person getPerson()    // Person 구조체를 반환하는 getPerson 함수 정의
{
    struct Person p;

    strcpy(p.name, "홍길동");
    p.age = 30;
    strcpy(p.address, "서울시 용산구 한남동");

    return p;    // 구조체 변수 반환
}

함수 안에서 Person 구조체 변수를 선언하고 값을 저장한 뒤 반환한다

 

int main()
{
    struct Person p1;

    p1 = getPerson();    // 반환된 구조체 변수의 내용이 p1로 모두 복사됨

    // getPerson에서 저장한 값들이 출력됨
    printf("이름: %s\n", p1.name);      // 홍길동
    printf("나이: %d\n", p1.age);       // 30
    printf("주소: %s\n", p1.address);   // 서울시 용산구 한남동

    return 0;
}

구조체 변수를 반환한 뒤 다른 변수에 저장하면

반환된 구조체의 내용을 모두 복사하게 된다

→ 복사할 공간이 더 필요하게 되어 공간이 낭비된다

 

구조체를 반환할 때는 구조체 복사가 일어나지 않도록

malloc 함수로 메모리를 할당한 뒤 구조체 포인터를 반환하는 것이 좋다

 

구조체 포인터를 반환하는 함수는

구조체 이름과 함수 이름 사이에 * 를 붙이면 된다

struct 구조체이름 *함수이름()
{
    return 구조체포인터;
}

 

struct Person *allocPerson()    // Person 구조체 포인터를 반환하는 allocPerson 함수 정의
{
    struct Person *p = malloc(sizeof(struct Person));    // 구조체 포인터에 동적 메모리 할당;

    strcpy(p->name, "홍길동");
    p->age = 30;
    strcpy(p->address, "서울시 용산구 한남동");

    return p;    // 구조체 포인터 반환
}

함수 안에서 구조체 포인터에 메모리를 할당하고 값을 저장한 뒤

구조체 포인터를 반환한다

 

int main()
{
    struct Person *p1;

    p1 = allocPerson();    // 포인터를 반환하여 p1에 메모리 주소 저장

    // allocPerson에서 저장한 값들이 출력됨
    printf("이름: %s\n", p1->name);       // 이름: 홍길동
    printf("나이: %d\n", p1->age);        // 나이: 30
    printf("주소: %s\n", p1->address);    // 주소: 서울시 용산구 한남동

    free(p1);    // 동적 메모리 해제

    return 0;
}

main 함수에서 allocPerson 함수를 호출한 뒤 반한된 포인터를 p1 에 저장한다

구조체 포인터이므로 -> (화살표 연산자)를 이용하여 멤버에 접근한다

메모리 사용이 끝났다면 free 함수로 해제해준다

 

주소가 들어있는 포인터만 반환하여 사용하므로

구조체의 내용을 복사하지 않아 효율적이다

 

typedef struct _Person {
    char name[20];
    int age;
    char address[100];
} Person, *PPerson;    // 구조체 별칭 Person, 구조체 포인터 별칭 PPerson

구조체 포인터 별칭을 정의했다면

함수의 반환값 자료형에 구조체 포인터 별칭을 그대로 지정하면 된다

 

※ 공용체와 열거형도 반환할 수 있다

 

 

3. 함수에서 매개변수 사용하기

1. 매개변수 사용하기

매개변수를 사용하려면 함수를 정의할 때 ( ) 안에 자료형과 변수 이름을 지정하면 된다

void helloNumber(int num1)    // 반환값 없음, int형 매개변수 한 개 지정
{
    printf("Hello, %d\n", num1);    // Hello, 와 매개변수를 조합하여 문자열 출력
}

함수를 정의할 때 int num1 과 같이 매개변수를 지정한다

함수와 매개변수

※ 매개변수를 여러개 사용하려면 각 매개변수를 , (콤마) 로 구분한다

 

매개변수, 파라미터(parameter), 형식 매개변수(formal parameter), 인자

→ 함수 바깥에서 전달된 값이 저장되는 변수

//          매개변수
//          ↓     ↓
int add(int a, int b)
{
    return a + b;
}

 

인수, 전달인자, 아규먼트(argument), 실행 전달인자(actual argument)

→ 함수를 호출할 때 전달하는 값이나 변수

//   인수
//   ↓  ↓
add(10, 20);

 

같은 변수나 값을 가리키지만

함수를 기준으로 봤을 때 용어가 다른 것이다

 

 

4. 심사문제

60.7 함수 정의하기

다음 소스 코드를 완성하여 "192.168.10.5" 와 "Jupiter" 가 각 줄에 출력되게 만드세요.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#include <stdio.h>

__________________________
__________________________
__________________________
__________________________

__________________________
__________________________
__________________________
__________________________

int main()
{
    printIPAddress();
    printHostname();

    return 0;
}

 

정답

void printIPAddress() {
    printf("192.168.10.5\n");
}

void printHostname() {
    printf("Jupiter\n");
}

60.8 함수 선언하기

다음 소스 코드를 완성하여 "Beethoven"과 "9th Symphony"가 각 줄에 출력되게 만드세요.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#include <stdio.h>

___________________
___________________ 

int main()
{
    printName();
    printOrdinal();

    return 0;
}

void printName()
{
    printf("Beethoven\n");
}

void printOrdinal()
{
    printf("9th Symphony\n");
}

 

정답

void printName();
void printOrdinal();

61.10 게임 캐릭터 능력치 함수 만들기

다음 소스 코드를 완성하여 20.500000, "false"가 각 줄에 출력되게 만드세요.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#include <stdio.h>
#include <stdbool.h>

____________________
____________________
____________________
____________________

____________________
____________________
____________________
____________________

int main()
{
    float armor;
    bool slow;

    armor = getArmor();
    slow = hasSlowSkill();

    printf("%f\n", armor);
    printf("%s\n", slow == true ? "true" : "false");
 
    return 0;
}

 

정답

float getArmor() {
    return 20.5;
}

bool hasSlowSkill() {
    return false;
}

61.11 문자열 포인터 반환하기

다음 소스 코드를 완성하여 "Neptune"이 출력되게 만드세요.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

_____________________
_____________________
_____________________

_____________________

_____________________
_____________________


int main()
{
    char *name;

    name = getName();
 
    printf("%s\n", name);

    free(name);

    return 0;
}

 

정답

void *getName() {
    char * str = malloc(sizeof(char) * 100);
    strcpy(str, "Neptune");

    return str;
}

61.12 메모리 할당 함수 만들기

다음 소스 코드를 완성하여 "Mercury"와 87.969002 11.5877602가 각 줄에 출력되게 만드세요.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

___________________
___________________
___________________

___________________
___________________

int main()
{
    char *name;
    float *stats;

    name = allocMemory();
    strcpy(name, "Mercury");
    printf("%s\n", name);
    free(name);

    stats = allocMemory();
    stats[0] = 87.969f;
    stats[1] = 115.8776f;
    printf("%f %f\n", stats[0], stats[1]);
    free(stats);

    return 0;
}

 

정답

void *allocMemory() {
    void *ptr = malloc(100);

    return ptr;
}

61.13 2차원 정보 만들기

다음 소스 코드를 완성하여 90 75 가 출력되게 만드세요.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#include <stdio.h>
#include <stdlib.h>

struct Point2D {
    int x;
    int y;
};

______________________________
______________________________
______________________________

______________________________
______________________________

______________________________
______________________________
 
int main()
{
    struct Point2D *pos1;

    pos1 = allocPoint2D();

    printf("%d %d\n", pos1->x, pos1->y);

    free(pos1);

    return 0;
}

 

정답

struct Point2D *allocPoint2D() {
    struct Point2D * pos = malloc(sizeof(struct Point2D));

    pos->x = 90;
    pos->y = 75;

    return pos;
}

62.7 게임 캐릭터 능력치 설정 함수 만들기

다음 소스 코드를 완성하여 공격 속도가 각 줄에 출력되게 만드세요.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#include <stdio.h>

__________________
__________________
__________________
__________________

int main()
{
    setAttackSpeed(0.638f);
    setAttackSpeed(1.23f);

    return 0;
}

 

정답

void setAttackSpeed(float a) {
    printf("Attack Speed: %f\n", a);
}

62.8 덧셈 함수 만들기

표준 입력으로 두 정수가 입력됩니다(입력 값의 범위는 long long의 범위와 같음.) 다음 소스 코드를 완성하여 두 정수의 합이 출력되게 만드세요.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

____________________
____________________
____________________
____________________
 
int main()
{
    long long num1, num2;

    scanf("%lld %lld", &num1, &num2);

    printf("%lld\n", add(num1, num2));

    return 0;
}

 

정답

long long add(long long a, long long b) {
    return a + b;
}

62.9 3차원 정보 만들기

표준 입력으로 x, y, z 좌표가 입력됩니다. 다음 소스 코드를 완성하여 입력된 x, y, z 좌표가 출력되게 만드세요.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

struct Point3D {
    float x;
    float y;
    float z;
};

_______________________________________________________
_______________________________________________________
_______________________________________________________

_______________
_______________
_______________

_______________
_______________

int main()
{
    float x, y, z;
    struct Point3D *pos1;
   
    scanf("%f %f %f", &x, &y, &z);

    pos1 = allocPoint3D(x, y, z);

    printf("%f %f %f\n", pos1->x, pos1->y, pos1->z);

    free(pos1);

    return 0;
}

 

정답

void *allocPoint3D(float a, float b, float c) {
    struct Point3D *p = malloc(sizeof(struct Point3D));

    p->x = a;
    p->y = b;
    p->z = c;

    return p;
}