기호 상수
symbolic constant
- 특정 상수에 변수처럼 이름을 부여한 것이다.
- 프로그래밍의 편의성과 소스 코드의 가독성을 높이는 역할을 한다.
- #define 지시자는 전처리기에 해당 기호 상수가 소스 코드에 나올 때 마다 해당 상수 값으로 대체할 것을 명령한다.
(Search & Replace)
- 전처리는 독립 토큰 (띄어쓰기가 된 이름) 에만 적용된다.
ex) "INT_MAX"가 기호 상수로 정의된 상태에서, "PINT_MAXIM"은 상수 값으로 대체되지 않는다.
기호 상수 정의법
1. Preprocessor Directive : "#define" 이용
- C에서 계승된 기호상수 정의방법
- 전처리기 지시자(preprocessor directive)인 "#define" 을 통해 정의한다.
- 전처리기 지시문이 작성된 위치에 같이 작성한다.
#define (Name) (Value) // 정의 형태
#define INT_MAX 32767 // 예시
2. C++ Keyword : "const" 이용 (권장!)
- C++에서 새로 고안된 기호상수 정의방법이다.
- 변수 선언의 의미를 제한하므로 const 제한자(qualifier)라 부르기도 함
- data type 앞에 const 키워드를 명시하고 기존과 같이 변수를 선언하는 방식으로 작성한다.
- 기호 상수임을 알아보기 용이하도록, 기호상수명은 대문자로 선언하는것이 관례로 통한다.
(변수와 기호 상수를 구분하기에 용이)
- 혹은 기호 상수임을 의미하게끔 모든 기호상수 앞에 k를 붙여서 선언하기도 한다. (ex. kmonths)
- ANSI C에서도 const제한자가 사용가능하다.
const int MONTHS = 12; // 앞으로 MONTHS는 숫자 12를 의미하는 기호 상수이다.
- K&R C에서는 정적 배열과 정적 구조체만 초기화할 수 있었으나, C++ Release2.0 또는 그 이후 버전과 ANSI C에서는 자동 배열과 자동 구조체까지 초기화 할 수 있는 범위를 확장시켰다.
- const 기호 상수는 선언 즉시 초기화 되어야 하며 아래 코드는 권장하지 않는다.
const int toes;
toes = 10;
// Not Recommended!
포인터와 \(\texttt{const}\)
- \(\texttt{const}\) 키워드는 포인터에 두 가지 방법으로 사용된다.
- 기본형을 지시하는 포인터에 대해서만 \(\texttt{const}\)를 사용할 수 있다.
1. 상수 객체를 지시하는 포인터를 만드는 방법이다. (지시하는 데이터에 대한 수정을 불허)
const typeName* pointerName;
// 실 사용 예시
const int* ptr = &val; // *ptr 값에 대한 어떠한 수정도 불가능하게 된다.
- 첫 번째 방법의 경우, 지시하는 값이 앞으로 상수가 되었음을 의미하는 것은 아니며, 포인터가 관계하는 한에서만 그 값이 상수로 취급됨을 의미한다.
- 포인터로 접근했을 경우에 수정이 불가한것이지, 변수로 직접 접근할 경우 물론 수정이 가능해진다. ((\(\texttt{const}\) 변수가 아닌 경우)
2. 포인터가 지시하는 주솟값을 상수로 만드는 방법이다. (포인터가 지시하는 주소에 대한 수정을 불허)
typeName* const pointerName;
// 실 사용 예시
int* const ptr = &val; // ptr이 추후에 다른 변수를 지시할 수 없게 된다.
Ex. \(\texttt{const}\) 포인터의 구분법
int val = 3;
const int* ps = &val;
int* const ptr = &val;
// *ps 와 ptr 은 const이고, (수정이 불가)
// ps와 *ptr 은 const가 아니다. (수정이 가능)
* C++에서는 \(\texttt{const}\) 변수의 주소를 \(\texttt{const}\) 포인터가 아닌 일반 포인터에 연결시키는 것을 금지한다. (\(\texttt{const_cast}\) 연산자를 통한 데이터형 변환을 이용하면 가능하긴 하다.)
- \(\texttt{const}\) 변수의 의미를 퇴색시킬 수 있기 때문이다.
const float g_earth = 9.80;
const float* pe = &g_earth; // 가능한 구문이다.
const float g_moon = 1.63;
float* pm = &g_moon; // 불가능한 구문이다.
- \(\texttt{const}\)가 아닌 포인터를 \(\texttt{const}\) 포인터에 대입하는 것은 한 다리 건너는 간접 지시인 경우에만 사용할 수 있다.
* 포인터 매개변수에 \(\texttt{const}\)가 권고되는 이유
- 실수로 데이터를 변경시키는 프로그래밍 에러를 미연에 방지한다.
- \(\texttt{const}\)를 사용하는 함수는 \(\texttt{const}\) 변수와 아닌 변수 모두를 처리할 수 있지만, 함수 원형에서 \(\texttt{const}\)가 생략된 함수는 \(\texttt{const}\) 변수를 처리할 수 없다.
#define 방식보다 const 제한자를 이용한 방식의 장점
- 데이터형을 명시적으로 지정할 수 있다.
- C++의 활동 범위 규칙 * 에 의해 그 정의를 특정 함수나 파일에서만 사용할 수 있도록 제한할 수 있다.
- 배열이나 구조체와 같은 복합 데이터형에도 사용할 수 있다.
- ANSI C 와 달리 C++에서는 const 제한자를 이용하여 배열의 크기를 선언할 수 있다.
* 활동 범위 규칙 (scoping rules)
- 어떤 식별자가 서로 다른 여러 모듈에 얼마나 널리 알려지는가를 나타내는 규칙
기호상수 라이브러리
Header file : climits
Symbolic Constant | Meaning |
CHAR_BIT | char형의 비트 수 |
CHAR_MAX | char형의 최댓값 |
CHAR_MIN | char형의 최솟값 |
SCHAR_MAX | signed char형의 최댓값 |
SCHAR_MIN | signed char형의 최솟값 |
UCHAR_MAX | unsigned char형의 최댓값 |
SHRT_MAX | short형의 최댓값 |
SHRT_MIN | short형의 최솟값 |
USHRT_MAX | unsigned short형의 최댓값 |
INT_MAX | int형의 최댓값 |
INT_MIN | int형의 최솟값 |
UINT_MAX | unsigned int형의 최댓값 |
LONG_MAX | long형의 최댓값 |
LONG_MIN | long형의 최솟값 |
ULONG_MAX | unsigned long형의 최댓값 |
LLONG_MAX | long long형의 최댓값 |
LLONG_MIN | long long형의 최솟값 |
ULLONG_MAX | unsigned long long형의 최댓값 |