Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Archives
Today
Total
관리 메뉴

Life Engineering

C/C++ 개념 정리 본문

공부/Computer Science

C/C++ 개념 정리

흑개 2022. 7. 11. 17:29

C언어 컴파일 과정

https://gracefulprograming.tistory.com/16

  1. 전처리 과정
    • 헤더 파일 삽입 - #include 만나면 전처리기가 헤더파일 찾아서 내용 삽입
    • 매크로 치환 및 적용 - #define된 부분 심볼 테이블에 저장, 문자열 만나면 #define된 내용으로 치환
  2. 컴파일 과정
    • 전반부: 소스코드 오류 분석, 트리 형태로 표현
    • 중단부: SSA 형태로 변환 → 최적화 → 고급 언어, 어셈블리 언어의 중간형태로 변환
    • 후반부: 최적화 → 어셈블리어로 구성된 .s 파일이 만들어짐
  3. 어셈블 과정
    • 어셈블러에 의해 어셈블리 코드 → 기계어로 변환
    • 목적코드(helloworld.o) 파일은 명령어(Instruction)와 데이터(Data) 가 들어있는 ELF 구조를 가짐
  4. 링킹 과정
    • 오브젝트 파일들, 표준 C 라이브러리, 사용자 라이으버리를 링크하여 실행 가능한 실행 파일을 만듦

구조체 메모리 크기

typedef struct student {
    char a;
    int b;
}S;

void main() {
    printf("메모리 크기 = %d/n", sizeof(S)); // 8
}

typedef struct student {
    char a;
    char b;
    int c;
}S;     // 8

typedef struct student {
    char a;
    int c;
    char b;
}S;    //12

typedef struct student {
    char a;
    int c;
    double b;
}S;       //16
  • 구조체의 각 멤버를 저장하기 위해서는 기본 4바이트 단위로 구성
  • 구조체 각 멤버 중 가장 큰 멤버의 크기에 영향을 받음

포인터

int* p; : int형 포인터로 p라는 이름의 변수를 선언

p = # : p의 값에 num 변수의 주소값 대입

printf("%d", *p); : p에 *를 붙이면 p에 가리키는 주소에 있는 값을 나타냄

printf("%d", p); : p가 가리키고 있는 주소를 나타냄

  • 이중 포인터

int main()
{
    int *numPtr1;     // 단일 포인터 선언
    int **numPtr2;    // 이중 포인터 선언
    int num1 = 10;

    numPtr1 = &num1;    // num1의 메모리 주소 저장 

    numPtr2 = &numPtr1; // numPtr1의 메모리 주소 저장

    printf("%d\n", **numPtr2);    // 20: 포인터를 두 번 역참조하여 num1의 메모리 주소에 접근

    return 0;
}

동적 할당

  • 동적 할당 ⇒ 힙 영역에 할당
  • 메모리 할당 함수: malloc
    • void* malloc(size_t size) : 메모리 할당은 size_t 크기만큼 할당
  • 메모리 할당 및 초기화 : calloc
    • void* calloc(size_t nelem, sizeo_t elsize) : 배열요소 갯수, 각 배열요소 사이즈, 할당된 메모리를 0으로 초기화
  • 메모리 추가 할당: realloc
    • void* realloc(void *ptr, size_t size) : 이미 할당받은 메모리에 추가로 메모리 할당(이전 메모리 주소는 없어짐)
  • 메모리 해제: free ( void free(void* ptr))

얕은 복사 vs 깊은 복사

  • 얕은 복사
    • 객체일 경우, 인스턴스가 메모리에 새로 생성되지 않는다
    • 주소값을 복사하여 같은 메모리를 가리킴
  • 깊은 복사
    • 데이터 자체를 복사, 복사된 두 객체는 완전히 독립적인 메모리를 차지함
    • 인스턴스가 독립적이다

https://marmelo12.tistory.com/272?category=1010557

⇒ 주로 깊은 복사가 사용되며 복사 생성자를 선언하여 새로운 메모리를 동적 할당 후 값 복사가 진행되어야 한다(깊은 복사)

가상 함수

  • 함수가 가상함수로 선언되면 해당 함수 호출 시, 포인터의 자료형을 기반으로 함수가 호출되지 않고 포인터 변수가 실제로 가리키는 객체를 참조하여 호출의 대상을 결정함
  • 자식 클래스에서 오버라이딩할 것으로 기대되는 멤버 함수 앞에 virtual 붙이면 OK → 실행시간에 함수의 다형성을 구현할 때 사용
class parent {
public :
    virtual void v_print() {
        cout << "parent" << "\n";
    }
    void print() {
        cout << "parent" << "\n";
    }
};

class child : public parent {
public :
    void v_print() {
        cout << "child" << "\n";
    }
    void print() {
        cout << "child" << "\n";
    }
};

int main() {
    parent* p;
    child c;
    p = &c;

    p->v_print();
    p->print();

    return 0;
}
// 출력 결과
// child
// parent
  • 일반 함수 ⇒ 컴파일 시간에 결정이 남
  • 가상 함수 = 실행 시간(런타임)에 결정이 남 (동적 바인딩)
    • public 섹션에 선언
    • static X, friend X
    • 런타임에서 다형성을 얻기 위해 기본 클래스의 포인터 또는 참조를 통해 접근해야 함
  • 가상 소멸자
    • 객체의 소멸 ⇒ A *a=new B(); 일 경우 A 클래스의 소멸자만 호출되어 메모리 누수 발생
    • 객체 소멸에서는 포인터 변수의 자료형에 상관없이 모든 소멸자가 호출되어야 함
    • 해결방법 → “가상 소멸자”: 상속 구조 맨 위에 있는 소멸자만 virtual 선언하면, 상속 구조의 맨 아래에 있는 유도 클래스의 소멸자가 대신 호출 ⇒ 기초 클래스의 소멸자가 순서대로 호출
  • 순수 가상 함수(Cracking Code Interview p.236 참조)
    • 상위 클래스의 A 함수를 pure virtual function으로 선언하여 구현은 하위 클래스에게 맡김 ⇒ 상위 클래스는 스스로 객체를 만들 수 없는 추상 클래스가 됨
    • virtual bool addCours(String s)=0
    • 하위 클래스가 함수를 구현하면, Person *p=new Student(); p→addCours(”Math”) 할 때 하위 클래스의 함수가 호출
  • 가상 함수 동작 원리:
    • 가상 함수는 vtable(virtual table) 에 의존⇒ 함수가 virtual로 선언될 경우 가상 함수 주소를 보관하는 vtable이 만들어지고, 해당 클래스의 vtable을 가리키는 vptr 이 추가된다
    • 하위 클래스가 상위 클래스 가상함수를 오버라이드 하지 않으면 하위 클래스의 vtable은 상위 클래스의 가상 함수 주소를 보관한다
    • 이 vtable을 이용하여 가상 함수 호출될 때 실제 어떤 함수가 호출되어야하는지 결정된다
    • 가상 함수는 동적 바인딩 시에 실행되며, 하위 클래스 객체에 대한 포인터를 상위 클래스 객체에 대한 포인터에 할당하면 vptr 변수는 하위 클래스의 vtable을 가리키고, 이렇게 배정되면 하위 클래스의 가상 함수가 호출됨

STL Map vs 해시 테이블

  • 해시 테이블(=unordered map)
    • 삽입, 삭제: O(1)
    • 연결리스트 배열로 구현 ⇒ 키값을 배열의 인덱스 값으로 대응시킨 다음에 해당 위치에 있는 연결리스트에 값을 삽입
    • 연결리스트에 보관되는 값은 동일한 키를 가지지 X, 즉 노드에 키, 값 모두 저장해야
  • STL map
    • key를 기준으로 만든 이진 트리에 키/값 쌍을 보관
    • 충돌을 처리할 필요 X, 트리의 균형이 유지되므로 O(logN)

정적 바인딩 vs 동적 바인딩

  • 정적 바인딩: 컴파일 타임에 타입 결정
  • 동적 바인딩: 런타임에 타입 결정 → 참조된 인스턴스의 실제 클래스에 따라 재정의 된 함수를 호출할 때 사용
  • 템플릿: 하나의 클래스를 서로 다른 데이터 타입에 재사용할 수 있도록 하는 것

심화

  • Volatile:
    • 변수를 선언할 때 앞에 volatile 을 붙이면 컴파일러는 해당 변수를 최적화에서 제외하여 항상 메모리에 접근하도록 함
    • 프로그램의 수행 흐름에 상관없이 외부 요인이 변수 값을 변경할 때 사용
  • 스마트 포인터:
    • 포인터처럼 사용하는 클래스 템플릿(=포인터 역할을 하는 객체)
  • https://min-zero.tistory.com/entry/C-STL-1-3-템플릿-스마트-포인터smart-pointer
  • malloc 과 new 차이?:

 

'공부 > Computer Science' 카테고리의 다른 글

OS 개념 정리 - 1  (0) 2022.07.11