카테고리 없음

C언어 5강 | 함수 Functions | call by value, extern, static 등

토마토. 2021. 12. 16. 22:50

Functions

효과적인 문제 해결의 정수는 문제 분해다. 

이를 위해 함수가 필요하다. (모듈화라고 함)

 

Function Definition

함수는 함수 프로토타입, 함수 정의(헤더/바디), 함수 call(main)으로 구성된다. 

이 중 함수 정의부터 살펴보자. 

 

함수 정의의 골격은 다음과 같다. 

type function_name(parameter list)
{
	declarations;
    statements;
}

예시를 보자

#include <stdio.h>
#include <assert.h>

long fact(int n)
{
    int i;
    long product = 1;
    for (i=2;i<=n;++i) product *= i;
    return product;
}
void main(void){
    int n, m;
    long comb;
    scanf("%d%d", &n, &m);
    assert(n>=0&&m>=0);
    assert(n>=m);
    comb=fact(n)/(fact(m)*fact(n-m));
    printf("%dC%d=%ld\n", n, m, comb);
}

 

assert.h 헤더 파일의 assert 함수는 statement가 거짓일 경우에 

프로그램을 중단하는 역할을 한다. 

전체 프로그램은 조합을 출력하는 것

 

local variable(internal), global variables(external)

#include <stdio.h>

int a = 33;

int main(void){
    int b = 77;
    printf("a = %d\n", a);
    printf("b = %d\n", b);
    return 0;
}

 여기서 a는 global 변수, b는 internal local 변수이다. 

 

디버깅하기 쉬워지므로

프로그램을 작은 함수로 모듈화하는 게 매우 중요하다. 

 

return statement

return;

return ++a;

return (a*b);

 

function prototypes

함수 사용 이전에 선언하는 부분

type function_name(parameter type list);

이런 식으로~!

 

styles for function definition order

 

call by value

function은 기본적으로 call by value로 사용된다. 

function 내부의 value들은 모두 로컬함

#include <stdio.h>
int compute_sum(int n);
int main(void){
    int n = 3, sum;
    printf("%d\n", n);
    sum = comput_sum(n);
    printf("%d\n", n);
    printf("%d\n", sum);
    return 0;
}

int compute_sum(int n){
    int sum = 0;
    for (;n>0;n--){
        sum += n;
    }
    return sum;
}

위 프로그램에 sum이 두 번 등장하지만, 

int compute_sum 함수의 sum은 main에 전혀 영향을 끼치지 못함. 

 

developing a large program

보통 큰 프로그램들은 .h 헤더 파일과 .c 소스 파일로 구성된다.

헤더 파일을 직접 작성한 경우, 

.c 파일에서 #include "pgm.h"

같은 형태로 헤더 파일을 포함시키면 된다~

헤더 파일은 컴파일할 필요 없고, 

c 파일들은

gcc -o pgm main.c fct.c prn.c 의 형태로 컴파일한다. 

 

scope rule

 

storage classes

c의 모든 변수와 function은 두가지 속성을 가진다.

바로 type과 storage class

storage class에는 auto, extern, register, static이 있다. 

하나씩 살펴보자. 

 

- auto

가장 흔한 storage class

자동으로 automatic이 된다. 

block 안으로 들어가면, 시스템에서 메모리를 할당해준다. 

block을 나가면, 그 안에 할당된 메모리는 free된다. 

 

- extern

external 변수를 활용하면, 

블럭을 넘어서도 변수 등 정보를 전달할 수 있다. 

function 밖에서 변수를 정의하면, storage가 영구적으로 변수를 저장하고, 

해당 변수는 extern storage class가 된다. 

+ global 전역 변수

#include <stdio.h>

int a =1, b=2, c=3;

int f(void);
int main(void){
    printf("%3d\n", f());
    printf("%3d%3d%3d\n", a, b, c);
    return 0;
}
int f(void){
    int b, c;
    extern int a;
    a=b=c=4;
    return (a+b+c);
}

extern int a;라는 것은 어딘가에 이미 a라는 전역 변수가 있으니, 잘 찾아보라는 뜻이다.

그리고 이 프로그램은 a, b, c 중 a만이 f 함수의 결과가 반영된 것을 알 수 있음. 

 

- register

높은 스피드의 메모리에 저장되도록 한다. 

실행 속도를 높이기 위한 것임

그리고 블록 밖으로 나가면, 해당 변수는 free됨

 

{
	register int i;
    for (i=0;i<LIMIT;i++){
    	...
    }
}

 

- static

정적 변수 

다시 블럭에 들어갔을 때 이전의 value를 유지할 수 있도록 한다. 

auto variables와 대조적임

void f(void)
{
	static int cnt = 0;
    ++cnt;
    if (cnt%2==0)
    ...
    else
}

위 프로그램에서 static 변수 cnt=0은

처음 f()에 들어가면 초기화된다. 

그러나 cnt가 정적 변수이기 때문에 두번째 접속할 때에는 초기화되지 않는다. 

 

default initialization

external, static

자동으로 0으로 초기화된다

auto, register

garbage 값을 가지고 있다.

 

recursion

자기 자신을 다시 부르는 경우 (직간접적으로)

ex) factorial

int factorial(int a){
    if (a==1) return a;
    else {
        return a * factorial(a-1);
    }
}

ex) fibonacci

int fibonacci(int a){
    if (a==0) return 0;
    else if (a==1) return 1;
    else {
        return (fibonacci(a-1) + fibonacci(a-2));
    }
}

ex) hanoi tower

int hanoi(int n, int on, int using, int to)
{
    if (n==1){
        printf("%d->%d\n\n", on, to);
    }
    else{
        hanoi(n-1, on, to, using);
        printf("%d->%d\n\n", on, to);
        hanoi(n-1, using, on, to);
    }
    return 0;
}

using assertions

assert.h 헤더 파일에 정의된 매크로

assert(expr)

만약 expr이 true이면, 그냥 넘어가고..

expr이 false이면, 메시지를 남기면서 프로그램이 중단된다. 프로그램의 robustness에 기여함

int f(int a, int b)
{
	assert(a==1||a==-1);
    assert(b>=7&&b<=11);
}

 

output function printf()

printf()는 stdio.h 헤더 파일에 정의되어 있다. 

형 지정을 잘 해주어야 한다. 이게 은근 헷갈림..

%c %d %f %s 는 기본적으로 사용하는 것

 

%u (unsigned decimal int) %o(unsigned octal int) %x %X (unsigned hexidecimal int)

%e %E (floating point number) 7.100e+00 st 표기법

 

추가적인 조건

%- : 좌측 정렬

%3 : 3칸 안에 표현한다. 

%.6s : 소수점 6에서 반올림

%03 : 3칸에 표현하되, 나머지는 0으로 채움 

%#x : hexadecimal에서 0x 표기법 사용함

 

input function scanf()

scanf("%d", &in);

%d (integer) %s (string) %f (float) %lf (double) %d (decimal) %ld (long decimal) %c (character)

 

ex) root of a function

#include <stdio.h>
#include <assert.h>
#include <math.h>

int cnt = 0;
const double eps = 1e-13;

double f(double x){
    return(pow(x, 3)-7.0*x-3.0);
}

double bisection(double a, double b){
    double m = (a+b)/2.0;
    ++cnt;
    if (f(m)==0.0||b-a<eps) return m;
    else if (f(a)*f(m)<0.0) return bisection(a,m);
    else return bisection(m,b);
    }

int main(void){
    double a = -10.0, b=10.0;
    double root;
    assert(f(a)*f(b)<=0.0);
    root = bisection(a, b);
    printf("%s%d\n%s%.3f\n%s%e\n", "no. of fct calls: ", cnt, "root:", root, "function value:", f(root));
    return 0;
    
}

mathematical functions

매우 다양함

#include <math.h> 헤더 파일에 포함된 함수들