440 likes | 576 Views
Computer Graphics (Part 1: C/C++ Programming). HyunKi Hong Dept. of Image Eng., GSAIM ChungAng Univ. Contents. 상수와 변수 수식과 연산자 제어 명령문 파생자료형 ( 배열 , 포인터 , 구조체 등 ) C++ 함수 (Part 4 참조 ) 클래스와 객체. 함수 (function). C/C++ 언어는 함수의 집합. 매개변수 (parameter), 인자 : 함수에 입력시키는 것. 함수
E N D
Computer Graphics(Part 1: C/C++ Programming) HyunKi Hong Dept. of Image Eng., GSAIM ChungAng Univ.
Contents 상수와 변수 수식과 연산자 제어 명령문 파생자료형(배열, 포인터, 구조체 등) C++ 함수(Part 4 참조) 클래스와 객체
함수(function) • C/C++언어는 함수의 집합 매개변수(parameter), 인자 :함수에 입력시키는 것 함수 (printf, 사용자가 만든) void:값을 리턴하지 않는 함수 리턴값(return value) : 함수가 돌려주는 결과 값 표준 라이브러리 함수 (standard library function)
함수의 기본 개념 • 필요한 자료를 입력받아 임의의 처리한 후 결과를 되돌려주는 부프로그램 • 호출에 의해 일정 행위를 하고 그 결과를 보고하는 기능 단위 • 다른 언어의 subroutine의 개념과 비슷 • 모듈화 프로그램 가능 : 적절한 함수를 만들어 필요할 때마다 호출/사용 • main()함수, 라이브러리(표준)함수, 사용자정의함수 등으로 구분
C/C++언어의 함수 종류 • main함수 • 한 프로그램에 반드시 하나 존재 • 제일 먼저 실행되는 함수 • 사용자 정의 함수 • 사용자가 작성한 함수 • main함수나 다른 함수에 의해 호출됨 • 표준 라이브러리(library)함수 • header 파일(stdio.h, stdlib.h 등)에 선언되어 있음 • printf, scanf, getchar 등
main 함수 • C프로그램에 반드시 있어야 하는 함수 • 한 프로그램에 하나만 있어야 함 • 제일 먼저 실행되는 함수
표준 라이브러리 함수(standard library function) • 화면에 출력하는 함수를 프로그래머가 일일이 만들어야 하나? • 프로그래머를 위해서 만들어져 있는 함수 • 사용하기 전에 반드시 선언해야 함 • printf()라는 함수를 사용하려면 먼저 선언해야 함 • 선언하는 방법은 2가지 • 이 함수가 선언되어 있는 stdio.h라는 파일(표준헤더파일)을 프로그램에 포함시킴 : #include <stdio.h> • 직접 선언함: int printf(const char *, ...);
표준 헤더 파일(standard header file) • 라이브러리 함수들을 미리 선언해 놓은 파일 • 함수들을 기능별로 분류하여 정리해놓음 • printf, scanf는 stdio.h에 선언(원형)이 들어 있음 • Microsoft Visual Studio/VC98/include/*.h • 약 400여개의 .h가 있음(C 경우)
사용자 정의 함수 • 기본 명령이나 표준 함수 조합으로 원하는 처리할 수 없는 경우 void main() { float list[50]; readlist(list); sort(list); average(list); bargraph(list); } 숫자를 읽어 들인다. 숫자들을 정렬시킨다. 평균을 구한다. 막대 그래프를 그린다. 서술적인 함수명칭 사용해 기능, 구조 등을 알기 쉽게 모듈화, 재사용 가능, 공동 작업시 편리
함수의 정의 [return할 데이타형] 함수명(자료형1 인수1, 자료형2 인수2) { // 함수 블록의 시작 함수 안에서 사용하는 변수 선언; 실행문; // 처리 명령어들이 위치 return(함수값); // 반환되는 합수값이 있는 경우 필요 } // 함수블록의 끝 매개변수가 있으면 각 매개변수의 형을 쓰고 개수 만큼 컴마로 구분 함수 정의
함수와 반환값 • 함수는 한 개의 값만을 반환 • 반환값이 있는 경우, 함수 이름 앞에 반환되는 값의 자료형을 명시(반환값이 없는 경우, void 표시) • 함수본체에는 return() 함수 통해 값을 반환 int sum(int x, int y); 함수 선언:함수의 헤더 만으로 한 문장을 만듬 int sum(int x, int y) { int z; z=x+y; return(z); } 함수 정의 return(x+y);
함수의 인수(매개변수) 전달 • 함수들 간에 서로 데이터를 교환할 때 • 매개변수 전달 방법 • 값에 의한 호출(call by value) : C에서 기본 • 참조(주소)에 의한 호출(call by reference)
함수의 호출(1) • main() 함수에서 다른 함수를 호출하면, 프로그램 실행 제어가 호출된 함수로 넘어가 실행된 후, return문 만나면 실행 제어가 복귀되어 다시 main() 함수 실행됨. void main(void) { int a; display(); a = sum(3, 4); cout << a << endl; } #include <iostream.h> void display(void) { cout << "함수값이 없는 함수입니다.\n"; } int sum(int x, int y) { return(x+y); } 함수값과 가인수 없음. 당연히 return문 없음. 13/44
함수의 호출(2) #include <iostream.h> double reverse(int x, int y) { if(x == y && x == 0) return(0); else if(x>=y) return(1.0/(x+y)); else return(1.0/(y-x)); } void main(void) { cout<<reverse(3, 5)<<" : "<< reverse(5, 3)<< ":"<< reverse(5, 5)<<":"<<reverse(0, 0)<<endl; } 14/44
#include <iostream.h> int myfunction(int, float); void main(void) { …… myfunction(3, 4.0); …… } // End of the main() int myfunction(int x, float y) { …… } // End of the myfunction() #include <iostream.h> void main(void) { int myfunction(int, float); myfunction(3, 4.0); …… } // End of the main() int myfunction(int x, float y) { …… } // End of the myfunction() “함수 호출 이전에 함수 선언” 만족. 하지만 옆 프로그램과는 차이점 존재 함수의 원형(prototype) 선언(1) • 함수는 호출되기 전에 반드시 먼저 선언 • 함수를 main() 함수 뒤에 정의하면, 컴파일러에게 함수 존재를 알려주어야 함. • 함수의 원형을 함수 호출이전에 선언: 함수원형 선언(선언문 성격이므로 ‘;’ 사용)
정의(definition)와 선언(declaration) • 정의(선언을 겸한다) • 만든다. • 변수나 함수의 특성을 완전하게 지정해 주어야 함. • 변수나 함수를 위한 저장장소를 메모리에 할당. • 내부구조를 모두 보여준다. • 선언 • 알린다. • 기본 사용법 • 변수나 함수의 특성을 컴파일러에게 알려 주기만 함. • 사용 방법만 알려준다(이름, 입력, 출력)
함수의 원형(prototype) 선언(2) • 함수 호출 이전에 함수 원형의 선언이 생략되면, ‘선언되지 않은 식별어(undefined identifier)’ 오류 발생 • 함수 원형 선언시, 인수 리스트에 인수에 해당하는 자료형만을 나열해도 인식 가능 : (1) • 자료형과 함께 인수 이름까지 명시하면 프로그램 이해에 유리 : (2), (3) (1) int myfunction(int, float); (2) int myfunction(int x, float y); (3) int myfunction(int a, float b); 인수의 실제이름은 중요하지 않음.
인수의 전달방법(1): 값에 의한 호출(Call by vale) • 호출 함수에서 변수 또는 객체의 값만을 전달. 즉, 호출함수의 실인수 값이 단순 복사되어 피호출 함수의 가인수에 전달 • 실인수와 가인수는 서로 다른 독립적인 변수나 객체 #include <iostream.h> int sqr(int); // 함수원형 main(void) { int result, x; x = 10; result = sqr(x); cout << x << "," << result << endl; } int sqr(int x) { x++; return(x*x); } 실인수 값은 여전히 10 (가인수와 무관) 가인수 값을 의도적으로 수정 18/44
call by value 예제 1 #include <iostream.h> int sum(int x,int y); void main() { int a=2,b=5,c; c=sum(a,b); //실제로는 이렇게 호출함 sum(2,5); cout<<c; } int sum(int x, int y) { return(x+y); } c=sum(a, b); 실매개 변수 변수의 실제 값 전달 int sum(int a, int b) { return(a+b); } 형식매개 변수 x로 a가 대입되는 것이 아님 a의 실제 값인 2가 대입됨 19/44
call by value 예제 2 #include <iostream.h> int sum(int x,int y); //int sum(int, int); void main() { int a=2, b=5, c; c=sum(a,b); cout<<a<< b<<c; //2,5,14 } int sum(int a,int b) //a에 2, b에 5가 넘어옴 { a+=2; //여기의 a와 main함수의 a는 전혀 다른 변수임 b+=5; cout<<a<<b; return(a+b); } 20/44
인수의 전달방법(2): 주소에 의한 호출(Call by address) • 호출 함수에서 피호출 함수로 변수 또는 객체의 포인터를 전달하는 방법 • 실인수는 변수의 주소값이 되고, 가인수는 이 주소를 받을 수 있는 포인터 변수 #include <iostream.h> int facto(int, int *); // 함수원형 main(void) { int n, count = 1; cin >> n; cout << "함수 호출 전 count 값 = " << count << endl; cout << "Fractorial of " << n << " : " << facto(n, &count) << endl; cout << "함수 호출 후 count 값 = " << count << endl; } int facto(int x, int *fac) { int i = 1; while(i++ < x) *fac *=i; return(*fac); } 주소값을 전달 → 실인수 count와 가인수 fac은 동일한 기억장소의 주소값을 공유 가인수 값 변경 → 호출함수의 실인수 값 변경 21/44
call by reference • 실매개변수의 주소를 피호출 함수의 변수인 형식매개변수로 전달 • 형식매개변수가 변하면 실매개변수도 변함 • return 값이 여러 개이거나 배열 전체를 전달하는 경우 사용 • 함수 호출할 때 : swap(&a, &b) • 함수 정의 시 : swap(int *a, int *b)
call by value vs. call by reference #include <iostream.h> void swap(int *, int *); void main() { int a=2,b=5; cout<<a<<b<< '\n'; swap(&a,&b); cout<<a<<b<< '\n'; } void swap(int *pa, int *pb) { int temp; temp=*pa; *pa=*pb; *pb=temp; cout<<*pa<<*pb<< '\n'; } #include <iostream.h> void swap(int, int); void main() { int a=2,b=5; cout<<a<<b<<'\n' ; swap(a,b); cout<<a<<b<< '\n'; } void swap(int a,int b) { int temp; temp=a; a=b; b=temp; cout<<a<<b<< '\n'; } 실매개변수를 주소로 전달 형식매개변수가 포인터 변수 23/44
call by reference 예제 #include <iostream.h> void ss(int, int, int*, int*); void main() { int a=2,b=5; int sum,sub; cout<<sum<<sub<<‘\n’; //쓰레기 값 ss(a,b,&sum,&sub); cout<<sum<<sub<<‘\n’; //7 -3 } void ss(int a,int b, int*psum, int *psub) { *psum=a+b; *psub=a-b; } SS()함수를 호출 후 변화되는 값은 2개 SS()함수는 리턴값이 없지만 실제로는 2개의 값이 리턴되는 것과 같은 효과 main()의 sum변수의 주소를 받는 포인터 변수 24/44
call by reference 2가지 방법 #include <iostream.h> void swap(int *, int *); void main() { int a=2,b=5; cout<<a<<b<< '\n' ; swap(&a,&b); cout<<a<<b<< '\n'; } void swap(int *pa, int *pb) { int temp; temp=*pa; *pa=*pb; *pb=temp; cout<<*pa<<*pb<< '\n'; } #include <iostream.h> void swap(int &, int &); void main() { int a=2, b=5; cout<<a<<b<< '\n'; swap(a,b); cout<<a<<b<< '\n'; } void swap(int &ra, int &rb) { int temp; temp=ra; ra=rb; rb=temp; cout<<ra<<rb<< '\n'; } 포인터를 사용하는 방법 참조자를 사용하는 방법 25/44
구조체 변수의 매개변수 전달 call by value call by reference #include <stdio.h> struct score{ int kor, eng; }; int hap(score hkd); void main() { score jumsu={80,90}; int sum; sum=hap(jumsu); printf("%d",sum); } int hap(score hkd) { return(hkd.eng+hkd.kor); } #include <stdio.h> struct score{ int kor, eng; }; int hap(score *hkd); void main() { score jumsu={80,90}; int sum; sum=hap(&jumsu); printf("%d",sum); } int hap(score *hkd) { return(hkd->eng+hkd->kor); } 26/44
인수의 전달방법(3): 참조에 의한 호출(Call by reference) • 피호출 함수의 가인수를 참조자(reference)로 선언하고 호출함수에서 실인수로 변수 이름만 쓰면, 호출함수의 실인수 사용 가능 • 실인수와 가인수는 서로 다른 독립적인 변수나 객체 #include <iostream.h> void swap(int &, int &); void main(void) { int a = 3, b = 7; cout << "a =" << a << ", b =" << b << endl; swap(a, b); cout << "a =" << a << ", b =" << b << endl; } void swap(int &x, int &y) { int temp; temp = x, x = y, y = temp; } 가인수(x, y)는 실인수에 대한 참조자가 되어 가인수를 통해 실인수를 간접적으로 조작 가능 27/44
인수의 전달방법 예제 #include <iostream.h> void Callvalue(int x, int y) // 일반 가인수 { cout << "Value : " << x++ << ", " << y++ << endl; } void Calladdress(int *x, int *y) // 포인터형 가인수 { cout << "Addess : " <<(*x)++ << ", " << (*y)++ << endl; } void Callreference(int &x, int &y) // 참조형 가인수 { cout << "Reference : " << x++ << ", " << y++ << endl; } void main(void) { int a = 3, b = 4; Callvalue(a, b); Calladdress(&a, &b); Callreference(a, b); cout << "최종값 : " << a << ", " << b << endl; } 28/44
객체지향 프로그래밍의 등장 배경 • 소프트웨어 위기(software crisis) - 컴퓨터 기술 발전에 따른 소프트웨어 기능 요구가 다양화 a. 프로그램(소프트웨어)의 대형화 및 복잡화 b. 새로운 프로그램의 개발에 많은 노력과 비용 소비 c. 작성 프로그램 관리 및 수정하는 유지보수가 복잡 프로그램 설계/구현시 효율이 떨어지고 생산성이 낮아진다. - 컴퓨터를 구성하는 하드웨어와 소프트웨어의 비용 a. 1960년대 약 4 : 1 정도 b. 1980년 이후 약 90%를 소프트웨어 비용이 차지 • 소프트웨어를 효율적으로 설계/구현하는 방법론 필요
구조적 프로그래밍(structured programming) 언어 • Divide and Conquer(나누어 정복) • 프로그램의 각 부분을 독립적인 여러 모듈(module)로 나누어 작성 • Pascal, C와 같은 언어 • 처리 동작(명령,연산)에 중점을 두어 프로그램을 작성 • 자료가 프로그램 전체에 노출 • 자료와 처리 동작을 별도로 구분하여 처리동작과 자료 사이의 관계가 서로 밀접한 연관성을 갖지 못함. • 프로그램이 복잡해지면 디버깅 및 유지보수가 어려워짐.
구조적 프로그래밍 자료 자동차, 차, 사과, 물건, 배 처리동작 소유하다 타다 먹다 읽다 맛있다 객체 지향 프로그래밍 물건 자동차 사과 책 배 소유하다 타다 먹다 읽다 맛있다 구조적 프로그래밍 기법과 객체지향 프로그래밍 • 객체 지향 프로그래밍 - 구조적 프로그래밍 기법을 계승하고 보다 발전시킨 개념 - 자료와 처리동작을 하나로 묶어 다루는 객체(object)개념 도입 - 소프트웨어 확장(extensibility) 및 재사용(reusability) 기회 증가
클래스 클래스 개 개 객체 객체 객체 객체 객체 객체 멍멍이 해피 메리 멍멍이 해피 메리 클래스 vs. 객체 int int x int y int z
캡슐화(encapsulation), 다형성(polymorphism), 상속성(inheritance) • 외부간섭, 오용으로부터 코드, 자료를 안전하게 유지 • 서로 다른 자료형을 처리하는 같은 이름의 함수 정의 가능, 함수 다중정의(overloading)라고 함. • 한 객체가 다른 객체의 특성을 이어받는 과정=파생 클래스가 기본 클래스의 정의된 속성(자료, 연산)을 상속받는 것 • 파생 클래스(derived class), 자식 클래스(child class) : 상위 클래스의 속성을 상속받은 하위 클래스 • 기본 클래스(base class), 부모 클래스(parent class) : 상위 클래스
자료 자료 연산(함수) = + 연산(함수) 객체 객체 = 프로그램 + 객체(object) • 객체는 자료와 이를 처리하는 동작인 연산(함수, method)을 하나로 묶어 만든 요소 • 프로그램을 구성하는 실체 • 객체란 자료를 표현하는변수만을 가지는 것이 아니라 그 객체가 무엇을 할 수 있는가를 정의한함수도 구성 • 인스턴스(Instance) • 어떤 클래스에서 생성된 객체 혹은 한 클래스에 속하는 각각의 객체
클래스(class) • 객체지향 프로그래밍의 특성인 캡슐화, 다형성, 상속성을 제공하는 사용자 정의 자료형 • 자료의 특성(attribute)과 상태(state)를 나타내는 기본 자료들과 그 자료들의 처리방법을 포함 • 클래스는 유사한 객체들이 갖는 공통된 데이터와 함수들을 정의한 객체의 기본 규격(specification)이다. • 클래스 구성하는 변수와 함수: 클래스의 멤버자료와 멤버함수 변수 함수
클래스 선언(declaring a class) 예약어 클래스 내부/외부에서의 접근특성 결정 class클래스명{ // class라는 keyword 사용 속성: // private이나 public, protected가 올 수 있음 // private: 비공개 멤버 자료 및 함수 // protected: 보호 멤버 자료 및 함수 // public: 공개 멤버 자료 및 함수 자료선언; // 멤버변수 속성: // 생략하면 private 함수선언; // 멤버함수 } 객체변수; // 객체를 만드는(정의) 첫 번째 방법 클래스명 객체변수; // 객체를 만드는 두 번째 방법
클래스 선언 예 class Date { private : int year, month, day; // 비공개형 멤버자료 public : void setdate(int yy, int mm, int dd) // 공개형 멤버함수 { year = yy; month = mm; day = dd; } void display() { cout << year << . << month << . << day; } }; 비공개형 자료들에 접근 가능 멤버자료는 자료 은닉 위해 비공개형으로 정의, 변수 설정/출력 위한 함수는 외부에서 접근하도록 공개형으로 정의
객체 정의 • 객체: 정의된 클래스형에 대해 선언된 변수로 클래스의 구체적 표현이며 그 실체를 의미 • 클래스 이름으로부터 객체를 직접 정의 Date 클래스의 객체 정의 → Date birthday2, wedday2; //Date 클래스형 객체 변수 정의 : 새로운 자료형 cf. int birthday1, wedday1; // int 형 변수 선언 형태가 서로 유사
클래스 멤버의 호출 • 객체의 공개된 멤버 자료나 함수를 호출할 때 객체이름 다음에 ‘.’ 와 멤버자료 또는 이름 Date birthday; birthday.setdate(2005, 11, 3); birthday.display(); 멤버결합 연산자
멤버함수의 정의방법 • 일반함수와 같이 멤버함수들도 클래스 정의부 내부에는 해당 멤버 함수의 원형만 쓰고 클래스 외부에 멤버 함수 정의부 작성 가능 → 어느 클래스에 속한 멤버 함수를 알리기 위해 클래스 이름과 영역지정 연산자 ‘::’ 사용 함수값형 소속 클래스 이름::멤버함수이름(인수 리스트) { … // 함수 정의 }
클래스 선언부 외부에서 멤버함수의 정의방법 class Date { private : int year, month, day; // 비공개형 멤버자료 public : void setdate(int yy, int mm, int dd) // 공개형 멤버함수 void display() }; void Date::setdate(int yy, int mm, int dd) { year = yy; month = mm; day = dd; } void Date::display() { cout << year << “ .” << month << “ .” << day; } 공개형 멤버함수 원형만 소개 멤버함수 실제 정의 : 함수 정의부 길이가 길어지면 이와 같이 정의
생성자(constructor) • 생성자(클래스 객체가 생성될 때 수행) - 함수의 반환형을 쓰지 않고 클래스 이름과 같은 이름의 함수를 클래스 공개지역(public)에 정의하는 멤버함수 - 전달 인수나 정의 등은 일반 함수와 동일 - 반환값이 없기 때문에 함수 본체 내에 return문이 없음. - 객체 생성시, 임의 값으로 초기화 등을 수행 class Date { private : int year, month, day; public : Date (int yy, int mm, int dd); }; Date::Date(int yy, int mm, int dd) { year = yy; month = mm; day = dd; cout << “Date 객체가 생성됩니다.\n”; }
소멸자(destructor) • 소멸자(객체 사용이 끝났을 때 수행되는 멤버 함수) - 클래스 이름과 같은 이름을 쓰되 그 앞에 ‘~’ 사용 - 생성자와는 달리 각 클래스는 단 한 개의 소멸자만 가지며, 전달인수를 갖지 않음. - 클래스의 공개 지역(public)에 선언되는 멤버 함수 - 반환값이 없기 때문에 함수 본체 내에 return문이 없음. class Date { private : int year, month, day; public : ~Date (); }; Date::~Date() { cout << “Date 객체가 소멸됩니다.\n”; }
객체 소멸 순서 void Date::SetDate(int yy, int mm, int dd) { year = yy; month = mm; day = dd; cout << year << "." << month << "." << day ; cout << ": 로 변경되었습니다.\n"; } void Date::Display() { cout << year << "." << month << "." << day << ": 입니다.\n"; } void main() { Date d1(1111, 11, 11), d2(2222, 22, 22), d3(3333, 33, 33); d2.SetDate(1234, 12, 34); d3.Display(); } #include <iostream.h> class Date { private : int year, month, day; public : Date (int yy, int mm, int dd); ~Date(); void SetDate(int yy, int mm, int dd); void Display(); }; Date::Date(int yy, int mm, int dd) { year = yy; month = mm; day = dd; cout << year << "." << month << "." << day ; cout << ": Date 객체가 생성됩니다.\n"; } Date::~Date() { cout << year << "." << month << "." << day ; cout << ": Date 객체가 소멸됩니다.\n"; } 컴파일러가 소멸자 호출하면서 생성된 객체를 역순으로 소멸 44/44