닷넷 API

 

 

 

C#이 사용할 수 있는 API 집합체인 닷넷에는 굉장히 많은 API가 있다.

 

 

◎ 닷넷 API 탐색기와 Docs

 

마이크로소프트는 닷넷 API 탐색기를 제공하여 웹에서 모든 API 검색을 할 수 있다.

https://docs.microsoft.com/en-us/dotnet/api/

위의 링크를 들어가보면 API를 검색할 수 있다. 

 

닷넷에서 제공하는 대부분의 API는 클래스이다.

그리고 구조체, 열거형이 있고, 이러한 클래스, 구조체, 열거형을 특정 이름으로 묶어 관리하는 네임스페이스가 있다. 

 

* 클래스 (Class)

: Console 클래스, String 클래스 등 거의 대부분이 클래스이다. 

 

* 구조체 (Struct)

: DateTime 구조체, TimeSpan 구조체 형태로 표현하며, 클래스와 거의 동일하게 사용한다.

 

* 열거형 (enumeration)

: Color 열거형 등이 있으며, 특정 목록을 관리할 때 편리하다.

 

* 네임스페이스 (namespace)

: System 네임스페이스처럼 많은 양의 클래스와 구조체, 열거형을 묶어서 관리한다. 

 

 

위의 API 탐색기에서 Math를 검색했을 때 System.Math를 클릭해보면 기능들을 더 자세히 볼 수 있다. 

 

 

 

 

참고 자료;

C# 교과서 (저; 박용준 출판사; 길벗) 

 

 

전처리기 지시문

#define , #error, #if / #elif / #else 및 #endif / #ifdef와 #ifndef

 

 

 

전처리기 지시문은 일반적으로 소스 프로그램을 쉽게 변경하고 여러 실행 환경에서 쉽게 컴파일할 수 있도록 하기 위해 사용된다.

전처리기(=선행처리)란 컴파일 이전의 처리를 의미하고 소스파일은 컴파일러에 의해 컴파일 되기 이전에 이 전처리기 단계를 거친다.

 

 

 

◎ #define 지시문

이 #define 지시문은 세 부분으로 나뉜다.

#define PI 3.1415
지시자 매크로 매크로 몸체

많이 쓰이는 PI로 예시를 들어보면 코드에서 PI라는 이름(매크로)이 나오면 3.1415(매크로 몸체)로 바꾸라는 명령과 같다.

 

#define PI 3.1415

int function_before()
{
	int angle;
	angle = 180 * PI;
}

int funcint_after()
{
	int angle;
	angle = 180 * 3.1415;
}

 

전처리기의 단계를 거치면 코드 상에서 function_before의 내용을 function_after로 바꾼다.

이렇게 상수를 매크로로 정의한 것을 매크로 상수라고 부르고

상수가 아닌 함수로 정의해 놓은 것을 매크로 함수라고 부른다. 

 

 

 

 

◎ #if, #elif, #else 및 #endif 지시문 

#if... #endif는 조건부 코드 삽입을 위한 지시문이다. 

소스파일의 각 #if 지시문은 #endif 지시문과 일치해야 하며 #if와 #endif 사이에는 여러개의 #elif가 나올 수 있지만 #else는 하나만 사용할 수 있다.

 

#define ENABLE 1
#define IMPOSSIBLE 0


int main()
{
#if ENABLE
	cout << "ENABLE" << "\n";
#endif IMPOSSIBLE
	cout << "IMPOSSIBLE" << "\n";
}

 사용법은 이처럼 if문이 나오면 반드시 endif가 나와야 하며 ENABLE이 참(=1)이라면 cout << "ENABLE" << "\n"; 의 코드를 삽입하고 IMPOSSIBLE이 참이라면 cout << "IMPOSSIBLE" << "\n";의 코드를 삽입하라는 뜻이다. 

 

 

 

 

 

 

◎ #ifdef 및 #ifndef 지시문

정의 된 연산자와 함께 사용될 때 #if 지시어와 동일한 효과를 가진다.

 

#ifndef 식별자
#ifndef 식별자

이 지시문은 아래의 지시문과 같은 내용이다.

#if 정의된 식별자
#if! 정의된 식별자

 

#if는 매크로가 참인지 거짓인지에 따라서 동작한다면 #ifdef는 매크로가 '정의되어 있는지 아닌지'를 기준으로 동작한다. 

#ifdef는 매크로가 정의되어 있다면이고

#ifndef는 매크로가 정의되어 있지 않다면이라는 의미이다. 

 

 

 

 

◎ #error 지시문

전처리기가 해당 지시문을 발견하면 컴파일이 중지되고 여기에 포함된 오류 메시지가 전달된다. 

#define _EXIT

#ifdef _EXIT
#error "Error exists."
#endif

 

전처리기가 해당 코드를 만나게 되면 error.c : error: #error "Error exists." 이렇게 출력되며 종료된다. 

 

 

 

 

 

이 외에도 #import, #include, #line, #undef, #using 등이 전처리기 지시문에 포함된다. 

 

 

 

 

 

참고 자료;

 

https://docs.microsoft.com/ko-kr/cpp/preprocessor/preprocessor-directives?view=vs-2019

 

전처리기 지시문

전처리기 지시문Preprocessor directives 이 문서의 내용 --> #define, #ifdef과 같은 전처리기 지시문은 일반적으로 소스 프로그램을 쉽게 변경하고 여러 실행 환경에서 쉽게 컴파일할 수 있도록 하는데 사용됩니다.Preprocessor directives, such as #define and #ifdef, are typically used to make source programs easy to change and easy to

docs.microsoft.com

 

 

 

클래스(Class)와 구조체(Struct)의 차이

구조체에 대한 설명과 클래스와 구조체의 차이

 

 

 

연관있는 데이터를 하나로 묶는다는 기본 개념은 동일하다. 그러면 각각은 무엇이고 둘의 차이는? 

 

◎ 구조체

C++에서 구조체를 선언하는 방법

Struct My_S
{
	char name[MAX_LEN];
	int age;
	int number;
}

 

 

C++에서는 C에서 처럼 typedef를 추가할 필요 없이 사용이 가능하다.

int main()
{
	My_S info = {"hi", 90, 100}
	
	info.name = "hello";
	info.age = 100;
	info.number = 7;
}

 

 

구조체와 클래스를 비교하면서 구조체에는 함수를 추가하지 못한다는 이상한 내용도 가끔 보이는데 아니다.

C++에서는 구조체 안에도 함수 추가 가능하다

Struct My_S
{
	char name[MAX_LEN];
	int age;
	int number;
    
    void SetMyAge(int _age)
	{
		age = _age;
	}
}

 

여기서 내가 My_S 구조체의 변수를 여러 개 선언해서 메모리 공간에 구조체 변수가 할당이 될 때, 

함수는 모든 구조체 변수 내에 각각 별도로 존재하는 것이 아니다.

구조체 안의 함수는 한 번만 선언된 하나의 함수를 공유한다. 

 

 

그리고 물론 이 함수를 외부로 뺄 수도 있다.

Struct My_S
{
	char name[MAX_LEN];
	int age;
	int number;
    
    void SetMyAge(int _age);
}

void My_S::SetMyAge(int _age)
{
	age = _age;
}

 

함수 원형 선언은 구조체 안에 두고 정의를 밖으로 빼서 정의하는 것이다.

딱히 쓸 일이 없어 보이지만 구조체를 보는 순간 정의와 기능을 한 눈에 파악하기 쉽게 작성하는 데 도움이 되기도 한다. 

 

그런데 구조체 안에 함수가 정의 되어있을 때와 바깥에 있을 때 다른 점이 하나 존재한다.

바로 구조체 안에 있을 때는 함수가 inline으로 처리되고 있다는 점이다. 

인라인의 의미를 그대로 유지하고 싶다면 inline 키워드를 사용해서 명시적으로 지시하면 된다. 

 

 

 

 

 

◎ 클래스

구조체는 클래스의 일종이다.

struct를 사용하면 구조체, class를 사용하면 클래스가 되는 것이다.

Class My_S
{
	char name[MAX_LEN];
	int age;
	int number;
}

위의 구조체와 비교했을 때 기본 선언 시 사용하는 키워드만 다를 뿐이다.

 

 

그런데 이 키워드의 차이는 초기화의 방법의 차이를 발생시킨다.

int main()
{
	My_S info = {"hi", 90, 100};
}

struct는 이와같은 방법으로 초기화가 가능했지만 클래스에서는 불가능하다.

그 이유는 클래스 내에 선언된 함수가 아니고 다른 영역에서 초기화를 하려고 했기 때문이다. 

클래스는 기본적으로 별도의 선언이 없다면 클래스 내에 선언된 변수는 클래스 내에 선언된 함수에서만 접근이 가능하다. 

 

여기서 접근에 대한 구분이 존재한다. 그것을 접근제어 지시자라고 하는데

public 모두에게 공개; 아무나 접근 가능
protected 상속관계로 얽혀있는 클래스에서만 접근허용
private 클래스 안에서만 건들일 수 있다.

 

접근제어 지시자를 명시하지 않으면 class는 기본으로 private으로 설정한다. 

그래서 위의 class는 private으로 지정되어 있어 외부에서 접근이 불가능한 것이다. 

 

 

이 접근제어 지시자는 구조체에서도 선언이 가능하다.

단, 클래스에서는 따로 지정하지 않으면 private으로 설정이 되지만 구조체에서는 public으로 설정이 된다. 

 

 

 

 

 

 

 

 

 

참고 자료;

윤성우의 열혈 C++ 프로그래밍 ( 저; 윤성우, 출판사; 오렌지 미디어)

'Study > C++ , C#' 카테고리의 다른 글

[C#] 닷넷 API  (0) 2021.12.22
[C++] 전처리기 지시문  (0) 2020.01.19
[C++] QueryPerformanceCounter / 프로그램 실행 시간 측정  (0) 2020.01.15
[C++] 매크로 함수와 인라인 함수(Inline)  (0) 2020.01.10
[C++] new 와 delete  (0) 2020.01.10

 

 

QueryPerformance을 이용해 프로그램 실행 속도 측정

컴퓨터 메인보드의 고해상도 타이머를 이용해 시간 간격을 측정한다.

 

 

 

 

현재 실행 속도를 측정하고 싶으면 QueryPerformanceCounter(=QPC)를 이용하면 된다.

하지만 QPC는 외부 시간 참조와 독립적이며 동기화되지 않으므로 현재 시간값을 구하고 싶으면 GetSystemTimePreciseAsFildTime을 이용하라고 MSDN에 나와있다. 

 

QueryPerformanceFrequency와 QueryPerformanceCounter를 이용하면 타이머, FPS 측정 등 여러 방면으로 활용할 수 있다. 

 

 

 

 

◎ QueryPerformanceFrequency

성능 카운터의 빈도를 검색한다. 

 

BOOL QueryPerformanceFrequency(
  LARGE_INTEGER *lpFrequency
);

 

lpFrequency = 현재 성능 카운터, 타이머의 주파수를 반환한다.

 

 

 

 

 

◎ QueryPerformanceCounter

시간 간격 측정에 사용할 수 있는 고해상도 타임 스탬프인 성능 카운터의 현재 값을 검색한다. 그러니까 현재 CPU의 틱을 받아오는 것이다. 

 

BOOL QueryPerformanceCounter(
  LARGE_INTEGER *lpPerformanceCount
);

 

lpPerformanceCount = 매개변수로 현재 성능 카운터 값을 계수로 받는 변수에 대한 포인터를 넘겨준다. 

 

반환 값은 함수가 성공하면 0 이외의 값을 리턴하고 실패하면 0을 리턴한다. 

(Windows XP 이상에서는 이 기능이 항상 성공해서 0을 리턴하는 경우는 없다.)

 

 

 

 

 

◎ LARGE_INTEGER

여기서 쓰이는 LARGE_INTEGER는 뭘까?

QueryPerformanceCounter나 QueryPerformanceFrequency같은 함수를 사용하기 위해선 크기가 큰 정수형이 필요하다. 

왜? 더 자세한 시간값을 저장하기 위해서 

그래서 windows.h에 포함돼 있는 LARGE_INTEGER가 그와 같은 것이다. 

부호가 있는 64비트 정수형 데이터를 저장하기 위해 선언된 사용자 정의 자료형.

 

LARGE_INTEGER는 구조체인데 그 안을 들여다 보면

typedef union _LARGE_INTEGER {
  struct {
    DWORD LowPart;	// 32bit 정수형
    LONG  HighPart;	// 32비트 정수형
  } DUMMYSTRUCTNAME;
  struct {
    DWORD LowPart;
    LONG  HighPart;
  } u;
  LONGLONG QuadPart;	// 64비트 정수형
} LARGE_INTEGER;

이렇게 되어 있다. 

컴파일러가 64비트를 지원할 땐 64비트 정수형 변수에, 32비트 지원 시에는 32비트 정수형 변수에 64비트 측정값을 나눠 저장한다. 

 

값은 64비트의 부호있는 정수형인 QuardPart에 저장되는 것이며 

LowPart는 하위 32비트 DWORD형, HighPart는 상위 32비트 LONG 형이다.

 

64비트 중 LowPart(32bit)와 HighPart(32bit)를 둘다 사용함으로써 더 큰값을 사용할 수 있다. 

 

 

 

 

 

◎ 사용법

#include <windows.h>

int main()
{
	LARGE_INTEGER timer, start, end;
	float DeltaTime;
    
	QueryPerformanceFrequency(&timer); // 타이머의 주파수를 얻어온다. 
   
   
	QueryPerformanceCounter(&start);  // 시작 시점의 CPU 클럭 수
    
    
    // 실행할 내용
    
    
	QueryPerformanceCounter(&end);	// 종료 시점의 CU 클럭 수
    
	DeltaTime = (end.QuadPart - start.QuadPart) / (float)timer.QuadPart; 	// 걸린 시간 계산
 
 }

 

 

 

 

 

 

참고 자료;

 

https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancefrequency

 

QueryPerformanceFrequency function - Win32 apps

Retrieves the frequency of the performance counter.

docs.microsoft.com

 


https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter

 

QueryPerformanceCounter function - Win32 apps

Retrieves the current value of the performance counter, which is a high resolution (<1us) time stamp that can be used for time-interval measurements.

docs.microsoft.com

 

 

'Study > C++ , C#' 카테고리의 다른 글

[C++] 전처리기 지시문  (0) 2020.01.19
[C++] 클래스(Class)와 구조체(Struct)의 차이  (0) 2020.01.15
[C++] 매크로 함수와 인라인 함수(Inline)  (0) 2020.01.10
[C++] new 와 delete  (0) 2020.01.10
[C++] 참조자와 함수  (0) 2020.01.08

 

 

매크로 함수와 인라인(Inline) 함수

in은 '내부', line은 '프로그램 코드라인'을 의미한다.

즉 프로그램 코드라인 안으로 들어가 버린 함수라는 뜻이다.

매크로, 일반 함수, 인라인 함수 모두 결과는 동일하지만 결과를 위한 과정이나 성격은 상당히 다르다! 

 

 

 

 

◎ 매크로 함수

#define 선행 처리 지시문에 인수로 함수의 정의를 전달해서 함수처럼 동작하는 매크로를 만드는 것이다. 

물론 일반 함수와는 달리 단순 치환만을 해주므로, 일반 함수와 완전히 똑같은 방식으로 동작하지는 않는다. 

매크로 함수는 일반적인 함수에 비해 실행 속도에서 이점이 존재하지만 가독성도 떨어질 수 있고 디버깅도 어려우며 복잡한 함수를 매크로의 형태로 정의하는 것에 한계가 존재한다. 

빠른 이유?

더보기

일반 함수처럼 호출된 함수를 위한 스택 메모리의 할당을 할 필요도, 실행위치의 이동과 매개변수로의 인자를 전달할 필요도, return 문에 의한 값의 반환을 할 필요도 없기 때문이다. 

 

 

매크로 함수의 예시.

#define Add(x) ((x)+(x))

int main()
{
	int a = Add(3);
	cout << a << endl;
}

이렇게 실행하면 a의 출력값은 6이 나올 것이다. 

 

 

왜 '전처리 과정'을 거칠까?

더보기

#와 ## 연산자는 선행처리기 연산자이다. 

이 선행처리기 연산자는 전처리기-> 어셈블러로 넘어가기 전에 전처리기가 # 뒤에 붙은 코드들을 처리하기 때문이다. 그래서 전처리 과정에서 처리한다. 

 

 

그리고 이 과정은 

#define Add(x) ((x)+(x))

int main()
{
	int a = Add(3);
	cout << ((3)+(3)) << endl;
}

이런 과정을 거치게 되는데 위와 같이 함수의 몸체 부분이 함수 호출 문장을 완전히 대체했을 때 '함수가 인라인화 됐다'고 표현한다. 

 

 

 

 

◎ 인라인 함수

인라인 함수는 매크로 함수의 장점은 유지하고 단점은 제거했다고 볼 수 있다.

 

또한 인라인 함수는 inline 예약어를 빼면 기존 함수와 다를 바 없고 자료형 등이 지정돼 있어 문법적으로도 완벽한 함수이다. 

매크로를 이용한 함수의 인라인화는 전처리기에 의해 처리되지만 키워드 inline을 이용한 함수의 인라인화는 컴파일러에 의해 처리된다. 

 

inlineint Add(int x)
{
	return x+x;
 }

int main()
{
	int a = Add(3);
	cout << a << endl;
}

 

그러나 매크로 함수의 장점을 인라인 함수가 완벽하게 가져오지 못한 부분도 있다.

예를 들면 위의 매크로 함수의 예시에서 x는 자료형에 의존적이지 않다. 

즉 실수를 넣든, 정수를 넣든 데이터 손실이 발생하지 않는다.

3을 넣으면 6이 1.92를 넣으면 3.84가 나오게 된다. 

하지만 인라인 함수는 자료형 기반으로 정의된 함수이기 때문에 int 자료형 기반에서 1.92를 넣게 되면 0.84의 손실이 발생한다. 

 

물론 함수의 오버로딩을 통해 문제 해결이 가능하지만 간단하게 한 번만 정의하면 된다는 매크로 함수의 장점과는 멀어지게 된다.

그런데 C++에는 이에 대한 해결책이 또 하나 존재한다. 바로 템플릿을 이용하는 것이다. 템플릿을 이용하면 여러 번 정의하지 않아도 한 번에 해결된다!

 

 

 

 

 

◎ 추가

Visual Studio를 통해 컴파일할 경우, 인라인으로 선언하지 않았더라도 적합하면 인라인 함수로 확장하는 설정이 있다.

반대로 인라인으로 선언했다고 해도 적합하지 않다면 일반 함수로 컴파일한다. 

 

기본값은 '인라인 함수로 사용하는 데 적합한 함수는 모두 인라인 함수로 만든다'는 의미이다. 

설정값은 보통 '기본값'으로 지정돼 있다.

 

 

 

 

 

 

 

참고 출처;


http://tcpschool.com/c/c_prepro_macroFunc  (코딩의 시작, TCP School ; 매크로 함수)

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com


윤성우의 열혈 C 프로그래밍 ( 저; 윤성우, 출판사 ; 오레지 미디어)


이것이 C++이다 ( 저; 최호성, 출판사 ; 한빛미디어 )

 

 

 

 

 

 

new 와 delete 

new는 C언어의 malloc을, delete는 free를 대신하는 키워드이다. 

 

 

 

 

new와 delete연산자는 내부에서 malloc()과 free()함수를 호출한다. 

 

 

굳이 왜 malloc과 free를 쓰지 않고 new와 delete를 사용하는 것일까?

malloc을 이용하여 동적할당을 할 때 할당할 대상의 정보를 무조건 바이트 크기 단위로 전달해야 한다는 것과 반환형이 void형 포인터이기 때문에 적절한 형 변환을 거쳐야 한다는 불편사항이 존재했었다. 

그러나 new는 메모리 크기를 정하지 않는다. 이것이 바로 new를 사용하는 가장 도드라진 특징이다.

 

또한 new와 malloc의 다른 점을 하나 더 뽑으라면 '생성자 함수 호출'이라는 특징이 존재한다. 

new를 통해 객체를 동적으로 생성하면서 객체의 생성자를 호출하기 때문이다. 

 

new와 delete는 단순히 메모리 관리 이상의 일들을 수행한다. 또한 문법적으로 연산자이며 malloc과 free는 객체지향 프로그래밍을 방해하는 원인이 될 수 있다.

 

 

 

 

 

new

자료형* 이름 = new 자료형;

int *testInt = new int;

int *testArray = new int[10];

int **testArray2 = new int*[10]; // 이중배열 동적할당

float *testFloat = new float;

double *testDouble = new double;

 

위와 같은 형태로 선언하면 된다. 

 

 

 

 

delete

delete 이름 ;

위와 같은 형태로 사용하면 되며 배열은 이름의 앞에 []를 붙여 준다. 

delete[] 배열 이름;

delete testInt;

delete []testArray;

delete testFloat;

delete testDouble;

 

 

 

 

 

 

 

 

 

참조자와 함수

 

Call-by-value와 Call-by-reference는 많이 들어봤을 것이다.

Call-by-value란 '값'을 인자로 전달하는 함수의 호출 방식, Call-by-reference란 '주소 값'을 인자로 전달하는 함수의 호출방식이다. 

 

 

 

 

 

int main()
{
	int a = 1;
	int b = 2;
    
	CallByValue(a,b);
	CallByReference(&a, &b);
	ReferenceFunction(a, b);
}

위와 같이 구성된 main함수가 있다고 가정하자. 

 

 

 

⊙ Call-by-value!

void CallByValue(int num1, int num2)
{
	num1 = 50;
	num2 = 30; 
}

CallByValue함수는 위와 같이 함수의 내부에서 a와 b의 값을 바꿔도 main함수의 a, b의 값은 바뀌지 않는다.

 

왜?

CallByValue함수의 인자값인 int num1와 int num2는 CallByValue라는 함수의 지역변수이다. 따라서 int num1과 int num2의 값을 바꿔도 해당 num1과 num2는 CallByValue함수가 끝나면 함께 사라지게 되는 것이다.

 

그래서 main함수의 a,b. 즉, 함수 외부에 선언된 변수에 접근이 불가하다. 

 

그래서 필요한 것이 주소 값을 인자로 전달하는 Call-By-Reference이다. 

 

 

 

 Call-by-Reference

void CallByReference(int *num1, int *num2)
{
      *num1 = 30;
      *num2 = 50;
}

 이렇게 되면 int *num1과 int *num2가 주소 값을 넘겨받아서 a와 b의 주소 값에 저장된 값을 '직접' 변경하게 되므로 함수 외부에 선언된 변수의 값을 바꿀 수 있는 것이다. 

 

정리하자면 Call-by-Reference는 주소 값을 전달받아서, 함수 외부에 선언된 변수에 접근하는 형태의 함수 호출이다. 

 

주소 값이 전달되었다는 사실이 중요한 것이 아닌 주소 값이 참조의 도구로 사용되었다는 사실이 중요한 것이며 이것이 Call-by-Value와 Call-by-Reference를 구분하는 기준이 된다.

 

 

 

Call-by-Value와 Call-by-Reference를 구분하는 기준에 대한 추가 설명

더보기

예를 들어

int *Test(int *ptr)
{
	return ptr+1;
}

이 함수는 분명 인자값을 주소로 전달하고 있음에도 Call-By-Reference가 아닌 Call-By-Value이다. 

왜냐하면 이 함수의 연산의 주체는 값이기 때문이다. 다만 그 값이 주소 값일 뿐인 것이다. 주소 값을 이용해 외부에 선언된 변수에 접근하는 Call-By-Reference와 거리가 멀다.

 

int *Test2(int *ptr)
{
	*ptr = 20;
	return ptr;
}

그러나 이런 식으로 사용되었다면 이 함수는 Call-By-Reference이다. 주소 값을 이용해서 함수 외부에 선언된 변수를 참조했기 때문이다. 

 

 

 

C++에서는 함수 외부에 선언된 변수의 접근 방법으로 두 가지가 존재한다. 

하나는 '주소 값'을, 다른 하나는 '참조자'를 이용하는 방식이다.

 

 

 

 참조자를 이용한 Call-By-Reference? 

Call-By-Reference의 가장 큰 핵심은 함수 내에서 함수 외부에 선언된 변수에 접근할 수 있다는 것이었다. 

void ReferenceFunction(int &num1, int &num2)
{
	num1 = 10;
	num2 = 20;
 }

매개변수로 참조자가 들어와 있다. 참조자는 선언과 동시에 변수로 초기화가 되어야 하지 않나?라고 생각할 수 있다.

하지만 매개변수는 함수가 호출되어야 초기화가 진행되는 변수들이다. 즉, 초기화가 이뤄지지 않은 것이 아니라 함수 호출 시 전달되는 인자로 초기화를 하겠다는 의미이다.

 

 

이와 같은 코드는 포인터로 주소값을 넘겨받는 Call-By-Reference 보다 함수의 특성이 한눈에 파악되지 않는다는 단점이 존재한다. 참조자로 넘겨줄 때 함수는 ReferenceFunction(a, b)로 Call-By-Value함수와 동일해 보인다.

 

그러나 실제 함수 외부의 변수 값에 영향을 끼칠 수 없는 CallByValue함수와 달리 이 함수는 함수 외부의 값을 변경할 수 있으므로 이 함수 내부에서 어떤 일이 일어날 지 직관적으로 파악이 불가능하기 때문에 단점이라고 하는 것이다.

 

 

이 단점은 const를 사용함으로써 극복이 가능하다. 함수의 매개 변수 선언 시 const를 붙여줌으로써 함수 내에서 참조자를 이용한 값의 변경을 하지 않겠다고 선언하는 것이다.

 

이렇게 하면 함수 내부에서 참조자에 값을 저장하는 경우 컴파일 에러가 발생하며 함수의 원형만 봐도 값의 변경이 이루어지지 않음을 확신할수 있다. 

 

 

 

 

 

 

 

 

 

 

참조자(Reference)

 

 

변수를 선언하면 해당 변수의 이름은 할당된 메모리 공간을 가리키는, 구분 짓는 이름이 된다. 

그래서 변수를 선언할 때 int a; 를 선언했다면?

이미 a라는 이름이 가리키고 있는 메모리 공간이 있으므로 해당 범위(scope, { })에서 동일한 이름의 변수를 선언할 수가 없다. 

 

 

 

그런데 이 상황에서 number가 가리키는 공간에 또 하나의 이름을 부여하고 싶다면

int &b = a;

위와 같이 b앞에 &를 붙여 선언하면 된다.

이 의미는 b라는 이름도 a라는 메모리 공간을 가리키는 또 하나의 이름이 된 것이다. 

 

 

왜 그럴까? 

보통 & 연산자는 변수의 주소값을 반환하는 연산자이다. 그러나 변수를 선언할 때 &를 붙이게 되면 이는 참조자의 선언을 의미하게 된다. 

 

int *c = &a;  → 변수 a의 주소 값을 포인터 c에 저장
int &b = a;  →  변수 a에 대한 참조자 b를 선언.

 

또한 참조자를 대상으로 참조자를 선언하는 것도 가능하지만 바람직하지는 않으며 딱히 크게 사용할 일이 없다.

int a;

int &b = a;

int &c = b;

 

 

참조자의 중요한 성질 중 하나는 변수에 대해서만 선언이 가능하다는 것과 선언됨과 동시에 참조할 대상이 있어야 한다는 것이다.

또한 NULL로 초기화하는 것도 불가능하다. 

 

int &b;  (X)  → 선언과 동시에 참조할 대상이 있어야 한다.
int &b = NULL;  (X)  → NULL로 초기화 하는 것은 불가능하다.
int &b = 100;  (X)  → 변수에 대해서만 선언이 가능하므로 상수를 넣을 수 없다. 

 

단, 다음 게시글에 나올 const 참조자는 상수도 참조가 가능하다.

+ Recent posts