사자자리

[C언어] 6주차 함수의 활용 본문

C언어/C언어 이론

[C언어] 6주차 함수의 활용

renne 2022. 6. 25. 23:55

함수의 인수 전달 방법

값에 의한 전달

 - 함수를 호출할 때 넘겨주는 인수의 값을 함수 정의에 있는 매개변수로 복사해서 전달하는 방식

#include <stdio.h>
void swap(int x, int y){
    int temp;

    temp = x;
    x = y;
    y = temp;
}

int main(){
    int a = 10;
    int b = 20;
    printf("swap 전의 a = %d, b = %d\n", a, b);
    
    swap(a, b);
    printf("swap 후의 a = %d, b = %d\n", a, b);
}

<실행 결과>
swap 전의 a = 10, b = 20
swap 후의 a = 10, b = 20

 

포인터에 의한 전달

 - 변수의 값을 복사해서 전달하는 대신, 변수의 주소를 전달하는 방식

#include <stdio.h>
void swap(int *x, int *y){	//매개변수를 포인터형으로 선언
    int temp;

    temp = *x;
    *x = *y;	//매개변수가 가리키는 곳에 처리 결과를 저장
    *y = temp;
}

int main(){
    int a = 10;
    int b = 20;
    printf("swap 전의 a = %d, b = %d\n", a, b);
    
    swap(&a, &b);	//변수의 주소를 인수로 전달
    printf("swap 후의 a = %d, b = %d\n", a, b);
}

<실행 결과>
swap 전의 a = 10, b = 20
swap 후의 a = 20, b = 10

 

배열의 전달

 - 배열은 항상 포인터로 전달한다.

 - 함수 안에서는 일반 배열인 것처럼 인덱스를 사용한다.

 - 함수를 호출할 때는 [ ] 없이 배열명만 인수로 넘긴다.

 - 함수 안에서 배열의 크기가 필요할 때, 배열의 크기도 인수로 전달한다.

 - 특정 원소의 주소를 전달하면 배열의 일부분만 인수로 전달할 수 있다.

#include <stdio.h>
void printArray(int *arr, int size){ 	//배열은 항상 포인터로 전달한다.
    for (int i = 0; i < size; i++){	//함수 안에서 배열의 크기가 필요할 때, 배열의 크기도 인수로 전달한다.
        printf("%d ", arr[i]);		//배열을 포인터 변수로 전달했지만, 일반 배열인 것처럼 인덱스를 사용한다.
    }
    printf("\n");
}

int getSum(int arr[], int size){	//int arr[]과 int *arr은 같은 의미다.
    int total = 0;
    for (int i = 0; i < size; i++){
        total += arr[i];
    }
    return total;
}

int main(){
    int x[5] = {10, 20, 30, 40, 50};
    int y[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    printf("x 배열: ");
    printArray(x, 5);	//함수를 호출할 때는 []없이 배열명만 인수로 넘긴다.
    printf("x 배열의 합계: %d\n", getSum(x, 5));
    
    printf("y 배열: ");
    printArray(y, 10);
    printf("y 배열의 합계: %d\n", getSum(y, 10));
    
    printf("배열의 일부분만 전달하기: ");
    printArray(y + 2, 5);	//특정 원소의 주소를 전달하면 배열의 일부분만 인수로 전달할 수 있다.
    return 0;
}

<실행 결과>
x 배열: 10 20 30 40 50
x 배열의 합계: 150
y 배열: 1 2 3 4 5 6 7 8 9 10
y 배열의 합계: 55
배열의 일부분만 전달하기: 3 4 5 6 7

 

 - 배열이 함수 안에서 변경되지 않을 때는 const를 지정할 수 있다.

void printArray(const int *arr, int size);	//arr이 가리키는 배열을 변경할 수 없고 읽을 수만 있다.
void printArray(const int arr[], int size);	//두 문장은 같은 의미다.

 

구조체의 전달

 - 구조체를 인수로 갖는 함수를 선언할 때, 포인터로 전달하는 것이 좋다.

#include <stdio.h>
typedef struct point{
    int x, y;
} POINT;

//구조체를 값으로 전달하는 경우
void printPoint(POINT p){
    printf("%d, %d\n", p.x, p.y);
}

//구조체를 포인터로 전달하는 경우
void printSwapPoint(POINT *p){
    int temp;
    temp = p->x;
    p->x = p->y;
    p->y = temp;
    printf("%d, %d\n", p->x, p->y);
}

int main(){
    POINT p1 = {10, 20};
    printPoint(p1);		//값으로 전달
    printSwapPoint(&p1);	//포인터로 전달
    return 0;
}

<실행 결과>
10, 20
20, 10

 

 - 구조체가 함수 안에서 변경되지 않을 때는 const를 지정할 수 있다.

void printPoint(const POINT *p);

 

재귀함수(recursive function)

 - 자기 자신을 다시 호출하는 함수

 - 문제 해결을 위한 알고리즘을 단순화한다.

#include <stdio.h>
int factorial(int n){
    if (n < 2) return 1;
    return n * factorial(n - 1);
}

int main(){
    int num;
    scanf("%d", &num);
    printf("%d! = %d\n", num, factorial(num));
    return 0;
}

<실행 결과>
5
5! = 120

 

 - 재귀 함수를 호출하면 함수가 여러 번 반복적으로 호출되므로, 오버헤드가 크다.

 - 따라서 재귀함수보다 반복문으로 구현하는 것이 성능 면에서는 좋다.

 - 재귀 함수 호출 시, 함수 호출의 깊이가 너무 깊어지지 않도록 주의한다.

 

기억 부류(storage class)

 - 변수나 함수를 선언할 때 사용되는 키워드

 - 변수나 함수의 저장 위치와 사용 범위를 결정한다.

 - 변수나 함수의 선언 시, 맨 앞에 지정한다.

 - 변수의 디폴트 기억 부류는 auto이고, 함수의 디포트 기억 부류는 extern이다.

 

auto

 - 지역 변수의 디폴트 기억 부류

 - 전역 변수에는 지정할 수 없다.

 - auto 지역 변수는 선언된 위치에서 자동으로 생성되고 선언된 블록을 빠져나갈 때 자동으로 해제된다.

#include <stdio.h>
int main(){
    auto int a = 10;
    int b = 20;	//auto를 생략해도, b는 auto 변수이다.
}

 

register

 - 변수를 메모리가 아닌 CPU의 레지스터에 할당한다.

 - 변수를 레지스터에 할당하면, 변수에 좀 더 빠르게 접근할 수 있다.

 - 보통 루프 제어 변수를 register 변수로 선언한다.

#include <stdio.h>
int main(){
    int sum = 0;
    register int i;
    for (i = 0; i < 11; i++) sum += i;
    printf("%d", sum);
    return 0;
}

 

 - register 변수를 선언해도, 변수가 레지스터에 할당되지 않을 수 있다.

 - register 변수에 대해서 주소 구하기 연산자를 사용할 수 없다.

register int i;
printf("%p", %i);	//컴파일 에러

 

extern

https://dojang.io/mod/page/view.php?id=802

 - 프로그램의 한 소스 파일(print.c)에 선언된 전역 변수(num1)를 extern을 통하여 다른 소스 파일(main.c)에서도 사용할 수 있다.

 

static

 - 정적 지역 변수: 함수가 리턴해도 해제되지 않고 남아있다가, 다음 함수 호출에 다시 사용된다.

#include <stdio.h>
void testStatic(void){
    int LocalVar = 0;	//지역 변수
    static int StaticLocalVar = 0;	//정적 지역 변수
    printf("LocalVar = %d, StaticLocalVar = %d\n", ++LocalVar, ++StaticLocalVar);
}

int main(){
    for (int i = 0; i < 5; i++) testStatic();
    return 0;
}

<실행 결과>
LocalVar = 1, StaticLocalVar = 1
LocalVar = 1, StaticLocalVar = 2
LocalVar = 1, StaticLocalVar = 3
LocalVar = 1, StaticLocalVar = 4
LocalVar = 1, StaticLocalVar = 5

 

 - 문자열을 역순으로 만들어서 리턴하는 함수

#include <stdio.h>
#include <string.h>

char *Reverse(const char *str){
    static char result[100];	//정적 지역 변수 선언
    int len = strlen(str);
    int i = 0;
    for (i = 0; i < len; i++){
        result[i] = str[len - i - 1];
    }
    result[i] = '\0';
    return result;	//result를 정적 지역 변수로 선언해서, 함수가 리턴해도 해제되지 않는다.
}

int main(){
    char name[] = "Regulus Black";
    char *result = NULL;

    result = Reverse(name);
    printf("%s", result);
    return 0;
}

<실행 결과>
kcalB sulugeR

 

 - 정적 전역 변수: 다른 소스 파일에서 전역 변수에 접근하지 못하도록 제한한다.

https://dojang.io/mod/page/view.php?id=690

 

  일반 지역 변수 정적 지역 변수 전역 변수 정적 전역 변수
선언 위치 함수 안 함수 안 함수 밖 함수 밖
생성 시점 변수 선언 시 프로그램 시작 시 프로그램 시작 시 프로그램 시작 시
해제 시점 함수 리턴 시 프로그램 종료 시 프로그램 종료 시 프로그램 종료 시
사용 범위 함수 안 함수 안 프로그램 전체 선언된 소스 파일
초기화하지 않으면 쓰레기 값 0으로 초기화 0으로 초기화 0으로 초기화

 

'C언어 > C언어 이론' 카테고리의 다른 글

[C언어] 7주차 동적 메모리 할당  (0) 2022.06.26
[C언어] 7주차 포인터의 활용  (0) 2022.06.26
[C언어] 6주차 구조체  (0) 2022.05.27
[C언어] 5주차 포인터  (0) 2022.05.20
[C언어] 5주차 배열과 문자열  (0) 2022.05.20
Comments