※요약

C언어에서 재귀 호출을 이용한 이진 탐색이다. 이진 탐색이란 이름 그대로 탐색할 데이터를 반으로 나눠 나머지 반만 탐색하는 방식을 반복하는 알고리즘이며 빠른 속도로 원하는 값을 찾을 수 있다. 이진 검색, 이분 검색 등으로도 불린다.


특징

 - 각 요소의 값들은 정렬되어 있어야 한다.

 - 각 요소의 값들은 모두 달라야 한다.


※예제

#include <stdio.h>

int RecusiveBinSearch( int nArr[], int nBegin, int nEnd, int nTarget )
{
	int nMid = 0;

	if( nBegin > nEnd )
	{
		return -1;	//탈출 조건 및 탐색 실패
	}

	nMid = (nBegin+nEnd) / 2;

	if( nArr[nMid] == nTarget )
	{
		return nMid;			//타겟을 찾았다.
	}
	else if( nTarget < nArr[nMid] )
	{
		return RecusiveBinSearch( nArr, nBegin, nMid-1, nTarget );
	}
	else
	{
		return RecusiveBinSearch( nArr, nMid+1, nEnd, nTarget );
	}
}

int main( )
{
	int nArr[] = { 1, 3, 5, 7, 9, 11, 13 };
	int nResult;

	nResult = RecusiveBinSearch( nArr, 0, sizeof(nArr)/sizeof(int)-1, 7 );
	
	if(nResult == -1)
	{
		printf( "값이 없습니다.\n" );
	}
	else
	{
		for( int i=0 ; i<sizeof(nArr)/sizeof(int) ; ++i )
		{
			printf( "%d ", nArr[i] );
		}
		printf( "\n%d번째 배열 요소에 있습니다.\n", nResult );
	}

	return 0;
}



결과



※요약

C언어에서 재귀 호출을 이용한 거듭제곱 구하는 함수다.

어떤 수 a에 대하여 n개 곱한 것을 a^n( a)이라 표시하고 a의 n제곱이라 하며, n을 거듭제곱의 지수라고 한다.

a = 3, n = 5일때 3^5 = 243 = 3 * 3 * 3 * 3 * 3 다.



※예제

#include <stdio.h>

double power( double a, int num );

int main( )
{	
	printf( "%.10lf", power( 3.141592653589793238462643383279, 3 ) );

	return 0;
}

double power( double a, int num )
{
    if( num == 0 )
        return 1;   
 
    return a * power( a, num-1 );
}


결과




요약

C언어에서 재귀 호출을 이용한 팩토리얼 구하는 함수이다.

정수 n의 팩토리얼은 n! 이라고 표시하며, n! = n * (n-1) * (n-2) * (n-3) * . . * 2 * 1 이며

5! 일 경우 5! = 120 = 5 * 4 * 3 * 2 * 1 다.



예제

#include <stdio.h>

unsigned __int64 factorial( unsigned __int64 num );

int main( )
{
	int nNum = 5;
	printf( "%d! : %I64u", nNum, factorial(nNum) );

	return 0;
}

unsigned __int64 factorial( unsigned __int64 num )
{
	if( num == 1 )
	{
		return 1;
	}

	return num * factorial( num-1 );
}


※결과



※요약

재귀 함수란 자기 자신을 호출하는 함수


일반적인 함수 호출 방법은 다음과 같다. 프로그램 실행 중 함수를 만나면 현재 위치를 저장 후, 호출할 함수의 주소로 점프해 함수 내용을 수행한다. 함수 실행이 끝나면 기억해뒀던 원래 위치로 복귀해 다음 코드를 수행한다. (일반적으로 잦은 함수 호출로 인한 점프의 반복은 속도를 느리게 한다.)


일반 함수나 재귀 함수나 위와 같은 방법으로 함수가 호출이 되는데 재귀 함수의 경우 함수의 실행이 채 끝나기도 전에 자기 자신을 호출한다. 이게 가능한 이유는 호출된 각 함수에 대한 복귀 번지, 인수, 지역 변수 등이 스택 프레임에 저장되기 때문에 기존의 작업 내용들이 서로 방해하지 않고 잘 동작할 수 있는 것이다.


※재귀 함수 특징

 - 무한 루프에 빠지지 않기 위해 일정한 탈출 조건이 있어야 한다.

 - 코드를 단순화 할 수 있다.

 - 재귀 함수는 호출 시 마다 스택 공간을 이용하므로 무리하게 호출하면 스택 오버플로우가 일어날 수 있다.

 - 재귀 함수의 호출 횟수는 스택의 남은 공간과 재귀 함수의 지역 변수 사이즈에 따라 달라진다.

 - 디버깅 및 실행 흐름을 파악하기 힘들다.



※예제

아래 예제는 기초적인 재귀 함수 예제다.

다음 게시물부터는 재귀를 이용한 팩토리얼(factorial), 거듭 제곱(power, square), 이진 탐색(binary search) 등을 알아보겠다.

#include <stdio.h>

void Recursive( int nCount )
{
	if( nCount <= 0 )
	{
		printf( "발사\n" );
		return ;
	}
	else
	{
		printf( "카운트 다운 : %d\n", nCount );
		Recursive( nCount-1 );        //인자로 받은 nCount에 -1을 줄여서 호출한다.
	}
}

int main( )
{
	Recursive( 5 );

	return 0;
}


결과




※요약

rewind : 개방된 파일에서 파일 포인터의 위치를 0으로 설정한다.



※함수 원형 및 설명

void rewind( FILE *stream );
//stream : 개방된 FILE 구조체의 포인터


※예제

#include <stdio.h>

#define print(n) printf( "%ld\n", n )

int main( )
{	
	FILE *pFile = NULL;

	pFile = fopen( "d:\\Text.txt", "w+" );
	if( pFile == NULL )
	{
		//에러 처리
	}
	else
	{
		print( ftell(pFile) );			//fopen 후 파일 포인터 위치 확인

		fputs( "0123456789", pFile );
		print( ftell(pFile) );			//fputs 후 파일 포인터 위치 확인

		rewind( pFile );
		print( ftell(pFile) );			//rewind 후 파일 포인터 위치 확인

		fclose( pFile );
	}

	return 0;
}





※요약

fseek : 개방된 파일에서 파일 포인터의 위치를 설정한다.



※특징

fseek 함수를 이용해 읽어온 파일의 크기를 구할 수도 있다.

fseek( stream, 0L, SEEK_END );
int nFileSize = ftell( stream );


※함수 원형 및 설명

int fseek( FILE *stream, long offset, int origin );
//stream : 개방된 FILE 구조체의 포인터
//offset : origin으로부터의 오프셋, 양수 또는 음수 가능
//origin : SEEK_SET, SEEK_CUR, SEEK_END 중 하나의 값
//		SEEK_SET : 파일 시작점(BOF)에서 offset만큼 이동
//		SEEK_CUR : 파일 포인터의 현재 위치에서 offset만큼 이동
//		SEEK_END : 파일의 끝(EOF)에서 offset만큼 이동
//반환값 : 0 (실패 시 0이 아닌 값)


※예제

#include <stdio.h>

#define print(n) printf( "%ld\n", n )

int main( )
{	
	FILE *pFile = NULL;

	pFile = fopen( "d:\\Text.txt", "w+" );
	if( pFile == NULL )
	{
		//에러 처리
	}
	else
	{
		print( ftell(pFile) );			//fopen 후 파일 포인터 위치 확인

		fputs( "0123456789", pFile );
		print( ftell(pFile) );			//fputs 후 파일 포인터 위치 확인

		fseek( pFile, 0L, SEEK_SET );    //처음 위치로 설정
		print( ftell(pFile) );			//fseek 후 파일 포인터 위치 확인

		fseek( pFile, 6L, SEEK_SET );    //처음 위치에서 6번 뒤로 설정
		print( ftell(pFile) );			//fseek 후 파일 포인터 위치 확인

		fseek( pFile, -2L, SEEK_CUR );    //현재 위치(6)에서 2번 앞으로 이동
		print( ftell(pFile) );			//fseek 후 파일 포인터 위치 확인

		fseek( pFile, 0L, SEEK_END );    //파일 끝으로 이동
		print( ftell(pFile) );			//fseek 후 파일 포인터 위치 확인

		fclose( pFile );
	}

	return 0;
}




※요약

ftell : 개방된 파일 스트림의 현재 파일 포인터의 위치를 구한다.



※특징

파일 포인터는 데이터를 읽거나 쓸 위치를 가르킨다.

파일 포인터는 데이터를 읽거나 쓴 크기 만큼 자동으로 증가하며,

위치를 수동으로 지정하려면 fseek함수나 fsetpos함수로 한다.



※함수 원형 및 설명

long ftell( FILE *stream );
//개방된 FILE 구조체의 포인터
//현재 파일 포인터의 위치를 반환


※예제

#include <stdio.h>

#define print(n) printf( "%ld\n", n )

int main( )
{	
	FILE *pFile = NULL;

	pFile = fopen( "d:\\Text.txt", "w" );
	if( pFile == NULL )
	{
		//에러 처리
	}
	else
	{
		long lp;

		lp = ftell( pFile );
		print( lp );

		fputs( "13579", pFile );

		lp = ftell( pFile );
		print( lp );

		fclose( pFile );
	}

	return 0;
}





※요약

fwrite : 개방된 파일에 바이트 단위로 쓴다.



※함수 원형 및 설명

size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
//buffer : 파일에 저장할 데이터 버퍼의 포인터
//size_t : 출력할 항목의 사이즈
//count : 출력할 항목의 개수
//stream : 개방된 FILE 구조체 포인터
//반환값 : 실제로 쓴 데이터 항목의 개수(count), 에러 시 count보다 작은 수


※예제

#include <stdio.h>

int main( )
{	
	FILE *pFile = NULL;

	pFile = fopen( "d:\\Text.txt", "w+t" );
	if( pFile == NULL )
	{
		//에러 처리
	}
	else
	{
		char buffer[] = { 'x' , 'y' , 'z' };

		int nResult = fwrite( buffer, sizeof(char), sizeof(buffer), pFile );
		if( nResult < sizeof(buffer) )
		{
			if( ferror(pFile) )
			{
				//파일 읽기 에러
			}
			if( feof(pFile) )
			{
				//파일 끝 도달
			}
		}

		fclose( pFile );
	}

	return 0;
}



※요약

fread : 개방된 파일에서 바이트 단위로 파일을 읽는다.



※함수 원형 및 설명

size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
//buffer : 파일 데이터를 읽어서 저장할 버퍼의 포인터
//size_t : 항목의 사이즈
//count : 항목의 개수, 읽어올 횟수
//stream : 개방된 FILE 구조체 포인터
//반환값 : 실제로 읽은 데이터 항목의 개수, 에러 시 count보다 작은 수


※예제

이번 예제는 Point Cloud 프로그램에서 사용하는 *.ply파일을 읽는 예제다.

파일의 구조는 헤더와 데이터로 나눠져 있으며, 예제에 주석으로 헤더, 데이터 읽는 부분을 표시하였다.

파일 헤더의 내용은 포맷과 점의 개수 표시, 데이터 타입, 데이터 순서 등이고 

데이터의 내용은 헤더에 표시된 점 개수 만큼의 x, y, z, r, g, b 값이다.

데이터의 내용을 읽을 때 fread로 데이터 타입 만큼의 바이트를 읽는다.


Wisconsin-Madison Capital.ply


#include <stdio.h>

struct tagData
{
	float x;
	float y;
	float z;

	unsigned char red;
	unsigned char green;
	unsigned char blue;
};

int main( )
{	
	FILE *pFile = NULL;

	pFile = fopen( "d:\\Wisconsin-Madison Capital.ply", "rt+" );
	if( pFile == NULL )
	{
		//에러 처리
	}
	else
	{
		tagData	*Data;
		int		nVertexCnt=0;

		{//헤더 읽기
			fscanf( pFile, "%*s \n" );
			fscanf( pFile, "%*s %*s %*s\n" );
			fscanf( pFile, "%*s %*s %d\n", &nVertexCnt );
			fscanf( pFile, "%*s %*s %*s\n" );
			fscanf( pFile, "%*s %*s %*s\n" );
			fscanf( pFile, "%*s %*s %*s\n" );
			fscanf( pFile, "%*s %*s %*s\n" );
			fscanf( pFile, "%*s %*s %*s\n" );
			fscanf( pFile, "%*s %*s %*s\n" );
			fscanf( pFile, "%*s \n" );
		}
		
		Data = new tagData[ nVertexCnt ];

		{//데이터 읽기
			for( int i=0 ; i<nVertexCnt ; ++i )
			{
				fread( &Data[ i ].x, sizeof( float ), 1, pFile );
				fread( &Data[ i ].y, sizeof( float ), 1, pFile );
				fread( &Data[ i ].z, sizeof( float ), 1, pFile );

				fread( &Data[ i ].red, sizeof( unsigned char ), 1, pFile );
				fread( &Data[ i ].green, sizeof( unsigned char ), 1, pFile );
				fread( &Data[ i ].blue, sizeof( unsigned char ), 1, pFile );
			}
		}
		fclose( pFile );
	}

	return 0;
}




※요약

fputc : 개방된 파일에 단일 문자를 쓴다.



※함수 원형 및 설명

int fputc( int c, FILE *stream );
⁄⁄c : 개방된 파일에 쓸 문자 또는 ASCII 값
⁄⁄stream : 개방된 FILE 구조체의 포인터
⁄⁄반환값 : 읽은 문자 값(int), 파일 끝 또는 에러 시 EOF(-1)


※예제

#include <stdio.h>

#define print(n) printf( "%x %c\n", n, n )

int main( )
{	
	FILE *pFile = NULL;

	pFile = fopen( "d:\\Text.txt", "w" );
	if( pFile == NULL )
	{
		//에러 처리
	}
	else
	{
		int nResult = fputc( 'T', pFile );
		print( nResult );

		fclose( pFile );
	}

	return 0;
}


+ Recent posts