C언어 (2) 변수와 상수
프로그램은 사용자에게 입력을 받고 그에 따라 다른 값을 내놓는다.
상수와 값을 저장하는 공간인 변수의 차이를 알아보고 입력 함수 scanf를 써보자.
변수
변수란 우리가 프로그램을 작성할 때 숫자나 문자 같은 데이터를 저장하기 위한 공간이다.
1
2
3
4
5
6
7
8
9
#include <stdio.h>
int main() {
int a, b;
a = 3, b = 5;
printf("%d", a + b);
return 0;
}
와 같이 더하기 프로그램을 만든다고 하면 a와 b라는 어떤 값을 저장할 변수가 필요하다.
변수는 컴퓨터에 있는 RAM에 저장되는데 RAM은 밑의 그림처럼 주소와 공간이 있다.
여기에 a와 b에 대한 공간을 컴퓨터가 알아서 할당해주는 것이다.
int, double
코드에서 a와 b 앞에 붙은 int는 무엇일까?
서식 문자표를 보면 정수, 실수, 문자 등을 출력하는 것이 있다.
변수에 3을 저장하려면 정수형, 3.5를 저장하면 실수형 ‘A’를 저장하면 문자형 변수를 선언해야 한다.
이 형태를 자료형이라고 하는데, int는 정수형, double은 실수형 변수를 만드는데 사용된다.
이런 int나 double 뒤에 변수의 이름을 적으면 공간이 할당되고, 그곳에 a=3처럼 값을 적울 수 있다.
변수를 선언한 다음에 값을 할당하지 않으면 어떻게 될까? 0이 나온다고 생각하기 쉽지만 그렇지 않다.
1
2
3
4
5
6
7
8
#include <stdio.h>
int main() {
int a, b;
printf("%d %d", a, b);
return 0;
}
같은 코드를 실행하면 오류가 나거나 -858298740 와 같은 아무런 의미 없는 쓰레기 값이 출력된다.
위에서 변수를 만들 때 어떠한 메모리 공간을 할당받는다고 했는데, 초기화를 해주지 않는다면 그 공간에
어떤 값이 들어가있었는지 모르고, 의미없는 값이 들어가있었다면 그것이 그대로 출력되는 것이다.
그렇기에 변수를 선언할 때 int a = 0 같이 초기화를 해주면 된다.
선언할 때 0으로 초기화 시켜주면 편하겠지만 C언어는 불필요한 일을 최대한 줄이기에 0으로 초기화하는 연산 과정을 추가로 하지 않는다.
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
int main() {
int a = 1;
printf("%d\n", a);
a = 11;
printf("%d", a);
return 0;
}
변수는 다른 값으로 바꿀 수 있고 위의 코드는 첫번째는 1, 두번째는 11이 출력된다.
변수의 이름
변수의 이름을 짓는 것은 꽤 귀찮은 일이기에 a, b, c, d.. 와 같이 대충 선언할 수 있다.
하지만 실제 프로그램을 짜면 코드가 길어지고 이 변수가 어디에 사용한 변수인지 기억이 나지 않을 때
곤란할 수 있다. 그렇기에 주석을 다는 것과 비슷한 느낌으로 이름을 지어줘야 한다.
1
2
3
4
5
6
7
8
9
#include <stdio.h>
int main() {
int current_year = 2024;
int birth = 2002;
printf("나이는 %d살 입니다.", current_year - birth);
return 0;
}
와 같이 코드를 짜면 변수의 이름만 보고도 나이를 구하는 프로그램임을 알 수 있다.
주의할 점
변수의 이름으로 지을 수 없는 것들이 있다.
int ?apple, int to tal, int 2bana와 같이 특수문자나 숫자가 맨 앞에 올 수 없으며 띄어쓰기가 안된다.
또한 int, char, if, max, min 같이 이미 지정된 의미가 있는 예약어들은 사용할 수 없다.
보통 띄어쓰기는 _ 로 이어주고 변수 이름은 대소문자를 구분하기에 잘 보고 써야한다.
상수
상수란 변수와 다르게 변하지 않는 값을 가지는 것이다.
10 = 5; 라고 한다고 해서 10이 5로 바뀌지 않는 것과 같다.
const를 이용해 변수 이름을 가졌지만 값이 바뀌지 않게 상수로 만들 수 있는데
이렇게 const int로 변수를 선언하면 a는 5라는 값으로 고정된다.
또 다른 방법으로 #include 밑에 #define PI 3.14 이런식으로 선언하면 PI는 3.14를 갖는 상수가 된다.
#define ll long long 이렇게 쓰면 long long이라는 자료형을 선언할 때 ll로 줄여 쓸 수도 있다.
scanf
printf로 콘솔 화면에 출력을 할 수 있었지만 아직 우리가 키보드로 값을 입력하는 방법은 모른다.
그 때 사용하는 것이 stdio.h에 선언되어 있는 scanf()이다.
1
2
3
4
5
6
7
8
#include <stdio.h>
int main() {
int a;
scanf("%d", &a);
printf("%d", a);
return 0;
}
변수 a에 정수를 입력받는 코드로, &는 변수의 주소를 뜻하는 연산자이다.
변수는 RAM의 공간에 할당이 되는데, scanf 함수는 그 공간의 주소에 접근해 저장하기 때문이다.
그런데 visual studio환경에서 이 코드를 그대로 쓰면 오류가 발생한다.
scanf() 함수에는 buffer overflow라고 부르는 취약점이 있는데 변수의 주소에 접근하는데
그림처럼 변수 a가 주소 100에 크기가 4만큼 할당이 되었다고 하자.
그런데 입력으로 크기가 5만큼인 입력이 들어오면 주어진 범위를 넘어 값을 쓰게 된다.
그림의 주소가 104인 공간은 어떤 곳에 사용되고 있는데, 만약 시스템의 중요한 부분이라면
아주 위험한 접근을 허용하게 되는 것이다. 그렇기에 visual studio는 scanf에 에러를 낸다.
해결 방법으로 scanf_s()를 사용하도록 제시하지만 이는 다른 곳에서 호환이 잘 안되기에
추천하는 방법으로는 코드 맨 위에
- #define _CRT_SECURE_NO_WARNINGS
- #pragma warning(disable:4996)
둘 중 하나를 써주면 에러가 사라진다. 위에는 _s에 관련한 보안 검사를 해제하는 것이고.
밑에는 이 버퍼 오버플로우 관련 에러의 이름이 C4996이라 4996 에러의 경고를 끄는 것이다.
scanf 뿐아니라 gets, strcat, strcpy, fopen 등이 같은 오류가 발생한다.
이렇게 입출력 하는 방법을 보았다.
다음은 변수를 선언할 때 붙었던 int, double과 같은 자료형과 bit에 대해 알아보자.