[C/C++ Syntax] #003 변수(Variable)와 상수(Constant)
이 글에선 우리가 사용할 자료들이 어떤 구조로 이루어져 있는지 상태에 대해 자세히 다루어 볼 예정입니다.
(이 부분을 자세히 이해하고 있어야 뒤에 배울 '포인터'라는 개념의 이해가 쉽습니다.)
변수(Variable)
대부분의 프로그램은 내재된 데이터 혹은 외부로부터 입력 받은 데이터를 연산해가며 동작합니다. 즉, 데이터들이 값(Value)이라는 정적인 형태로만 존재하는 것이 아니라 언제든 연산의 과정에서 변화의 과정을 거치게 됩니다. 이때 우리는 이 값들을 담을 일시적인 저장공간이 필요한데 이를 변수(Variable)라고 부릅니다.
변수가 없는 상황을 가정하여, 1+2=3을 계산하는 프로그램을 다음과 같이 만들 수 있습니다.
#include <stdio.h>
int main(){
1 + 2;
}
이 프로그램을 실행시켜보면 콘솔 창에는 아무것도 나타나지 않고 프로그램이 종료가 되어버립니다. 분명히 컴퓨터는 1+2의 값을 계산했지만 문장이 끝나면서 계산 결과값인 3은 증발해버리고 맙니다.
그렇다면 1 + 2 = 3 이라는 계산과 그 결과를 출력을 하는 프로그램을 다음과 같이 작성해 봅시다.
#include <stdio.h>
int main(){
int a = 1;
int b = 2;
int c;
c = a + b;
printf("%d + %d = %d", a, b, c);
}
이 프로그램은 'a', 'b', 'c'라는이름의 세 개의 변수를 사용했는데요, 위와 같이 변수를 사용함에 있어서 특정한 규칙이 존재합니다. 바로 선언(declare)의 개념입니다. 선언은 프로그래머가 기존 컴퓨터 하드웨어 혹은 컴파일러에 없는 새로운 데이터를 앞으로 이런 이름으로 사용하겠다고 프로그램 시작 최초에 한 번만 알려주는 것으로, 변수 이외에도 다양한 선언이 존재합니다. 이 프로그램에서 main() 함수의 중괄호가 열린 직후에 따라오는 세 문장의 형태를 '변수를 선언한다.' 라고 표현합니다.
선언의 규칙은 다음과 같습니다.
'int'는 integer의 줄임말로 정수형이라는 자료형 중 하나로 선언시에는 반드시 변수가 담을 데이터의 형태를 명시해주어야 하고 이렇게 선언된 변수는 선언시에 명시한 자료만 담을 수 있게 됩니다. 선언 이후에는 자료형을 언급하지 않으며 이런 자료형의 제약은 C/C++에서 굉장히 엄격한 부분입니다.
그리고 'a'는 변수의 이름으로, 나중에 등장할 함수 이름, 클래스 이름 등 프로그래머가 새롭게 선언하는 자료들의 이름을 통틀어 식별자(identifier)라고 부릅니다. 식별자는 혼용을 방지하고자 몇 가지 규칙이 있고 다음과 같습니다.
- 식별자는 영문 대소문자와 숫자, 밑줄 문자(_)로만 이루어진다.
- 숫자를 식별자의 시작으로 사용할 수 없다. (ex 12name) (다만, underscore(_)로 시작하는 식별자는 운영체제에서 종종 사용되므로 사용에 주의해야 합니다.)- 식별자 사이에 공백이 들어가면 안된다. (ex my name) (단어 구분을 위해 my_name, myName 등과 같이 사용합니다.)
- 대문자와 소문자는 구분된다.
(apple와 APPLE는 서로 다른 변수입니다.)
- 예약어(reserved word)는 사용할 수 없다.
(이미 문법에서 사용하고 있는 int, if, for, return 등의 키워드는 사용할 수 없습니다.)
하지만 또 다른 형태가 보이는데요, 아래와 같이 선언 뒤에 '=(값)'의 형태가 추가로 붙는 경우입니다. 변수를 선언하게되면 컴퓨터는 우리가 사용할 새로운 공간을 할당해주는데, 위에서 처럼 자료형과 변수 이름으로만 선언을 하게되면 할당된 공간에는 임의의 값이 들어가게 됩니다. 이를 쓰레기 값이라고 합니다. 이런 쓰레기 값은 프로그램에서 의미를 갖지 않는 값이므로 변수를 사용하기 전에는 반드시 의미 있는 값을 저장해주어야 합니다.
물론 선언 이후에 값을 변경할 수 있지만, 코드가 길어지다보면 쓰레기 값을 갖는 변수를 오용할 경우도 있기 때문에 아래와 같이 대입 연산자(=)를 이용하여 선언과 동시에 값을 저장해주는 경우를 초기화(initialization)라고 부릅니다.
이렇게 선언을 하고 나면 원하는 계산을 해줍니다.
c = a + b;
'a'에 저장된 값 1과 'b'에 저장된 값 2를 산술 연산자 '+'를 사용해 더하여 대입 연산자 '='를 통해 쓰레기 값이 저장되어 있던 'c'에 연산의 결과를 저장해주는 문장입니다. 대입 연산자, 수식 값 등의 부분은 이후 Chapter 02. 수식과 연산자 에서 다루도록 할 것입니다.
이제 프로그램의 마지막인 출력을 담당하는 부분을 살펴보겠습니다.
printf("%d + %d = %d", a, b, c);
우리가 이전에 봤던 "Hello World!"의 출력과는 분명 다른 구조입니다. 바로 형식 지정자(format specifier)의 개념이 등장했기 때문입니다. 일전에 사용했듯이 printf()는 문자열을 인수(argument, 함수 사용 시 소괄호 안에 들어가는 값)로 받아 출력하는 함수입니다. 변수의 개념이 등장하면서 단순 문자열만으로는 출력에 장애가 생깁니다. 문자열을 의미하는 큰 따옴표 안에 변수의 이름을 넣게 되면 변수의 값을 출력하는 것이 아니라 변수 이름 그대로를 출력해버립니다.
예를 들어
printf("a + b = c");
의 출력 결과는 '1 + 2 =3' 이 아닌 'a + b = c'입니다.
그렇다고 숫자를 직접 쓰자니 변수를 사용한 의미가 없어지게 됩니다. 이럴 때 사용하는 것이 형식 지정자입니다. 추후에 자세히 다루겠지만, 이 프로그램에선 %d에 해당합니다. 정수를 출력하라는 의미로 문자열 이후에 콤마를 사용하여 나타나는 정수의 값을 받아서 %d 위치에 대신하여 사용하라는 의미입니다.
총 3개의 %d가 나타났으니 문자열 뒤에도 3개의 정수 값이 나타나며 %d가 나타나는 순서대로 대응됩니다.
상수(Constant)
코딩을 할때 모든 값을 변수에 넣어 사용하지는 않습니다. 컴퓨터에 내재된 값을 불러오는 경우도 있을 것이고, 한 번 정해놓으면 변할 필요가 없는 값들도 있을 것입니다. 이러한 값들을 상수(Constant)라고 말합니다.
예를 들어
int a = 10;
a = a + 1;
변수 'a'의 값을 10으로 초기화하며 선언한 뒤, 그 값을 1 증가 시키는 문장입니다. 여기서 사용된 1과 10은 모두 상수입니다.
이런 상수를 변수에 넣지 않고 특별하게 사용하는 경우가 있는데, 반지름이 10인 원의 둘레와 넓이를 구하여 출력하는 예제를 통해 살펴보겠습니다.
#include <stdio.h>
int main() {
const float pi = 3.141592;
float radius = 10.0;
printf("perimeter = %lf\n", 2 * pi * radius);
printf("area = %lf\n", 0.5 * pi * radius * radius);
}
일단 메인 함수 내부 첫 줄을 제외하고 새롭게 등장하는 부분을 살펴보겠습니다.
float radius = 10.0;
변수를 선언하는 부분인데 자료형 자리에 float라는 키워드가 들어가 있습니다. 다음 글에서 배우게 될 자료형 중 하나인 부동소수점형입니다. 일단은 실수 범위의 수를 의미한다고 생각하면 됩니다.
printf("perimeter = %lf\n", 2 * pi * radius);
먼저 형식지정자 %lf가 새롭게 보입니다. 위에서 %d가 정수형을 나타내는 형식지정자였다면, %lf는 부동소수점형(float)를 나타내는 형식지정자입니다.
그다음 \n이라는 문자가 보입니다.(\와 \는 같은 문자이며 back slash라 부릅니다.) 제어문자(escape sequence) 중 하나인데 제어문자는 컴퓨터의 상태를 제어하는 문자입니다. \n의 경우엔 출력한 뒤 커서를 다음줄로 바꾸는 줄바꿈 문자입니다. 즉, 이 프로그램에서 \n를 넣어주지 않으면 두 줄에 걸친 기존의 출력 결과가 한 줄로 이어져 나오게 됩니다.
이제 본론으로 돌아와 첫 줄을 살펴보겠습니다.
const float pi = 3.141592;
기호 상수(symbolic constant)라 불리는 개념인데요, 특정 상수에 이름을 붙여 의미를 부여하는 기능입니다.
형태를 살펴보면 변수를 선언하는 문장 앞에 const라는 키워드가 붙어있는 것을 확인할 수 있습니다. const는 뒤에 오는 것을 변경시키지 않는 기능을 하는 키워드입니다. 즉, pi라는 변수의 값을 변경시키지 않음으로써 상수로 사용하는 것입니다. 따라서 이후에 pi의 값을 변경시키는 문장을 사용하면 에러가 발생하게 됩니다.
굳이 기호 상수를 사용하는 데에는 이유가 있습니다. 몇 가지 장점을 살펴보도록 합시다.
첫째로, 프로그램의 가독성이 좋아집니다. 짧은 코드에서는 와닿지 않을 수 있지만, 코드의 길이가 늘어나다보면 특정 숫자들이 무엇을 의미했는지 모호해지는 경우가 있습니다. 그렇게 숫자의 의미를 찾는 과정의 낭비를 줄여주는 역할을 합니다.
둘째로, 오타에 의한 잘못된 값을 줄여줍니다. 물론 기호 상수를 정의하는 과정에서부터 오타라면 할 말이 없지만, 기호 상수를 사용하지 않는 상황에서 특정 값이 여러번 반복될 때 어떤 한 값을 잘못 적은 경우가 있어도 프로그램은 그 잘못된 값에 의한 결과를 내놓습니다. 직접 계산해보지 않는 이상 잘못된 값을 모르고 지나갈 경우가 많구요. 하지만 기호 상수를 사용하면 이런 오타에 의한 문제가 많이 줄어들게 됩니다. 또한 기호 상수의 이름에서 오타가 있는 경우엔 에러가 나서 오타의 인지도 가능해집니다.
마지막으로, 값을 변경함에 용이합니다. 위 프로그램만 하여도 3.141592의 소수점 이하를 줄여 3.14로 바꾸고 싶은 경우에 기호 상수 선언 위치에서 한 번만 변경해주면 됩니다. 기호 상수를 사용하지 않았으면 각 값의 모든 위치에서 값을 바꾸어야겠죠.
사실 기호 상수를 표현하는 방법에 #define이라는 전처리기를 이용한 방법도 있으나 잘 사용하지 않는 부분이기도 하고 개념에서도 이른 감이 있어 추후 전처리기 파트에서 추가 설명하도록 하겠습니다.