[ 동적할당 ]

- 메모리 영역의 종류

  >> 스택 : 지역 변수

  >> 데이터 : 전역, 정적, 외부(데이터) 변수

  >> ROM : 코드

  >> 힙 : 동적할당

 

- 특징

  >> 런타임 중에 대응 가능

  >> 사용자가 직접 관리해야함 ( 직접 해제 필요)

 

// 동적할당 설명
#include <stdio.h>

int main(){
// 메모리 영역
// 1. 스택 : 지역 변수
// 2. 데이터 : 전역, 정적, 외부(데이터) 변수
// 3. ROM : 코드
// 4. 힙 : 동적 할당

	int* pInt = (int*)malloc(100);
    	// malloc(N) : N개의 Byte에 대하 주소를 받음
        // 힙 영역에 N Byte 입력
        // malloc 는 Byte에 대한 주소값만 제시함
        // 이 주소값을 어떤 자료형으로 해석할 지는 미지수
        // (int*)로 하여서 주어진 주소값을 int값으로 볼 것이라고 제시
        
    free(pInt)
    	// Byte 할당 해제
	return 0;
}

 

[ 가변 배열 ]

일반적인 배열

  >> 메모리의 크기 고정

 

가변 배열

  >> 메모리의 크기가 고정되어있음

  >> 동적으로 메모리 크기를 변경시켜서 크기가 변할 수 있는 배열로 존재할 수 있게 해줌

 

// main.cpp
#include <stdio.h>
	// printf, scanf_s 등등을 사용하기 위한 라이브러리
#include <cstdlib>
	// free : 메모리 해제를 사용하기 위한

// 가변배열 구조체 선언
typedef struct _tabArr{
    int*    pInt;
    int     iCount;
    int     iMaxCount;
}_tArr;

// 가변배열 관련 함수 선언
void Init(_tArr* _pArr){
    _pArr->pInt = (int*)malloc(sizeof(int)*2);
    _pArr->iCount = 0;
    _pArr->iMaxCount = 2;
}	
	// 초기화 함수

void Release(_tArr* _pArr){
    free(_pArr->pInt);
    _pArr->iCount = 0;
    _pArr->iMaxCount = 0;
}	
	// 해제 함수

void Resize(_tArr* _pArr){
    _pArr->iMaxCount *=2;
    int* _pNew = (int*)malloc(_pArr->iMaxCount*sizeof(int));
    for(int I=0; I < _pArr->iCount ; ++I){
        _pNew[I] = _pArr->pInt[I];
    }
    free(_pArr->pInt);
    _pArr->pInt = _pNew;
    printf("Size UP : %d\n",_pArr->iMaxCount);
}
	// 크기 변경 함수

void PushBack(_tArr* _pArr, int Data){
    if (_pArr->iCount >= _pArr->iMaxCount){
        Resize(_pArr);
    }
    _pArr->pInt[_pArr->iCount++] = Data;
}
	// 데이터 입력 함수

int main(){
    _tArr S={};

    Init(&S);
    	// _tArr*의 타입으로 들어가야하기에, &(Reference)를 사용
    for (int i =0; i<10 ; i++){
        printf("%d\n",i);
        PushBack(&S,i);
        	// _tArr*의 타입으로 들어가야하기에, &(Reference)를 사용
    }
    for (int i=0; i<S.iCount; i++){
        printf("%d\n",S.pInt[i]);
        	// _tArr->pInt로 접근
    }
    Release(&S);
    	// _tArr*의 타입으로 들어가야하기에, &(Reference)를 사용
    return 0;
}

'C++ > 기본 문법' 카테고리의 다른 글

void 포인터  (0) 2022.06.30
포인터 && const  (0) 2022.06.30
배열 && 구조체  (0) 2022.06.30

[ void ] : 아무런 타입 없음

 

[ void 포인터 ] : 원본의 자료형이 정해지지 않은 포인터

- 특징

   >> 원본의 자료형이 정해지지 않음

   >> 어떠한 타입의 변수 주소를 다 저장할 수 있음

   >> 어떠한 타입으로 해석할 지가 정해져 있지 않기에, 역참조 불가능

   >> 어떠한 타입으로 주소 연산을 할 지가 정해져 있지 않기에, 주소 연산 불가능

 

#include <stdio.h>

int main(){
	void* pVoid = nullptr;
    
    { 
    	int a = 0;
        float b = 0.0f;
        double c = 0.;
        long long d = 0;
        
        pVoid = &a;
        pVoid = &b;
        pVoid = &c;
        pVoid = &d;
        	// 가능
            // 모든 타입의 변수 주소를 저장할 수 있음
        
        *pVoid; 
        	// 불가능 - error
            // 어떤 타입으로 해석할 지가 안 정해져 있음
            // 역참조 불가능
        
        pVoid + 1;
        	// 불가능 - error
            // 어떤 타입으로 변수 주소를 연산할 지가 안 정해져 있음
            // 주소 연산 불가능
    }
	return 0;
}

 

'C++ > 기본 문법' 카테고리의 다른 글

동적할당 && 가변배열  (0) 2022.07.01
포인터 && const  (0) 2022.06.30
배열 && 구조체  (0) 2022.06.30

[ 포인터 ] : 포인터 변수

근본 자체는 주소를 저장하는 것

주소를 저장하는 변수의 크기는 동일

- 32 bit -> 4 btye : 4 GB

  >> 이래서 32 bit를 사용하는 컴퓨터에선, 4 GB 이상의 RAM을 장착하여도 성능 개선이 없음

- 64 bit -> 8 byte : 4 GB * 4 GB 

  >> 근래 대부분의 컴퓨터의 경우, 64 bit 지원

  >> 포인터 변수의 크기는 8 byte

 

// 포인터 설명
#include <iostream>

using namespace std;

int main(){
	// 자료형* 변수명
    int* pInt = nullptr;
    	// nullptr : 0, 아무것도 가리키지 않는 상태
	
    // 포인터 변수의 메모리
    int iSize = sizeof(pInt);
    	// 포인터 변수의 크기는 8 Byte
        // iSize = 8
    
    // 포인터 변수에서 +1은 자기한테의 저장된 전체의 주소를 +1만큼 이동한 것
    int i=0;
    pInt = &i;
    pInt += 1;
    	// pInt는 Int형 포인터
        // 즉, 4 Byte 만큼의 주소를 가리킨다.
        // pInt += 1 일 경우, 4 Byte 만큼 이동한 다음의 주소를 가르킨다
        
// 포인터와 배열
    // 배열
    // 1. 열 묶음의 메모리로, 주소가 연속적
    // 2. 배열의 이름은 배열의 시작 주소, 즉 포인터
    int iArr[10] = {};
    iArr;
    	// 배열의 시작 주소, iArr[0]의 메모리 주소
    *(iArr +1);
    	// iArr의 포인터에서 +1
        // Int 메모리의 크기만큼 이동
        // 즉, 다음 주소인 iArr[1]을 가리킨다
        
    int* pInt = iArr;
    cout << pInt << endl;
    	// pInt는 iArr의 시작주소, iArr[0]을 호출
    cout << pInt[0] << endl;
    	// pInt[0]은 iArr[0]의 주소를 호출
    cout << *(pInt + 1) << endl;
    	// *(pInt+1)은 iArr의 시작 주소, iArr[0]에서 int값만큼 이전한
        // iArr[1]을 호출
    cout << pInt[1] << endl;
    	// 위와 동일,
        // 즉, 다음은 동일한 형태
        // *(pInt + 1 ) : pInt[1]

	return 0;
}

 

[ const ] : r-value ( 상수 )

주어진 값을 상수화로 시켜준다.

그리고, 이 값은 CPU에서 상수로 인식하기에,

저장된 공간에서 값이 변하더라도, 기존에 인식된 상수값들 CPU 연산 과정에서 사용

- r-value ( 상수 )

- l-value ( 변수 )

 

// const
#include <stdio.h>

int main(){
	// const
    // 값을 상수화
    const int cint = 100;
    	// 상수화
        // r-value : 상수
        // l-value : 변수
        
    cint = 300;
    	// error
        // 상수로 되었기에 변경될 수 없다.
        
	return 0;
}

 

[ const 와 포인터 ]

#include <stdio.h>

int main(){
	// 상수화
    const int cint = 100;
    	// cint의 상수화
        
// 포인터를 활용한 cint 변경
    pInt = (int*)&cint; 
    	// 상수를 int로 바꾸기 위한 명시적 형변환 후, 주소 값 가져오기
    *pInt = 300;
    printf("%d",cint);
    	// cint가 저장된 메모리는 300으로 바뀜
        // CPU 상에선, 사전에 제시된 cint의 상수값 100을 사용
        // const이기에, 직접 cint로 접근하여 데이터 사용 X
        // 사전에 제시된 cint 값을 사용
        
        // 포인터로 물리적 메모리 공간에 저장된 값을 변경하여도
        // CPU에선, const로 처음에 제시된 값만 연산 중에 사용
        
// 포인터를 활용하여 변경할수 있는 것
	int a = 0;
    int* pInt = &a;
    
    *pInt = 1;
    	// 1. 포인터가 가리키는 대상을 변경 가능
    pInt = nullptr;
    	// 2. 포인터가 가리키는 주소를 변경 가능
        
// const 포인터
	const int* pConstint = &a; 
    	// 포인터가 가리키는 대상이 const
        // 같은 형식
        // int const* pConstint = &a;
    *pConstint = 100;
    	// error
    pConstint = nullptr;
    	// 가능
        
// 포인터 const
	int* const pIntConst = &a;
    	// 포인터가 가리키는 주소를 const
    *pIntConst = 100;
    	// 가능
    pIntConst = nullptr;
    	// error
        
// 간단한 문제
	int a = 0;
    const int* pInt = &a;
    
    a = 100;
    	// 가능
        // const가 제한하는 것은 pInt에서 근본 값을 변경하지 못하게 하는것
        // 근본 값 자체가 변하지 못하게 제한을 둘 수 있는 것은 아님
        
// const & 포인터가 같이 사용 될 때
	void Output(const int* pInt)
    	// 함수에서 변수를 입력 받을 때
        // 변수의 주소를 가져오면 메모리 측면에서 이점 有
        // 하지만, 포인터의 형식으로 가져오게 되면 변수 수정될 가능성 有
        // const를 사용하여 변경이 되지 못하도록 함
        
        // 억지로 메모리값에 접근하여 바꿀 수는 있지만,
        // 작성자의 의도를 const로 보여줄 수 있음


	return 0;
}

'C++ > 기본 문법' 카테고리의 다른 글

동적할당 && 가변배열  (0) 2022.07.01
void 포인터  (0) 2022.06.30
배열 && 구조체  (0) 2022.06.30

배열

주어진 메모리연속적으로 존재하는 자료형

구조체

주어진 메모리를 어떤 방식으로 읽어서 표현할 것인지, 방법을 사용자 정의에 맞춘 자료형

 

[ 코드 ] C 형식 - 단순 struct

잘못된 표현 - 명시적 표현의 생략

// C 형식 - 잘못된 표현 : 명시적 표현이 생략됨

struct NewStruct{
	int A;
    float B;
};

int main(){
	NewStruct New;
	return 0;
}

옳은 표현 - 명시적 표현 표기

// C 형식 - 옳은 표현 : 명시적 표현을 해줘야함

struct NewStruct{
	int A;
    float B;
};

int main(){
	struct NewStruct New;
	return 0;
}

 

[ 코드 ] C 형식 - typedef struct

// C 형식 - 옳은 표현 : 타입 선언 typedef

typedef struct NewStruct{
	int A;
    float B;
}NewStruct;

int main(){
	NewStruct New;
	return 0;
}

 

[ 코드 ] C++ 형식

C++ 에선 struct에 대한 명시적 표현이 생략 가능

// C++ 형식 - 잘못된 표현 : 명시적 표현이 생략됨

struct NewStruct{
	int A;
    float B;
};

int main(){
	NewStruct New;
	return 0;
}

 

하지만, C/C++ 둘 다 사용이 가능하기 위해서, typedef 형식으로 구조체를 선언하는 것이 좋다

// C++ 형식 - 옳은 표현 : 타입 선언 typedef

typedef struct NewStruct{
	int A;
    float B;
}NewStruct;

int main(){
	NewStruct New;
	return 0;
}

 

[ 결론 ] - 구조체는 typedef 형식으로 선언해주자

'C++ > 기본 문법' 카테고리의 다른 글

동적할당 && 가변배열  (0) 2022.07.01
void 포인터  (0) 2022.06.30
포인터 && const  (0) 2022.06.30

+ Recent posts