참조
지속적으로 추가될 예정
.h 확장자를 가진 헤더파일은 다른 파일에 대한 선언을 가지고 있다.
<> 는 표준 라이브러리의 헤더파일 즉 컴파일러와 함께 제공되는 헤더파일을 include 할 때 사용한다.
" "는 inlcude 를 실행하는 소스파일이 있는 위치에서 해당 헤더파일을 찾는다.
헤더파일 내에 같은 이름의 함수를 가지고 있는 두 헤더파일을 같이 include 하는 상황에서는 두 헤더파일 중 어떤 함수를 실행해야하는지 판단할 수 없기에 오류 발생
위 상황에 대한 일종의 대비책으로 헤더가드를 사용한다.
//헤더가드의 시작
#indef 함수이름
// (함수이름)이 정의되지 않았다면
#define 함수이름(); //함수에 대한 원형(Prototype)
#endif
//헤더가드 끝
위 헤더 파일이 add.h 이고, 함수 이름이 add라면
main.cpp 에서 #include "add.h" 를 컴파일하면 add.h의 add() 함수에 대한 함수 원형을 전방 선언(Forward Declaration) 한다.
(일반적으로 헤더파일의 이름은 구현파일과 동일하게 설정해 헷갈리지 않게 한다.)
전방선언
실행할 함수에 대해 실제로 구현하기 이전에 원형(Prototype)의 정보를 컴파일러에게 알려 함수의 존재를 인식시키는 것
#pragma once와 함께 사용해 효율과 헤더가드의 기능성을 높이자.
main.cpp를 컴파일 하기 이전에 전처리기(preprocessor)가 헤더파일을 읽고 해당 헤더파일에 선언된 함수들을 확인하고 전방선언 하는 것
namespace(이름공간) 에 있는 std 클래스를 사용한다는 뜻
C++은 클래스 개념이 도입되어 있기 때문에
cout,endl,cin등 자주 쓰이는 함수가 정의되어있는 std 클래스를 사용한다고 명시하는 것이다.
명시하지 않으면 출력할때마다std::cout이라고 해야하기에.. 번거로움을 없애기 위해서 위와같이 명시하는 것
표준안을 논의하는 과정에서 표준 라이브러리 헤더파일에는 .h 확장자를 붙이지 않고 name space 에 기존에 사용하던 표준 라이브러리의 요소들을 포함시키기로 논의 되었다.
namespace(이름공간)은 그저 std::를 생략하는 역할을 할 뿐이며, 이름들을 모아놓은 공간 그 이상 그 이하도 아니다.
한 공간안에 같은 이름이 있으면 안되는데,
속해있는 이름공간이 다르면 이름이 같아도 사용할 수 있는 효율을 가져다줄 뿐이다.
cout, endl 을 사용하기 위해서 함수가 구현되어 있는 코드를 현재 실행된 cpp 파일에 불러와야하는데, 이 역할을 해주는게 #include <iostream> 라는 전처리 자주 쓰이는 헤더들을 모아 나중에 컴파일할 일이 없도록 MS에서 미리컴파일해둔 헤더파일
메모리의 데이터값을 원하는 크기만큼 특정 값으로 세팅할 때 사용한다.
Func1 temp = NULL;
temp = new Func1;
Func2 temp2 = NULL;
temp2= new Func1;
memset(temp, 'a', sizeof(Func1));
memset(temp2, 0, 5);
함수 원형
:void* memset(void* ptr, int value, size_t num);
| 자료형 | 크기 | 범위 |
|---|---|---|
| char (signed char) | 1byte | -128~127 |
| short (short int) | 2byte | -32,768~32,767 |
| int | 4byte | -2,147,483,648~2,147,483,647 |
| long (long int), (signed long), (signed long int) | 4byte | -2,147,483,648~2,147,483,647 |
| long long (long long ...) | 8byte | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 |
-> int 와 long 은 21억..
long long 은 걍 미친넘
| 자료형 | 크기 | 범위 |
|---|---|---|
| float | 4byte | ±3.4×10^(-37) ~ ±3.4×10^(38) |
| double | 8byte | ±1.7×10^(-307) ~ ±1.7×10^(308) |
| long double | 8byte | ±1.7×10^(-307) ~ ±1.7×10^(308) 이상 |
예시 구조체
typedef struct student
{
char a;
int b;
}
char 형은 1byte, int 형은 4byte 를 할당받기에, 이상적으로
student 구조체는 5byte 를 할당받아야할 것 같다.
하지만, 구조체가 메모리 공간을 할당받는 원리는
- 각각의 멤버를 위한 메모리 공간은 최소 4byte
- 각 구조체 멤버 중 가장 큰 메모리 크기에 영향을 받는다.
- 멤버의 배열 순서에 따라 메모리 할당 크기가 달라진다.
student 구조체의 멤버
//1.
char a;
char b;
int c;
//2.
char a;
int b;
char c;
1번 상황
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|
| a | b | c |
2번 상황
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|
| a | b | c |
-> 따라서 작은 자료형을 먼저 배열하는 것이 유리하다.
초기 개발 환경 (32bit 아키텍쳐가 상용화되기 이전)의 c89 시절에는 int 는 아키텍쳐의 종류에 따라 크기를 달리해 사용할 수 있는 효율적으로 사용할 수 있는 데이터 타입 이었다. (최소 2byte 사용)
하지만, 32bit 아키텍쳐와 64bit 아키텍쳐의 개발 등으로 서로 다른 int의 크기로 문제 발생
64bit 에서 int는 8byte 인데, 32bit에선 long int 가 4byte 로 모순이 발생
-> 이에 64bit 아키텍쳐에서는 int = 4byte 로 유지
CPU 의 레지스터의 연산을 빠르게 하기 위해 32bit 보다 발전된 64bit 아키텍쳐가 개발되었다.
CPU 에는
1. 연산 중간값 저장
2. 가장 최근에 수행한 명령어
3. 메모리에서 가져온 데이터 저장
4. 메모리의 접근할 주소 저장
등, 매우 작은 용량이지만 고속으로 처리할 수 있는 내장 메모리인 레지스터가 있다. (일반적인 메모리는 외부에 장착하는 RAM 지칭)
레지스터의 용량 자체도 늘어나고, 32bit 아키텍쳐는 최대 메모리(RAM)을 4GB까지밖에 지원하지 않는 단점이 있다.
-> 제한된 메모리 사용으로 성능 저하
32bit 아키텍쳐는 32비트 단위로 데이터를 처리하고
64bit 아키텍쳐는 64비트 단위로 데이터 처리
64비트 아키텍쳐에서는 32비트 아키텍쳐에 맞춰 개발된 프로그램을 사용할 수 있다.
동적으로 메모리를 할당하는 방법
new 는 연산자 로 C++ 에서 도입되었다.
객체의 개념이 도입되면서 그저 메모리만 할당해주던 malloc 과의 가장 큰 차이점은 new 는 생성자를 호출해준다는 것
malloc은 캐스팅 (형변환 (int*)) 이 필요하다는 것이고, 사이즈를 매개변수로 전해줘야한다.
할당된 메모리 공간의 시작주소를 반환하며, 기본적으로 메모리 공간의 요소들을 초기화해주지 않고 쓰레기값을 가지고 있다.
new - delete / malloc - free
부동 소수점이 필요한 이유
부동 소수점으로 나타내는 과정
우선 실수 부분을 2진법으로 변환해야한다.
실수값을 (1)부호, (2)정수, (3)실수 세 부분으로 나눠야한다.
-9.6875 를 2진법으로 나타내보면
부호는 음수, 정수는 9, 실수는 0.6875
정수인 9는 2진법으로 1001 로 해결
정수를 2진법으로 바꿨을때는 2로 나눠준 것과 반대로
실수는 2를 곱해가며 수행한다.
0.6875 * 2 = 1.375 / 1
0.375 * 2 = 0.75 / 0
0.75 * 2 = 1.5 / 1
0.5 * 2 = 1 / 1
-> 0.6875 는 2진법으로 1011
대부분의 실수는 무한히 순환하며 반복되기 때문에, 적당한 갯수의 유효숫자만 취해서 사용한다.
즉 실수 -9.6875 는 2진수로 표현하면 -1001.1011 (2)
이제 이를 저장하기 위해 정규화 과정을 거쳐야 한다.
정규화는 정수부를 1로 맞추고 소수점 위치를 조정하는 것
꼴로 변화시키는 것을 말한다.
! 에는 10의 승수를 넣어준다.
-1001.1011 를 정규화하면 -1.0011011 * 2^3
이제 정규화 과정을 거쳐
부호, 소수부의 유효숫자, 지수부 를 알게 되었고,
소수부의 유효 숫자를 가수부라고 부른다.
이제 부동소수점으로 표현된 자료를 정해진 비트 양식에 맞춰 저장하면 되는데, 일종의 약속으로 규정된 표준들이 존재한다.
부호 1비트, 지수부 8비트, 가수부 23비트 = 32비트
이런 식으로 할당해주어 저장한다.
부호부 : 0이면 양수, 1이면 음수
가수부 : 왼쪽부터 그대로 채우고 나머지는 0으로 채운다.
지수부 : 00000000 을 -127, 00000000 은 -126 ... 1씩 증가
컴퓨터가 음수를 저장하기 위해 사용하는 방법
가장 왼쪽 비트를 부호 비트로 사용하고 나머지 비트로 수를 표현한다.
2의 보수 : NOT x + 1
-> 양수를 음수로, 음수를 양수로 변환해주는 연산
-7 를 2진법으로 표현하는 법
더 큰 비트에서
12의 2진수 : 0000...1100
2의 보수 연산 : 1111...0011
1을 더한다 : 1111...0100
-> -12 는 1111...0100 이 된다.
2의 보수 연산 = NOT 연산 + 1 임으로
NOT 연산 = 2의 보수 연산 - 1 이 됨으로
-> NOT x = -x -1 이 된다.
이제 2진수를 10진법으로 변환할때,
부호비트가 1 이면 음수임을 인지하고
2의 보수 연산을 취해 음수를 양수로 만들어 값을 찾는다.