Showing posts with label c language. Show all posts
Showing posts with label c language. Show all posts

Monday, September 27, 2010

C++ part 4 (객체지향의 완성)

이제 마지막 파트이다. 아마도 C++을 시작한지 2주쯤 되는거 같다. 확실히 oop를 다시 보는거라 처음 자바로 시작할때보다 기억이 오래가고 이해가 빠르다. 마지막 4장을 향해서 고고~! 


chapter10 : 연산자 오버로딩
연산자 오버로딩은 일종의 약속같은 것으로 기존의 연산자에 프로그래머가 추가적인 기능(새로운 피연산자등)을 확장시키는것. 예를 들어 p라는 객체가 있으면 p+3 과 같은 연산이 가능하게끔 연산자 +에 대해 새로히 연산자를 오버로딩하는 것이다.
연산자 오버로딩하는방법에 두가지가 있다. 한 코드의 예를 가지고 각각의 방법으로 연산자 오버로딩하는 것을 설명하겠다. main함수에 Point p1(1,2); Point p2(2,1); Point p3=p1+p2 라는 코드가 있다면... 
1.멤버 함수에 의한 오버로딩 : 클래스의 멤버 함수로 연산자를 오버로딩 하는 것으로 위를 위한 코드는 class Point{public:Point operator+(const Point& p){Point temp(x+p.x,y+p.y);return temp;}} 과 같이 연산자 오버로딩을 위한 함수를 클래스 Point 안에 명시한다. 위의 마지막 문장 Point p3=p1+p2는 Point p3=p1.operator+(p2) 으로 해석된다. 기억해야 할것은 main함수에 p3=p1+p2에서 p1.operator+(p2)가 호출되고 결과인 temp 객체가 리턴되서 default 복사 생성자에 의해 p3이 생성되는 것이다.
2.전역 함수에 의한 오버로딩 : 연산자 오버로딩을 위한 함수를 전역 함수로 명시하는 것으로 위의 경우 Point operator+(const Point& p1, const Point& p2){Point temp(p1.x+p2.x,p1.y+p2.y); return temp;} 식으로 전역 함수로 표현할수 있다. 단 객체의 멤버 변수를 직접 접근하므로 operator+를 friend로 지정해야 한다. 이때 main의 마지막 문장 Point p3=p1+p2는 Point p3=operator+(p1,p2)를 의미하게 된다.
필자에 따르면 객체지향에서는 전역이라는 개념이 없기 때문에 멤버 함수로의 연산자 오버로딩을 권한다.
단항 연산자 오버로딩은 위의 이항 연산자랑 매개변수가 하나씩 적다는것 빼고 동일하다. 기억해야 할것은 '++'를 오버로딩할때 이를 멤버 함수로 구현해보면 Point& operator++(){x++;y++;return *this;}이다. 여기서 return 이 존재 하여야 하고 그 type이 reference 이여야 한다. p라는 객체가 있을 때 (++(++p)).ShowPosition()을 고려해 보면 return이 없으면 ++한뒤의 돌아오는 결과물이 없게 되서 에러가 나고 type이 reference가 아니면 돌아오는 것이 객체의 copy가 돌아오기때문에 정작 p의 값은 2가 더해진 것이 아니라 1이 더해지게된다(++p는 p.operator++()로 p++는 p.operator++(int)로 구분된다). 
연산자 오버로딩을 통해 객체에 더하기가 가능할때, 즉 p+3, 교환법칙(3+p)가 가능하게 하기 위해서는 어쩔수 없이 전역 함수에 의한 오버로딩으로 구현해야 한다.
cout 은 ostream의 객체이고 cin은 istream의 객체이고 std라는 namespace에 존재하는 것이다. 그리고 <<나 >> 역시 연산자일 뿐이다. 그래서 cout<<"aa"라고 하는 것은 ostream클래스의 객체인 cout의 operator<<()를 호출하는 것이다.

Saturday, September 25, 2010

C++ part 3 (객체지향의 전개)

chapter7 : 상속의 이해
책에서 상속의 개념이 필요한 문제를 제기, "employee problem"(이 문제를 간략하게 요약하자면 회사가 있을때 처음에는 고용형태가 permanent 밖에 없었는데 회사가 커지면서 다른 형태의 고용형태가 생겼을때(예를 들어 part time, sales permanent) 새로운 형태의 고용에 대한 클래스를 만드는 것은 당연하나 이 모두를 담기위한 department 클래스의 변형이 불가피 하다. 이럴때 상속의 개념을 이용하면 department의 클래스 변형 없이 가능하게 한다라는 내용).
student하는 클래스가 person이라는 클래스를 상속할때 student 클래스의 정의시 다음과 같이 서술한다. class student : public person {클래스 내용...}. 여기서 student를 sub 클래스 (혹은 derived 클래스)라고 하며 person을 super 클래스 (혹은 base 클래스)라고 한다.
상속하고 있는 클래스의 객체 생성 과정: 1.메모리 공간의 할당, 이때 상속되는 클래스를 감안해서 메모리 공간 할당. 2.base 클래스의 생성자 실행. 3.derived 클래스의 생성자 실행.(추가적으로 설명하자면 derived 클래스가 생성될때 derived 클래스의 생성자가 먼저 호출이 되나 그 생성자의 몸체({}부분)가 실행하기에 앞서 이 클래스가 base 클래스를 상속하고 있음을 알고 base 클래스의 생성자를 호출해서 실행하고 그 다음에 derived 클래스의 생성자의 몸체가 실행이 된다.)
AAA 클래스의 생성자가 여러개일때(함수 오버로드) derived 클래스인 BBB 클래스의 생성자가 특정 AAA 클래스의 생성자를 호출하고자 할때 BBB(int j) : AAA(j) {BBB 생성자 내용} 식으로 선언한다(이는 앞서 배운 const 맴버 변수를 초기화 할때 사용하는 member initializer 와 같은 형태이다). 
base 클래스의 맴버 변수가 private으로 선언되어 있을 시에는 위와 같이 member initializer방식으로 base 클래스의 초기화 하고 public으로 선언되어 있을때는 base 클래스의 맴버 변수를 직접 초기화도 가능하다.
상속된 개체의 소멸과정은 생성과정과 반대로 derived 클래스의 소멸자가 먼저 실행되고 base 클래스의 소멸자가 그 다음으로 실행된다.
protected 멤버는 상속 관계에 놓여 있는 경우에 접근을 허용한다는 것을 제외하고 private 멤버와 동일하다. 
상속에는 세가지 형태 public, private, protected가 있는데 각각의 형태에 따라 상속되는 과정에서 접근 권한이 변경된다(아래 표와 같다).
control 클래스 : 기능적 성격이 강한 클래스. 프로그램의 주요기능을 설명한다.
entity 클래스 : 데이터적인 성격이 강한 클래스. 저장되어야 하는 최소 단위 데이터로 인식 



chapter8 : 상속과 다형성
상속의 조건 :: 1.IS-A관계 : "A student is a person" 의 문장에서 student 클래스와 person 클래스는 IS-A 관계에 있다.곧 student가 person 클래스를 상속하게 된다. 2.HAS-A관계 : "The police have a cudgel"문자에서 police 클래스는 cudgel클래스와 HAS-A관계에 있다. 곧 police 클래스는 cudgel 클래스를 상속하게 된다.
그러나 HAS-A관계는 상속말고도 다른 방식으로 표현이 가능하다(police 클래스 안에 cudgel 클래스 맴버 변수 선언 혹은 cudgel 클래스를 가리키는 포인터 변수 선언). 그렇기에 거의 대부분의 클래스 상속은 IS-A관계를 표현하기 위해 사용된다.
Design pattern이란? 문제 상황에 따른 좋은 클래스 디자인 모델의 집합
CCC 클래스가 BBB 클래스를 상속하고 있고(CCC->BBB) BBB클래스가 AAA 클래스를 상속하고 있다면(BBB->AAA) AAA객체 포인터에 BBB객체와 CCC 객체의 주소를 넣을수 있다(AAA* t1=new BBB; AAA* t2=new CCC 가능). 곧 base 객체 포인터를 이용해서 derived 클래스의 객체의 주소를 담을 수 있다. 그러나 이때 base 객체 포인터로 정의된 변수는 base 클래스 내에 선언된 멤버만 접근이 가능하다(객체 레퍼런스도 마찬가지이다).
이를 해결하기 위한 방법이 virtual 키워드를 이용한 함수 오버라이딩.
함수 overriding : base 클래스에 선언된 형태의 함수를 derived 클래스에서 다시 선언하는 현상
virtual 키워드 : base 클래스(AAA)가 func이라는 이름의 함수를 virtual라는 키워드로 가상으로 선언하게 되면(virtual func(func매개변수){func의 함수 내용}) base 클래스의 객체 포인터로 derived 클래스(BBB)의 객체를 가리킬때(AAA* t1 = new BBB), 그 객체 포인터로 base 클래스의 func 함수에 접근하게 되면 자동적으로 derived에 오버라이딩하는 함수를 호출하게 된다. 이러한 형식의 함수 호출을 dynamic binding이라 한다(컴파일 되는 동안에 호출될 함수가 결정되는 것이 아니라 실행하는 동안에 호출될 함수가 결정되기때문).
다형성이란? 모습은 같은데 형태가 다른것 (dynamic binding, 함수 overriding등)
그렇다면 overriding 당한 함수는 어떻게 호출할수 있나? base 함수::virtual 함수 이름(AAA::func())으로 호출 가능하다.
virtual 키워드를 이용한 가상함수는 그 특성이 상속된다. 예를 들어 base 클래스 AAA에 virtual func으로 가상화된함수가 있고 derived 클래스 BBB에 함수 overriding을 위해 func 함수가 있을때 BBB 클래스의 func 함수도 virtual이란 키워드를 붙여 주지 않았다고 하더라도 저절로 가상화가된다.
이렇듯 가상화된 함수는 상속하는 클래스의 함수 오버라이딩을 위한 것이라서 base 클래스에 virtual 함수에 내용을 지정할 필요가 거의 없다. 그렇기 때문에 이러한 가상함수는 순수 가상 함수로 선언한다(virtual int func()=0 과 같이 =0을 붙이면 pure virtual function임을 나타내는 것이다). 이러한 순수 가상 함수를 포함하고 있는 base 클래스를 추상(abstract) 클래스라고 한다. 추상 클래스는 객체화되지 못한다.
가상함수 말고도 소멸자 역시 virtual 키워드를 이용해서 가상화 되어야 한다. 예를 들어 base 클래스(AAA)와 derived 클래스(BBB)가 있고 AAA* t1 = new BBB로 선언된 객체 포인터 t1이 있을때 t1을 소멸하게 되면 AAA 클래스의 소멸자만이 호출되어 BBB안에 있을지 모르는 동적메모리가 free되지 않는다. 이를 방지 하기 위하여 소멸자 역시 가상화 하는 것이 좋다(두 클래스의 소멸자의 이름은 다르나 소멸자라는 특성때문에 이름이 다르더라도 overriding된다),
8장에서 배운 내용을 가지고 7장의 employee problem을 완벽히 해결할수 있다(기본적으로 employee라는 base 클래스를 만들고 permenant와 parttime, salesperment클래스를만들어서 employee 클래스를 상속한다. employee클래스안에 permenant등 상속될 클래스의 각각의 구현이 다른 맴버함수에 대해서 virtual키워드로 가상화 시키면 contol클래스에서 employee 객체 포인터로 클래스들을 선언하더라도 각각의 클래스의 맴버 함수에 접근할수 있게 되어진다).

chapter9 : virtual의 원리와 다중 상속
객체가 생성되면, 멤버 변수는 객체 내에 존재한다. 그러나 멤버 함수는 메모리의 한 공간에 존재하면서, 모든 객체가 공유하는 형태를 취한다.
1개의 가상 함수를 포함하는 클래스에 대해서, 컴파일러는 가상 함수 테이블(virtual table,VTable)이라는 것을 만들어 준다. 이 VTable는 key로 함수의 이름이 들어가고 value로 그 함수가 메인 메모리에 위치한 메모리 주소를 갖게 된다. 함수가 overriding 되었을때 derived 클래스의 VTable에는 overriding된 base 클래스의 virtual 함수는 포함되어 있지 않는다.
하나 이상의 가상 함수를 멤버로 지니는 클래스의 객체에는 VTable을 위한 포인터가 멤버로 추가된다.
이렇듯 가상 함수를 지니는 클래스의 객체가 생성되어지고 그 객체의 함수가 호출되어지면 VTable을 가리키는 포인터를 참조해서 VTable의 내용을 읽게 되고 VTable안에 존재 하는 key로 함수를 찾게 되어 value(함수의 메모리 상의 위치)를 가져오게 된다. 곧 base 클래스의 virtual 함수가 derived 클래스에 의해 overriding되어지면 derived 클래스의 객체의 VTable에는 base 클래스의 virtual 함수에 대한 정보가 없기 때문에 derived 클래스의 함수가 호출되는 것이다.
가상함수가 없으면 VTable이 생성되지 않고 직접 호출할 함수에 접근하기때문에 성능은 좋아진다.
다중 상속에 대한 이야기도 있지만 필자가 말하듯 굉장히 모호해질수 있는 문법인지라 이 내용은 생략한다.

Monday, September 20, 2010

C++ part 2 (객체지향의 도입)








chapter3 : 클래스의 기본
C++에서는 구조체(struct)를 이용하여 변수를 선언할때 struct를 붙이 않아도 된다(C에서는 반드시 struct 를 붙여야 했고 이를 피하기 위해 구조체를 선언할때 typedef를 이용하였다).  C++에서는 구조체에 함수를 넣을 수 있는데 이는 구조체가 class의 범주에 속하기 때문이다.
클래스 = 맴버변수 + 멤버함수
-데이터 추상화 : 현실세계의 사물을 데이터적인 측면과 기능적 측면을 통해서 정의하는 것.
-클래스 : 추상화된 데이터를 가지고 자료형을 정의 하는 것
-객체 : 클래스를 이용해서 정의된 자료형의 변수
내부접근은 같은 클래스 내의 존재하는 맴버에 의해 접근되는것, 이 이외는 외부 접근이라 한다.
private 으로 맴버가 선언되면 내부접근만 허용되는 것이고 public으로 선언되면 외부접근까지 허용하는 것이다. private도 public도 없으면 private 으로 간주한다 (단 구조체일 경우 default는 public이다).
 클래스의 맴버함수가 전달 인자가 없을때 void는 보통 생략한다, 클래스의 내부를 간결하기 위하여 함수는 외부에서 정의가 가능하다(예를 들어 aa 라는 클래스가 있으면 그안에 bb라는 맴버함수가 잇을때 class aa{public: void bb();} 라고만 해놓고 외부에 aa::bb(){}식으로 외부에서 bb를 정의할수 있다). 클래스의 맴버 함수를 내부 선언 한다는 것은 그 함수를 in-line화 하고자 하는 것이다. 맴버 함수를 외부에서 정의 했을 때에 in-line화 하고 싶을 때는 외부 정의시 앞에 inline을 붙여 주면 된다.

chapter4 : 클래스의 완성
캡슐화와 정보은닉을 충족하는 클래스가 좋은 클래스이다. 
캡슐화 : 관련 있는 데이터와 함수를 하나의 단위로 묶는 것.
정보은닉 : 객체 외부에서 객체의 맴버 변수에 직접적인 접근을 허용하지 않는것.
정보은닉을 위해 멤버변수의 접근을 위한 맴버함수들을 access method라고 한다.
C++에서는 클래스 선언및 정의 부분에서 맴버 변수를 초기화 할수 없다(java와 c#은 가능).
일반적으로 클래스 객체를 생성과 동시에 초기화 할수 없다(물론 맴버 변수가 public이라면 상관없다). 이럴 때 이용하는 것이 생성자 (constructor)이다. 
생성자 : 1.함수이다, 2.클래스의 이름과 같은 이름을 지닌다, 3.리턴하지도 않고 리턴 타입도 선언되지 않는다.
클래스에 생성자가 하나도 정의되어 있지 않을시 default 생성자가 자동 삽입된다. 생성자 역시 함수 이므로 오버로딩이 가능하다. 디폴트 매개 변수 설정 역시 가능하다.
동적메모리가 할당된 클래스는 클래스의 객체의 소멸시 메모리를 반환해야 하는데 이를 자동화하기 위해 이용될 수 있는 것이 destructor(소멸자)이다.
소멸자 : 1.함수이다, 2.클래스의 이름 앞에 ~가 붙은 형태의 이름을 지닌다, 3.리턴하지도 않고 리턴타입도 선언되지 않는다, 4.매개변수를 받을 수 없다.따라서 오버로딩,디폴트 매개 변수 선언이 불가능하다.
소멸자 역시 생성자처럼 아무런 선언이 없을시 디폴트 소멸자가 삽입된다.
생성자 소멸자의 결론 : 클래스를 정의할때에 멤버 변수를 초기화를 위해서 생성자를 항상정의한다. 반면 객체 소멸 시 처리해야할 것(동적 메모리를 해제)이 있을때만 소멸자를 정의한다.
객체를 동적 메모리에 할당해서 생성과 소멸 시에 new와 delete가 사용된다. malloc과 free는 사용될수 없는데 이는 malloc과 free는 단순히 힙 영역에 메모리를 할당할 뿐이지 객체 생성시 필요한 생성자와 소멸시 필요한 소멸자를 호출하는 기능이 없기 때문이다.
this는 자기 참조 포인터로 이름 그대로 자기 자신을 가리키는  용도의 포인터이다.
private으로 선언된 맴버 변수는 원래 외부 접근이 불가능하나 friend로 선언된 전역 함수나 다른 클래스에 의해서 맴버 변수의 외부 접근이 가능해 진다.

chapter5 : 복사생성자
변수 초기화 하는 방법 int val = 20 이렇게 하는 방법이 있고, int val(20)이렇게 하는 방법이 있다. 첫번째 것이 두번째 것으로 묵시적 변환이 일어난 것이다.
복사 생성자 : 자기 자신과 같은 형태의  객체를 인자로 받을 수 있는 생성자 (예, class AA{public: AA(AA& a){}}) 
복사 생성자를 정의 할때 매개변수에 &(레퍼런스) 기호를 붙여야 한다. 그렇지 않으면 무한 루프에 빠지게 된다(레퍼런스화 되어 있지않다면 무한 반복해서 계속 복사 생성자를 call할 것이다.복사 생성자가 호출되는 시점을 고려해 보면 알수 있다).
복사 생성자 역시 정의 해주지 않아도 디폴트 복사 생성자가 생기나 이럴땐 shallow copy를 하기때문에 객체가 배열이나 스트링처럼 주소를 담고 있는 맴버 변수가 있을때 주소를 카피 하기 때문에 문제가 있을 수 있다. 이럴 땐 직접 복사 생성자를 정의해서 deep copy를 해야 한다.
복사 생성자가 호출되는 시점 : 1.기존에 생성된 객체로 새로운 객체를 초기화하는 경우, 2.함수 호출 시 객체를 값에 의해 전달하는 경우, 3.함수 내에서 객체를 값에 의해 리턴하는 경우.

chapter6 : static 멤버와 const 멤버
const 키워드의 복습 : 1.const double PI=3.14라고 선언시 PI=3.1415라고 하면 error,즉 const라고 지정한 변수는 값을 변화 할수 없다. 2. const int* pN=&n이라고 하고 *pN=20이라고 하면 error, 즉 const화한 포인터 변수는 자신이 가르키는 값을 변화 시킬수 없다(포인터 변수를 통해서 값을 바꿀수 없다). 3.int* const pN=&n1이라고 하면 *pN=20은 가능하나 pN=&n2를 하면 error, 즉 pN이 가리키는 주소가 저장한 value는 변경할수 있으나 pN이 가리키는 주소값 자첼 바꿀수는 없다.
클래스에서 값이 변하지 않는 맴버변수는 const를 지정하면 좋다. 이러한 const화 된 맴버 변수를 초기화 하기 위해서 member initializer라는 기법이 필요하다. 생성자이름(매개변수들) : const 맴버 변수(매개 변수) 꼴로 초기화 한다(예 student라는 class가 있고 그안에 id가 const int화 되어 있으면 id를 초기화 하기위한 생성자는 student(int _id) : id(_id) 가 되어야 한다).
const 함수도 만들수 있다. const화 된 함수는 맴버 변수를 참고 할수는 있으나 조작은 불가능하다. 또한 const화된 함수는 맴버 변수의 포인터를 리턴 할수도 없으며(리턴되는 포인터를 const화 시키면 가능하다, 곧 const char* func() const 라고 리턴되는 타입 char*을 const화 시킨면 가능) const화 되지 않은 함수를 호출할 수도 없다. 이는 다른 경로를 통한 맴버 변수의 조작을 방지하기 위함이다.
const 키워드를 이용해 객체도 상수화가 가능하다. 그렇게 되면 객체의 맴버 변수의 조작이 불가능 할뿐만 아니라 상수화된(const화된) 맴버 함수만 호출이 가능해 진다.
상수함수냐 아니냐에 따라 함수 오버로딩이 가능하다(매개변수가 같더라도 상수화냐 아니냐에 따라 다르게 인식).
클래스의 static 맴버의 특징 : 1.main 함수가 호출되기도 전에 메모리 공간에 올라가서 초기화된다. 따라서 public으로 선언이 된다면, 객체 생성 이전에도 접근이 가능하다(거꾸로 생각하면 static 변수를 private화하면 객체에 의한 접근만 가능해진다). 2.객체의 멤버로 존재하는 것이 아니다. 다만 선언되어 있는 클래스 내에서 직접 접근할 수 있는 권한이 부여된 것이다.
static 맴버변수, static 맴버함수는 다른말로 class 변수, class 함수라고 한다.
explicit 키워드 : 생성자 앞에 explicit를 써주면 묵시적인 생성자 호출을 허용하지 않는다는 의미이다(AA 라는 클래스가 있을때 explicit AA(int _a){}라고 생성자가 explicit 키워드가 붙어있으면 AA a(10)은 가능하나 AA a=10은 불가능)
mutable 키워드 : mutable로 선언된 맴버 변수는 맴버함수가 const라고 선언되었더라도 변경이 가능하다.

Thursday, September 16, 2010

C++ part 1 (C와 C++의 기본적 차이)

연대에 있는 Bernd Burgstaller의 parallel programming 수업자료 중에 computational history에서 3개의 crisis를 설명한다.
1960에서 1970년 사이에 사람들은 assembly 언어를 사용하였다. 그 당시 각각의 하드웨어가 자신 고유의 assembly언어가 있었기 때문에 프로그램의 다른 하드웨어로의 이식이 불가능했다. 그래서 나온것이 high-level language 인 C, fortran.
그런데 컴퓨터 사향이 좋아짐으로서 컴퓨터를 완전히 활용하기 위한 프로그램을 절차지향적으로 코딩을 하다보면 수만라인, 그리고 엄청난 인력이 필요했다. 이것이 두번째 crisis이다. 그뒤 해결책으로 나온것이 OOP 를 채택하고 있는 C++ 이나 JAVA와 design pattern 기법.
이후 uni 프로세서의 발달은 다른 부과적인 하드웨어나 파워등의 문제로 인해 uni processor 가 아닌 multi processor 체제로 가기 시작한다. 이것으로 인하여 기존의 방식의 프로그램밍으로는  multi processor를 온전히 활용하지 못한다. 이것이 세번째 crisis. 이것의 해결책이 병렬 프로그래밍.


병렬 프로그래밍 수업자료를 보는데.. 난 아직 두번째 crisis의 해결책 조차 모른다는 생각이 문뜩 든다. 해서 급하게 C++을 보려 한다. 단기간에 끝내는게 목표다. 물론 코딩은 체득이 중요하다는걸 안다. 하지만 우선은 책을 한번 다 보는게 목표다. 이번 포스팅에서 기억할 만한 것을 예전의 C 포스팅에서처럼 간략하게 적는다. 참고 자료는 윤성우의 열혈강의 C++이다.
----------------------------------------------------------------------------
chapter 1 : C 기반의 C++
c++는 확장자가 cpp 이며 컴파일러가 g++이다.
기본적으로 을 include 한다. #include
printf 대신 std::cout<< somthing<
scanf 대신 std::cin>>somthing;
c와 달리 함수 오버로딩이 가능하다. (return형만 다르면 오버로딩 불가)
함수에서 매개변수의 default 지정이 가능하다. 함수 선언이 먼저 될시 선언부에 default 지정을 해야 한다.
C에서는 매크로 #define을 써서 매크로 함수를 정의 하엿다. 이는 전처리 과정에서 in-line화가 되는데 C++에서는 그냥 함수 앞에 inline라고만 붙여주면 같은 효과가 생긴다.(이는 컴파일러에 의해 처리됨)
서로 다른 사람(곳)에서 만든 함수들의 이름이 겹치는 것을 방지 하기 위해 namespace 를 도입. 그래서 namespace something{void function(void){}}으로 함수를 감싸면 function이란 함수는 something이라는 이름 공간에 선언되어 잇는것이고 이를 사용하기 위해선 "::" 범위 지정 연산자 (scope resolution operator)를 사용한다.(something::function 이렇게)
using 이라는 키워드를 사용하면 함수를 사용할때 이름 공간을 명시할필요가 없다. (using something::function  또는 using namespace something 이라고 하면 그냥 function이라고 사용하면 된다.)
::를 이용하여 전역변수에 접근 할수도 있다.


chapter 2 : C 기반의 C++ 2
true와 false가 새로운 데이터 타입인 bool로 추가 되었다. int형으로 변환시 true는 1, false는 0으로 변환된다.
레퍼런스는 이름이 존재하는 메모리 공간에 하나의 이름을 더 부여하는 것으로 int &ref=val과 같이 레퍼런스(여기서 ref)앞에 &를 붙인다. 레퍼런스는 변수랑 동일하며 다만 만들어지는 차이만 있을 뿐이다. 레퍼런스 변수는 선언과 동시에 초기화가 되어야 한다.
이 레퍼런스 변수를 이용하면 swap 함수(변수 두개의 값을 서로 바꿔주는 역할)를 구현할때 함수의 매개 변수가 포인터가 아니라 레퍼런스 변수로 하면 포인터의 개념 없이 구현이 가능하다 (이는 call-by-reference인지 value인지를 구분하기 힘들기 때문에 위험할수 있다).
또한 call-by-value에서 value대신에 reference변수를 매개변수로 하면 넘기는 변수의 값이 copy되지 않아서 메모리를 아낄수 있다. (여기서도 문제가 있다. &p라는 매개변수로 받으면 함수에서 잘못된 연산으로 원본의 값이 변할수 잇기 때문에 최정적으로 매개변수를 const int &p 식으로 상수화 시켜 버린다. 그렇게 되면 매개변수 p를 통한 변수의 값이 변하는 일이 없다.)
C의 malloc과 free대신 new와 delelete를 사용한다.
int* p = (int*)malloc(sizeof(int)*size) 대신 int p = new int[size], free(p) 대신 delete []p
new를 통한 동적 메모리 할당이 실패할경우 NULL즉 0, 이 리턴된다.

Monday, June 28, 2010

makefile 무엇일까

리눅스에서 수동으로 소스코드들을 컴파일 할때 Make를 많이 쓴다. 그럼 Make명령어를 위한 Makefile은 과연 무엇일까? 다음 아래 링크에 친절하게 설명되어 있다.

Monday, June 21, 2010

C 언어

다음은 초 단기로 c 언어를 공부하고 정말 기억해야 하는 것만 추스린 것이다. 이는 사실 velvet를 분해해보고 싶은 단순한 호기심에 의한 것이다(velvet이 c 로 되어 있다). 다음은 "C 프로그래밍 윤상우 저 (주)프리렉"을 참고로 한다.

함수내 변수 사용시 반드시 변수 선언이 우선되어야 한다.

음수를 표현할때 2의 보수 체계를 기억해야 한다.


정수형과 실수형은 메모리의 비트를 숫자화하는 방식이 다르다.

정수형은 int를 실수형은 double를 사용하길 권장한다.

const 를 이용한 심볼릭 상수 선언시 초기화를 동시에 해주어야 한다. 이는 상수라는 속성상 변화할수 없기때문에 선언시 값을 할당해주어야 하는것이다.

함수는 호출되기 전에 정의되거나 선언되어야 한다.

문자열 변수를 서식문자 %d로 받으면 주소가 %s로 받으면 문자열이 된다. 사실 포인터가 가리키는 value를 지칭하기 위해서는 *을 사용하는데 그렇기 때문에 char * str = "good" 이라고 정의했을때 "good"을 보기위해서는 마치 *str을 불러야 할거 같지만 정작 서식문자만 %s로 해준다면 str를 사용하는 것이 맞다.

배열의 이름은 포인터이지만 변할수 없는 상수 포인터이다.

포인터는 데이터 타입에 상관없이 4바이트를 갖는다.

int a = 3, int *pa = &a 라고 하고 함수 func()이 있다고 할때 func(&a)의 경우 call-by-reference가 되지만 func(pa)는 call-by-value
가 되며 두 경우 모두 func의 입장에서 받는 변수는 int * temp (포인터) 의 꼴이 되어야 한다.

다차원 배열, 예를 들어 이차원 배열, int arr[2][4] 가 있을 때 이를 가르키는 포인터는 int (*pArr)[4] 와 같이 포인터가 가르키는 변수의 데이터 타입 뿐만 아니라 포인터 연산시 증감 되는 바이트의 갯수도 명시해주어야 한다.

배열 arr이 있을때, arr[i] == *(arr+i) 이나 이것이 주소인지 값인지는 상황에 따라 달라진다. arr 이 1차 배열일 경우에는 값이지만 arr이 다차배열일 경우에는 arr[i] 역시 주소를 값으로 갖으므로 이는 주소를 뜻한다.

함수를 구동하기 위해서는 함수를 RAM에 올리게 되고 함수 이름은 함수의 위치를 나타내는 포인터가 된다. 포인터라함은 변수 이므로 이를 가르키는 함수 포인터를 만들 수 있으면 그 타입 선언은다음과 같다. 예를 들어 void func(char * str) 이라는 함수가 있을때 이를 가르키는 함수 포인터 정의는 void (*fPtr) (char*) = func 와 같다.

문자열 입력함수로 gets와 fgets 가 있지만 gets는 할당된 배열의 크기보다 큰 길이의 문자열이 입력 되었을때 overflow를 일으키므로 fgets 을 사용하는 것이 좋다.

필요한 함수를 찾아보고 싶은땐 C reference를 참고하자.

구조체란 하나 이상의 변수를 그룹 지어서 '사용자 정의 자료형'을 정의하는것이다. 구조체 멤버에 문자열 배열이 존재할 시에 그 변수에 문자열을 초기화 할시에는 strcpy함수를 이용해야 한다. 예를 들어 구조체 변수 person이 char name[10]을 멤버로 갖고 있을때 person.name = "김세환" 식으로 초기화를 하면 에러가 생기는데 이는 앞에서 언급한 내용(배열 이름은 상수 포인터이다)에 위배되는 문법이기 때문이다.

*연산자는 . 연산자에 비해 우선순위가 낮기 때문에 구조체 변수를 가르키는 포인터를 이용하기 위하선 (*pMan).name 식으로 괄호를 이용해야 한다. 혹은 pMan -> name 도 가능하다.

typeof 키워드를 이용하여 기본자료형에 사용자가 원하는 이름을 붙일 수도 있으며 이를 이용하여 구조체에 새로운 이름을 정의하여 구조체 변수 선언시 struct 를 넣지 않아도 되도록 할수 있다.

메모리(RAM)은 크게 데이터영역, 힙 영역, 스택 영역으로 나뉘며, 데이터 영역은 전역변수, static 변수가 스택영역은 지역변수, 매개 변수가 힙영역은 프로그래머가 할당한 변수가 자리잡게 된다. 변수의 특징에서 알수 있듯이 데이터 영역은 프로그램 시작과 동시에 변수가 메모리 할당되며 프로그램이 끝나야 메모리에서 지워진다. 반면에 스택영역의 변수는 함수가 호출될때 메모리를 할당되며 해당 함수가 끝나면 메모리에서 지워지며 힙영역의 변수는 프로그래머가 malloc(memory allocation의 약자) 를 통해 메모리를 할당하고 free 함수를 통해 메모리에서 지우게 된다.

스택 영역과 데이터 영역에 할당될 메모리의 크기는 컴파일되는 동안에 결정되어야 한다. 이것이 배열의 크기를 정할때 반드시 상수를 이용해야 하는 이유이다.

malloc에 의해 힙영역에 메모리를 잡을때 메모리가 부족하면 NULL을 리턴한다. 즉, 항상 malloc으로 힙 영역에 메모리를 지정하면 메모리가 여유가 있지 않을수 있음을 확인해야 한다. 또한 힙 영역에 저장된 변수는 반드시 포인터를 통해서만 접근 가능하다.

컴파일을 더 세분화시키면 전처리(preprocess)와 컴파일로 나뉜다. #로 시작하는 문장은 전처리기 지시자라고 하며 #define은 단순 치환 작업을 요청할 때 사용되는 지시자이다. 예를 들어 #define PI 3.1415라고 하면 소스에서 PI라고 되어있는 것을 3.1415로 전처리 과정에서 치환하라는 뜻이며 PI를 매크로 3.1415를 대체 리스트라고 하며 #define 지시자를 통해 매크로 함수도 선언 할수 있다.

#if, #elif, #else, #endif 는 전처리기에게 조건을 제시한다. 곧 컴파일러에 의해 컴파일되기 전에 전처리기가 위의 조건문을 보고 코드에 소스를 넣는다.
#ifndef, #endif는 헤어파일이 중복 포함되어 있음을 방지 하고자 하는것으로 #ifndef (if not defined) 는 #define이 전처리 지시자가 따라야 한다. 곧 #define에 의해 특정 변수가 정의되어 있는가로 #ifndef로 확인하고 정의되어 있지 않으면 #ifndef, #endif  안에 구문을 넣게된다. #if defined() || defined() 등에 여러개를 확인하기 위해서는 if defined를 사용해야 한다.