2010. 9. 27. 21:03



[스택(Stack), 힙(Heap) 그리고 데이터(Data)영역 ]
프로그램을 실행시키기 위해서는 메모리 공가ㅏㄴ이 필요하다. 지역 변수나 전역 변수를 선언하기 위해서도 메모리 공간이 필요하지 않은가? 그래서 컴퓨터는 여러분이 실행시킨 프로그램의 실행을 위해서 메모리 공간을 할당해 준다. 이렇게 해서 할당되는 메모리 공간은 크게 스택, 힙 그리고 데이터 영역으로 나뉘어 진다.
->쉬운 이해를 위해서 컴퓨터가 메모리 공간을 할당해 준다고 표현하였지만, 운영체제가 메모리 공간을 할당해 준다고 표현해야 정확한 표현이 된다.


할당 시기 : 프로그램이 실행될 때마다
할당 장소 : 메인 메모리에 할당되는데, 여기서 말하는 메인 메모리라 함은 RAM을 의미 함
할당 용도 : 프로그램 실행 시 필요한 메모리 공간(ex: 지역 변수, 전역 변수 선언을 위해)의 할당을 위해서

 전역변수
 Static 변수
  데이터 영역
 프로그래머 할당  힙(Heap)영역 
 지역변수
 매게변수
 스택(Stack)영역 

 * 데이터 영역(Data Area)
데이터 영역은 전역 변수와 static 변수가 할당되는 영역이다. 이 영역에 할당되는 ㅂ변수들은 일반적으로 프로그램의 시작과 동시에 할당되고, 프로그램이 종료되어야만 메모리에서 소멸된다. 즉, 데이터 영역에 할당된 변수는 프로그램이 종료될 때까지 존재한다는 특징을 지닌다. 전역변수와 static 변수는 프로그램이 종료될 때까지 존재하는 변수들이다. 이러한 특징이 데이터 영역의 특징과 일치한다.


* 스택 영역(Stack Area)
스택 영역은 함수 호출  시 생성되는 지역 변수와 매게 변수가 저장되는 영역이다. 이 영역에 할당된 변수는 함수 호출이 완료되면 사라진다는 특징을 지닌다. 이는 다른 메모리 영역과 확실히 비교되는 특징이다.

예를 들어 다음과 같은 stack이라는 함수가 있다고 가정해 보자

인자가 전달되면서 stack이라는 함수가 호출되면, 일단 매게 변수 a가 스택 영역에 올라가게 된다. 그리고 나서 지역 변수 b가 스택 영역에 올라갈 것이다. 반대로 stack 함수의 호출이 완료되고 나면, 변수 a와 변수 b는 스택영역에서 사라져 버린다.



* 힙 영역(Heap Area)
힙은 프로그래머가 관리하는 메모리 영역이다. 즉 프로그래머의 필요에 의해서 메모리 공간이 할당 및 소멸되는 영역이다.



◎배열은 반드시 상수로 선언해야 하는 이유
-배열 선언 시 배열의 길이는 반드시 상수로 표현해야 한다. 그 이유를 찾는 과정에서 힙 영역의 필요성-특징을 이해하게 될 것이다.
∴"스택과 데이터 영역에 할당될 메모리의 크기는 컴파일되는 동안(compile-time)에 결정되어야 하기 때문"
-힙은 데이터 영역이나 스택과 달리 런-타임에 필요한 만큼 메모리를 할당하고 해제할 수 있다.
"할당해야 할 메모리의 크기를 런-타임(프로그램이 실행되는 동안)에 결정해야 하는 경우, 유용하게 사용되는 메모리 공간이 바로 힙(Heap)영역이다."





출처 ;C프로그래밍 _ 윤성우
Posted by Triany
2010. 9. 26. 19:23

프로그램을 구현하다 보면 난수(Random Number)를 발생시켜야 하는 경우가 종종 있다. 여기서 말하는 난수란 임의의, 정해지지 않은, 무엇이 될지 모르는 수를 의미하는 것으로 무작위로 값을 생성해야 함을 의미한다.

ANSI표준에서는 이렇게난수를 생성할 때 사용할 수 있는 함수 rand를 제공하고 있다.

다음 예에서는 rand 함수의 사용 방법과 난수의 범위를 보여준다.
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
     int i;

     printf("난수의 범위 : 0부터 %d까지\n", RAND_MAX);

     for(i = 0; i < 5; i++)
          printf("난수 출력 : %d \n", rand() );

     return 0;
}



위의 예제를 여러 번 돌려보면 한가지 특징을 발견할 수 있다. 그것은 발생하는 난수가 규칙적이라는 것이다.
예를 들어서 처음 실행했을 때 생성된 난수가 21, 1, 43, 13, 2라면 다시 실행해 봐도 생성되는 난수는 21, 1, 43, 13, 2가 생성된다는 것이다. 이는 분명히 문제다! 왜냐하면 진짜로 난수가 생성되는 것이 아니기 때문이다. 따라서 rand 함수를 가리켜 의사 난수(pseudo-random number)를 생성하는 함수라고 하는 것이다. 의사 난수라는 뜻은 가짜 난수를 의미한다.

물론 여기에는 대안이 있다. srand함수를 사용하는 것이다.
#include <stdlib.h>

void srand( unsigned int seed );

함수 srand는 하나의 인자를 전달받는다. 이 인자를 가리켜 씨드(seed) 값이라 한다. 씨드란 씨앗이라는 뜻이다. 즉 난수 생성을 위해 씨앗을 심는 용도로 사용되는 함수이다. "콩 심은 데 콩 나고, 팥 심은 데 팥 난다."는 속담이 있듯이 씨드 값이 무엇이냐에 따라서 rand 함수 호출 시 생성되는 난수들은 달라지기 마련이다.

다음 예제는 씨드값을 입력 받아서 srand 함수를 호출하는 예제이다.
#include < stdio.h >
#include < stdlib.h >

int main(void)
{
     int seed, i;
 
    printf("씨드 값 입력: ");
    scanf("%d", &seed);
    srand(seed);    //씨앗을 심는다.
   
    for( i = 0; i < 5 ; i++)
        printf("정수 출력: %d \n", rand()); //열매를 수화ㅏㄱ한다.

    return 0;
}



srand 함수도 공부했으니 이제 진정한 난수를 발생시킬 수 있겠는가?
아니다!! 여전히 문제는 있다. 위 예제처럼 프로그램을 실행할 때마다 씨드 값을 입력받을 수는 없는 노릇 아닌가? 설사 입력받는다 해도 매번 다른 값을 입력받는다는 가정을 세우는 것도 무리가 있다. 한가지 방법은 있다. 시스템의 시간을 이용하는 것이다. 현재 여러분 컴퓨터의 시간 값을 얻어와서 srand 함수의 인자로 전달할 수만 있다면 문제는 해결이 된다. 왜냐하면 컴퓨터의 시간은 계속해서 변하기 때문이다.

그렇다면 컴퓨터의 현재 시간을 어떻게 얻어와야 할까? 헤더파일 time.h 에 선언되어 있는 time이라는 이름의 함수를 사용하면 된다. 이 함수는 컴퓨터의 현재 시간과 1970년 1월 일 이후의 시간적 차를 초 단위로 계산하여 반환해 준다(굉장하지 않은가?) 우리는 정확한 시간을 얻겠다는 것이 아니고, 프로그램 실행시 마다 변경되는 값을 얻겠다는 것이므로, 이 정도면 충분하다.


지금까지 이야기한 내용을 종합하여 진정한 난수를 발생시키는 예를 제시하겠다. 실행해 보면 매번 출력되는 난수의 값이 다르다는 것을 알 수 있을 것이다.
#include < stdio.h >
#include < stdlib.h >
#include < time.h >

int main(void)
{
    int i;
    srand( (int) time (NULL) ); //현재 시간을 이용해서 씨드 설정
    for( i = 0; i < 5; i++ )
        printf("정수 출력 : %d \n", rand() );

    return 0;
}


출처 :  C프로그래밍 _ 윤성우 저
Posted by Triany
2010. 9. 3. 14:13
함수 포인터 선언!
int (*fPtr1)(int);
: 함수포인터 fPtr1은 리턴형이 int이고 int형 인자 1개를 전달받는 모든 함수를 가리킬 수 있는 포인터가 된다.
void (*fPtr2)(int, int);
: 함수포인터 fPtr2는 리턴형이 void이고 int형 인자 2개를 전달 받는 모든 함수를 가리킬 수 있는 포인터가 된다.

 #include <stdio.h>

void Add(int a, int b);
void Sprint(char *str);
int main(void){
 char *string = "Function Pointer";
 int a=10, b=20;

 void (*fPtr1)(int, int) = Add;
 void (*fPtr2)(char*) = Sprint;

 fPtr1(a, b);
 fPtr2(string);

 return 0;
}

void Add(int a, int b)
{
 printf("덧셈 결과 : %d\n", a+b);
}
void Sprint(char *str)
{
 printf("입력된 문자열: %s\n", str);

}






포인터 배열과 같이 '함수 포인터를 원소로 갖는 함수 포인터 배열'을 생각할 수 있다.
다음은 배열의 크기가 4인 함수 포인터 배열을 선언하는 문장으로 배열의 각 원소가 가리키는 함수는 반환값이 void이고 인자목록이 (double*, double, double)이어야 한다.

    void (*pfary[4]) (double*, double, double)

위에서 선언된  배열에 함수 4개의 주소값을 저장하는 문장은 다음과 같다.
   
    pfary[0] = add;
    pfary[1] = subtract;
    pfary[2] = multply;
    pfary[3] = devide;

또는 간단하게 배열의 선언화 초기화 문장으로 처리할 수 있다.
   
    void (*pfary[4]) (double*, double, double) = {add, subtract, multiply, devide};

위의 것들을 사용하여 간단한 사칙연산 예제 프로그램을 작성해 보자.

 예제 10-17   출처 : C로 배우는 프로글밍 기초 p.327

 /*
   파일 : pfunarray.c
   목적 : 함수 포인터 배열을 이해하기 위한 예제
*/
#include <stdio.h>

void add(double*, double, double);
void subtract(double*, double, double);
void multiply(double*, double, double);
void devide(double*, double, double);

int main(void)
{
 double m, n, result;
 int opmenu;
 char *opstr[4] = {"더하기", "빼기", "곱하기", "나누기"};
 char op[4] = {'+', '-', '*', '/'};

 void (*pfary[4])(double*, double, double) = {add, subtract, multiply, devide};

 printf("사칙연산을 위하여 각 연산에 대한 번호를 입력하세요. >> \n");
 printf("[더하기] : 0, [빼기] : 1, [곱하기] : 2, [나누기] : 3 >> ");
 scanf("%d", &opmenu);
 if( !(0 <= opmenu && opmenu <= 3) )
 {
  printf("메뉴 입력이 잘못되어 프로그램을 종료합니다.\n");
  return 0;
 }

 printf("\n사칙연산을 수행할 실수 2개를 입력하세요. >> ");
 scanf("%lf %lf", &m, &n);

 //사칙연산을 배열의 첨자를 이용하여 수행
 pfary[opmenu](&result, m, n);
 printf("\n문장  : pfary[%d] 함수 호출\n", opmenu);
 printf("%s 수행 : %lf %c %lf == %lf\n\n", opstr[opmenu], m, op[opmenu], n, result);

 return 0;
}

void add(double *z, double x, double y)
{
 *z = x + y;
}
void subtract(double *z, double x, double y)
{
 *z = x - y;
}
void multiply(double *z, double x, double y)
{
 *z = x*y;
}
void devide(double *z, double x, double y)
{
 *z = x/y;
}



[+@  if문을 while문으로 고쳐서, 사용자가 임의로 종류하기 전까지 반복하는 식으로 변경해 보자.]
[      잘못된 수를 썼을 경우, 다시 입력하라는 문구가 뜨게 하자.]
Posted by Triany
2010. 9. 3. 13:33

값에 의한 전달(call by value)
주소에 의한 전달 (call by address)


 예제 10-15   (출처 : C로 배우는 프로그래밍 기초 p.321)

/*
 파일 : callbyaddrress.c
 목적 : 주소에 의한 호출과 값에 의한 호출을 비교하는 예제
*/
#include <stdio.h>

void incrementbyvalue(int number);
void incrementbyaddress(int *number);
int main(void)
{
 int number = 10;

 printf("함수 main()에서 \n");
 printf("\t지역변수 주소(number) = %p, number = %d\n\n", &number, number);

 incrementbyvalue(number);
 printf("\tincrementbyvalue(int number)를 호출 이후 =>> \n");
 printf("\t지역변수 주소(number) = %p, number = %d\n\n", &number, number);

 incrementbyaddress(&number);
 printf("\tincrementbyvalue(int *number)를 호출 이후 =>> \n");
 printf("\t지역변수 주소(number) = %p, number = %d\n\n", &number, number);

 return 0;
}

void incrementbyvalue(int number)
{
 number++;
}

void incrementbyaddress(int *number)
{
 (*number)++;
}




Posted by Triany