1. 변수
수학에서의 변수는 수식에 따라 변하는 값을 의미한다.
C언어의 변수는 다음을 의미한다.
어떤 값을 저장할 수 있는 메모리 공간에 붙은 이름. 또는 그 메모리 공간 자체.
쉽게 말해 어떤 값을 담을 수 있는 공간이다.
정확히 말하면, 변수는 운영체제를 통해 할당받은 메모리 공간인 것이다.
할당이란 단어가 낯설다면 전체 메모리 공간에서 일부만 빌려왔다고 생각하자.
그럼 그냥 메모리 공간인데, 왜 변수(Variable)라는 이름이 붙은 걸까?
이유는 간단하다. 메모리 공간에 저장된 값은 언제든 변할 수 있기 때문이다.
n이라는 변수에 3을 저장해서 쓰다가 필요하면 5로 바꿀 수도 있다는 것이다.
변수를 만드는 방법은 다음과 같다.
1
|
int num;
|
cs |
int: 정수(integer)를 저장할 수 있는 메모리 공간을 만든다.
num: 그 공간의 이름을 num이라 한다.
변수를 만든 후에는 다음과 같이 그 이름을 통해 저장된 값을 바꿀 수도, 출력할 수도 있다.
참고로 변수를 만든다는 말 보다는 변수를 선언한다는 말을 더 많이 쓴다.
1
2
3
4
5
6
7
8
9
10
11
|
#include <stdio.h>
int main()
{
int num;
num = 3;
printf("%d\n", num);
...
}
|
cs |
7번째 줄을 보자. num = 3 이라는 식이 있는데, 여기서 = 는 같다는 뜻의 등호가 아니라,
우변을 좌변에 대입하는 대입 연산자다.
그러니까 위 예제에서는 변수 num 에 3 이 대입되는 것이다.
대입 연산자에 대해서는 나중에 설명하겠다.
참고로 다음과 같이 선언과 동시에 대입하는 것은 초기화한다고 한다.
1
|
int num = 3;
|
cs |
변수를 초기화하지 않고 출력하려고 하면 에러가 발생하거나 이상한 값이 출력된다.
이 때 이 이상한 값을 쓰레기값이라 한다.
이런 이유로 변수를 선언하면 초기화하는 게 일반적이다. 보통 0으로 초기화한다.
참고로 여기서 3과 같은 이름이 없는 상수를 리터럴(literal) 상수라고 한다.
변수를 선언할 때의 규칙은 다음과 같다.
① 변수의 이름은 알파벳, 숫자, 언더바(_)로 구성되며, 알파벳은 대소문자를 구분한다.
② 변수의 이름은 숫자로 시작할 수 없다.
③ 키워드(keyword)는 사용할 수 없다.
④ 공백은 사용할 수 없다.
⑤ 중괄호 내에 변수를 선언할 경우, 변수 선언은 중괄호의 앞 부분에 와야 한다.
따라서 다음과 같은 이름은 사용할 수 없다.
int #num; //알파벳, 숫자, 언더바만 사용할 수 있으므로 특수문자는 사용할 수 없다.
int 1stVariable; //숫자로 시작할 수 없다.
int if; //키워드는 사용할 수 없다.
int my name; //공백은 사용할 수 없다.
규칙 ④에서 키워드는 C언어에서 기능적인 의미를 갖는 단어를 말한다.
아직 키워드들을 모르는 게 정상이다.
그러니 야매로라도 구별하자면 다음과 같다.
1
2
|
int num
int if;
|
cs |
차이가 보이는가?
비주얼 스튜디오 등의 C언어 편집기에서는 문법 강조라는 기능을 통해 키워드는 다른 색으로 표시된다. 정 모르겠다면 일단 써보고 색으로 구분하는 방법도 있다.
위 예제의 경우 C언어에서 if 는 조건을 나타내는 키워드이므로 분홍색으로 표시되는 것이다.
물론 이런 구분법은 지금이나 쓰는 것이고, 공부하다 보면 어떤 것이 키워드인지 알게 된다.
규칙 ⑤는 다음과 같이 선언하는 것은 불가능하다는 의미다.
1
2
3
4
5
6
7
8
|
#include <stdio.h>
int main()
{
int a;
a = 3;
int b; //여기서 에러가 발생한다.
}
|
cs |
변수 b 의 선언이 a 에 3을 대입하는 문장보다 뒤에 있다.
따라서 컴파일 에러가 발생하는 것이다.
...라고 옛날 책에 적혀 있을 것이다.
1999년에 발표된 C언어 표준에 따르면 변수의 선언 위치에는 제한이 없다.
하지만 많은 컴파일러가 변수 선언이 중괄호의 앞 부분에 위치하지 않으면 에러로 처리하므로
중괄호에 앞부분에 변수를 선언하는 습관을 들이는 게 좋다.
2. 자료형
먼저, 비트와 바이트에 대해 알아보자.
흔히 컴퓨터를 0과 1밖에 모르는 바보라고 한다. 익히 알려져 있듯이, 컴퓨터는 모든 데이터를 0과 1로 처리한다. 따라서 컴퓨터에서 데이터 표현 최소 단위는 0과 1이라고 할 수 있다.
이 때 0 또는 1 하나가 저장되는 공간의 크기를 1비트(bit)라고 하고,
8비트를 하나로 묶어 1바이트(byte)라고 한다.
자료형이란 데이터를 표현하는 방법, 또는 데이터의 표현 형태라고 할 수 있다.
그러니까 이 변수가 얼만큼의 공간을 차지하는지, 어떤 형태의 수를 저장할 것인지
명시하는 것이다.
예를 들어 앞에서 선언한 int num;의 의미는 다음과 같다.
정수를 저장할 것이며, 크기는 4바이트로 하고, 이름은 num으로 한다.
int는 정수를 저장하는 4바이트의 자료형이다.
그러니까, int형 변수를 선언한다는 것은 전체 메모리 공간에서 4바이트만큼을 떼어 와 쓰겠다는
이야기다.
C언어에서 기본적으로 제공되는 자료형을 기본 자료형이라 하는데,
이는 크게 정수와 실수의 두 가지로 나뉜다.
그리고 정수형과 실수형은 다시 다음과 같이 분류된다.
정수형: char, short, int, long, long
실수형: float, double
앞에서 선언한 변수는 모두 int 형이었다.
만약 float 형 변수를 선언하고 싶다면 다음과 같이 선언하면 된다.
float num;
여기서 의문이 생긴다.
① 왜 실수형과 정수형을 구분할까?
00000000 00000000 00000000 000010001
부호 |
m |
e |
0 |
5 |
126 |
이런 방식의 장점은 표현할 수 있는 수의 범위가 넓어진다는 것이다.
하지만 실수 표현에 오차가 생긴다는 단점이 있다.
예를 들어 위와 같은 방식으로, 다시 말해 m과 e에 적절한 값을 넣어 0.0을 나타낸다고 해보자.
가능할까? 당연히 불가능하다.
이처럼 컴퓨터는 실수의 값을 정확히 표현할 수 없기 때문에 근사치로 표현한다.
이 때 생기는 오차를 부동 소수점 오차라 한다.
다음 예제를 보자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#include <stdio.h>
int main()
{
int i;
float sum = 0.0;
for (i = 0; i < 100; ++i)
sum += 0.1;
printf("%f\n", sum);
return 0;
}
|
cs |
부동소수점 방식은 소수를 다음과 같이 4개의 부분으로 나누어 보는 방식이다.![]()
±: 부호(sign)
m: 가수(mantissa)
n: 기수(base)
e: 지수(exponent)
계산기에서 아주 큰 수를 계산하면 4.22E+29와 같은 표현을 볼 수 있는데,이것이 부동소수점의 대표적인 예다.4.22E+29를 풀어쓰면 다음과 같다.
여기서 +는 부호, 4.22는 가수, 10는 기수, 29는 지수다.
기수는 기수법을 정의하는 정수인데,
기수법이란 2진법 10진법과 같이 수를 나타내는 방법을 말한다.
쉽게 말해 기수는 n진법의 n을 말하는 것이다.
위의 예시의 경우 10진수기 때문에 기수가 10이지만, 컴퓨터에서의 기수는 당연히 2다.
따라서 실제로 실수를 저장할 때 기수는 (당연히 2니까) 생략하고,
다음과 같이 부호와 가수, 지수만 저장된다.
부호(1비트)
지수(8비트)
가수(23비트)
부호 (1비트)
지수(11비트)
가수(52비트)
비트 수는 다양하게 생각할 수 있지만, 위 그림은 *IEEE의 표준에 따른 것이다.
전자는 단정도 부동 소수점이라 하고, 후자는 배정도 부동 소수점이라 한다.부호는 한 개의 비트로 나타낸다. 양수면 0, 음수면 1.수의 크기는 가수부와 지수부로 나타낸다. 가수×2^지수의 형태.이 때 가수부는 정규 표현에 의해 나타내어진다.'부동(不動)' 소수점이라는 이름이 붙은 이유가 여기에 있다.0.75를 부동 소수점 수로 나타내면, 다음과 같이 여러 형태로 나타낼 수 있다.10진수의 경우 소수를 표현할 때 다음과 같은 규칙에 따라야 한다.소수점 이상은 0으로 나타내고 소수점 아래 첫 번째 자리는 0이 아닌 수로 나타낸다.따라서 10진수 0.75는 0.75×10으로만 나타낼 수 있다.이처럼 어떤 규칙에 따라 (움직이지 않는 소수점으로) 소수를 표현하는 방법을 정규 표현이라 한다.당연한 이야기지만 2진수는 10진수와 정규 표현의 규칙이 다르다.2진수 정규 표현의 규칙은 다음과 같다.소수점 이상의 값을 1로 고정한다.2진수 1011.0011을 단정도 부동소수점으로 나타낸다고 하면,가수부(m)는 다음과 같은 과정에 의해 결정된다.① 소수점 바로 위 첫 번째 자리가 1이 되도록 비트 열을 오른쪽으로 민다.-> 1.0110011② 소수점 아래를 23비트로 만든다.-> 1.01100110000000000000000③ 소수점 아래만 떼어낸다.-> m = 01100110000000000000000다음은 지수부다.왜 e 가 아닌 e-127 이고, 하필 127 을 빼주는 걸까?앞의 그림을 다시 보자.
부호(1비트)
지수(8비트)
가수(23비트)
지수는 8비트다. 따라서 00000000~11111111(255)의 수를 나타낼 수 있다.
그런데 지수부에서는 음수를 표현해야 하는 경우도 생긴다.이 때 별도의 비트를 사용하지 않고 나타내기 위해 다음과 같은 방법을 사용한다.① 지수부에서 나타낼 수 있는 최대값을 반으로 나눈다. (여기서는 255÷2=127이 된다.)② 그 값을 0으로 간주한다.즉, e가 127일 때 0으로 치겠다는 것이다.따라서 지수에 자리에 e-127이 오는 것이다.이런 방법을 익세스(excess) 표현이라 한다.*IEEE(Institute of Electical and Electronics Engineers): 미국전기전자협회. 컴퓨터 분야의 다양한 규격을 제정하는 단체다.
② 왜 실수형과 정수형을 다시 여러 개로 분류할까?
자료형 |
크기 |
표현 범위 |
|
정수형 |
char |
1바이트 |
-128 ~ +127 |
short |
2바이트 |
-32,768 ~ +32,767 |
|
int |
4바이트 |
-2,147,483,648 ~ +2,147,483,647 |
|
long |
4바이트 |
-2,147,483,648 ~ +2,147,483,647 |
|
long long |
8바이트 |
-9,223,372,036,854,775,808 ~ +9,223,372,036,854,775,807 |
|
실수형 |
float |
4바이트 |
![]() |
double |
8바이트 |
![]() |
|
long double |
8바이트 이상 |
double형 이상 |
short와 int는 2바이트 이상으로 하되, int의 크기는 short 이상이어야 한다.
unsigned int size = sizeof(int)
A는 65로, B는 66로, C는 67로 표현하자. 그리고 D는 68로, E는 69로, ...
아스키 코드 A의 아스키 코드 값은 65고,아스키 코드 B의 아스키 코드 값은 66고,아스키 코드 C의 아스키 코드 값은 67이라 하자.그리고 아스키 코드 D의 값은 68이고,아스키 코드 E의 값은 69고, ...

1
2
3
4
5
6
7
|
int main()
{
char c1 = 'A';
char c2 = 65; //A
...
}
|
cs |
① double num = 437;
② int num = 3.141592;
③ char ch = 259;
00000000 00000000 00000001 00000011
00000011
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#include <stdio.h>
int main()
{
double num1 = 436;
int num2 = 3.141592;
char ch = 259;
printf("num1: %f\n", num1);
printf("num2: %d\n", num2);
printf("ch: %d\n", ch);
return 0;
}
|
cs |
④ short num1 = 3, num2 = 5;short num3 = num1 + num2;
일반적으로 CPU가 처리하기에 가장 적합한 크기의 정수를 int형으로 정의한다.
따라서 int형 연산 속도가 다른 자료형의 연산 속도와 동일하거나 더 빠르다.
※ 정말 int형 연산이 short형 연산보다 빠를까?현대의 컴퓨팅 환경에서는 차이가 없다고 봐도 무방하다.int형 연산이 short형 연산보다 빠른 것은 CPU의 구조 때문인데,이미 CPU의 성능 및 구조가 이전에 비해 많이 발전해서 연산 속도의 차이는 거의 없다.하지만 앞으로 어떤 CPU가 등장할 지 모르고,이미 만들어진 CPU도 아주 많기 때문에 정수의 승격은 여전히 유의미하다고 볼 수 있다.
⑤ double num = 1.14 + 2;
데이터 손실을 최소화하는 방향으로 일어난다.
int -> long -> long long -> float -> double -> long double
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#include <stdio.h>
int main()
{
int num1 = 3, num2 = 4;
double result;
result = num1 / num2;
printf("result: %f\n", result);
return 0;
}
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#include <stdio.h>
int main()
{
int num1 = 3, num2 = 4;
double result;
result = (double)num1 / num2;
printf("result: %f\n", result);
return 0;
}
|
cs |
num1을 double형으로 변환하라는 의미다.
result = 3.0 / num2
result = 3.0 / 4.0
00000000 00000000 00000000 0000000111111111 11111111 11111111 11111111
정수 자료형 |
크기 |
표현 범위 |
char |
1바이트 |
-128 ~ +127 |
unsigned char |
0 ~ +(127 + 128) |
|
short |
2바이트 |
-32,768 ~ +32,767 |
unsigned short |
0 ~ +(32,767 + 32,768) |
|
int |
4바이트 |
-2,147,483,648 ~ +2,147,483,647 |
unsigned int |
0 ~ +(2,147,483,647 + 2,147,483,648) |
|
long |
4바이트 |
-2,147,483,648 ~ +2,147,483,647 |
unsigned long |
0 ~ +(2,147,483,647 + 2,147,483,648) |
|
long long |
8바이트 |
-9,223,372,036,854,775,808 ~ +9,223,372,036,854,775,807 |
unsigned long long |
0 ~ +(9,223,372,036,854,775,807 + 9,223,372,036,854,775,808) |
참고로 signed int 와 같은 표현도 가능하지만, int 와 다를 게 없으므로 대부분 생략한다.
'Programming > C' 카테고리의 다른 글
입력과 출력 (0) | 2019.03.02 |
---|---|
연산자 (0) | 2019.03.01 |
C 프로그램의 기본 구조 (1) | 2019.02.24 |
Visual Studio 2017 설치 (0) | 2019.02.24 |
About C Language (0) | 2019.02.22 |