Unifox C언어 보고서 (상)

저자

  • 선린인터넷고등학교 소프트웨어과, Unifox 10기 나정휘

제 1장 float와 double의 작동 방식

가. 부동 소수점 방식의 구조
C언어의 실수형 자료형인 float와 double은 실수를 부동 소수점 방식으로 저장한다. 부동 소수점 방식은 자료형의 비트를 가수, 기수, 지수 부분으로 나누어 저장한다.
± m * n^p (m:가수, n:기수, p:지수)
n진법에서 n이 기수가 된다.
부동 소수점은 실수를 정확하게 표현하지는 못하고 유효 자릿수만큼만 표현 가능하다. 만약 10진법에서 유효 자릿수가 5이고 표현하고 싶은 수가 1.234567이라면 실제로는 1.234567을 반올림 해서 5째 자리까지 나타낸 1.23457로 저장이 된다.
32비트에서는 부동 소수점이 다음과 같이 표현이 된다.
부호 1bit, 지수 8bit, 가수23bit

나. 부동 소수점 변환
-118.625를 32비트 부동 소수점으로 나타내보자.

  1. 음수이므로 부호는 1이 된다.
  2. 절댓값인 118.625를 이진법으로 나타내면 1110110.101이 된다.
  3. 소수점을 이동시켜 소수점 왼쪽에는 1만 남게 만든다. (1.110110101 * 2^6)
  4. 가수 부분은 소수점의 오른쪽 부분이 되고, 부족한 비트는 뒤쪽에 0을 추가해 23비트로 만든다.
  5. 지수는 6이지만, 후술할 설명에 의해 bias인 127을 더해 133이 되고, 이진법으로 변환하면 10000101이 된다.
  6. 모두 정리해보면 11000010111011010100000000000000이 된다.

참고로 double형은 부호 1bit, 지수 11bit, 가수 52bit, 총 64bit으로 이루어져 있다.

다. 왜 bias를 더하는가?
8bit로는 256가지를 표현할 수 있지만, 최소값인 0은 실제 숫자 0을 표현하기 위해 예약이 되어 있고, 최대값인 255는 무한대를 표현하기 위해 예약되어 있다. 즉, 8bit 중에서 실제 사용 가능한 범위는 1~254이다. 이를 음/양수로 나누어서 할당하기 위해 127을 빼준 -126 ~ 127의 범위를 이용하고, 저장할 때는 127(bias)를 더하여 저장해준다.

제 2장 const와 static

가. const의 역할
const는 constant의 약자이며, ‘변함 없는’의 뜻을 갖고 있다. 변수를 선언할 때 앞에 const를 붙이면 값과 주소를 변경할 수 없게 만들고, 이를 ‘상수’라고 한다.

나. const의 사용
int a = 5; 를 이용해 선언을 하면 이후에 a = 4; 와 같이 값을 변경할 수 있지만, const int a = 5; 를 이용해 선언하면 a = 4; 처럼 값을 변경할 수 없다.
이렇게 변하지 않는 특성 때문에 원주율(3.141592653…)이나 하루 24시간 같은 변하지 않는 값을 대개 const를 사용해 선언한다.

다. static의 역할
static은 지역변수와 전역변수의 성질을 모두 갖고 있다. static변수는 지역변수처럼 선언된 블록 내부에서만 사용이 가능하지만, 전역변수처럼 프로그램이 종료될 때까지 메모리 공간에 사라지지 않고 남아있는다.

라. static의 사용
함수 내부에서 int a = 5; 를 이용해 변수를 선언하면 그 함수가 종료될 때 메모리에서 변수가 없어진다. 하지만 static int a = 5; 를 이용해 선언하면 함수가 종료 되고 메모리에 변수와 값이 그대로 남아있는다.

제 3장 함수

가. 함수란?
값만 바뀌면서 같은 코드가 반복이 된다면 코드가 길어지고 가독성이 떨어지며 작성 도중 실수할 가능성도 생긴다. 이 때문에 ‘함수’ 를 쓴다. 함수는 특정 용도의 코드를 한 곳에 모아 놓은 것이다. 그래서 한 번 작성하면 계속 불러서 쓸 수 있다.

나. 함수 정의와 실행
함수를 정의하는 방법은 다음과 같다.

1
2
3
반환_자료형 함수_이름(매개변수){
    실행할 
}

매개 변수에는 함수에 전달할 값을 적는다. F(x) = 5x처럼 F라는 함수에 x값을 전달해주는 형식이다.
만약 n번 만큼 HelloWorld를 출력하고 아무것도 반환하지 않는 함수를 만들고 싶다면 다음과 같이 하면 된다.

1
2
3
4
5
6
void print(int n){
    int i;
    for(i=0; i<n; i++){
        printf(HelloWorld\n);
    }
}

5번 출력하고 싶다면 print(5); 와 같이 함수를 호출해주면 된다.
두 정수가 주어지고, 그 수의 합을 반환하는 함수를 만들자.

1
2
3
int sum(int a, int b){
    return a + b;
}

값을 반환할 때에는 return 값; 형태로 써주면 된다.

1
2
3
4
5
6
7
8
void hello(){
    printf(HelloWorld);
}

int main(){
    hello();
    return 0;
}

이 코드는 다음과 같이 실행된다.

제 3장 배열과 포인터

가. 배열이란?
특정 기능에만 쓰이는 int형 변수를 100개 선언하는 것은 비효율적이다. 그래서 ‘배열’이라는 것을 사용한다. 배열은 같은 자료형인 여러 개의 변수를 쉽게 선언할 수 있고, 그 데이터들은 메모리에 연속적으로 할당이 되어 있어 각각의 요소에 쉽게 접근할 수 있다.

나. 배열의 선언과 접근
배열 선언은 다음과 같이 한다.
자료형 배열_이름[개수];
만약 int형의 변수 100개를 배열을 이용해 저장하고, 그 배열의 이름을 arr로 한다면 int arr[100]; 과 같이 선언한다.>br? 배열에 있는 각각의 요소는 인덱스로 접근이 가능하다. 인덱스는 0부터 시작하고 (길이-1)번까지 접근 가능하다. 만약 첫 번째 요소의 값을 가져오고 싶다면, arr[0] 으로 가져올 수 있고, 10번째 요소는 arr[9]로 가져올 수 있다.

다. 데이터의 주소 값
데이터의 주소는 해당 데이터가 저장된 메모리 주소의 첫 번째 바이트를 나타낸다.


라. 포인터
C언어에서 포인터는 메모리의 주소 값을 저장하는 변수이다.
변수의 주소는 &변수 로 가져온다.

1
2
int n = 100;
int *ptr = &n;



마. 포인터와 배열의 관계
배열의 이름은 배열의 첫 번째 요소(0번 인덱스)의 주소이다. 그리고, 거기에 n을 더하면 n번 인덱스의 주소를 가져올 수 있다.
예를 들어, int arr[100]; 로 배열을 선언했다고 하자. arr 자체는 arr[0]의 주소를 나타낸다. arr+5는 arr[5]의 주소를 나타낸다. 그래서 arr[i]는 *(arr+i)처럼 쓸 수 있고, &arr[i]는 arr+i로 쓸 수 있다.
참고로, 포인터에 대한 덧셈 연산은 +1 할 때 마다 sizeof(타입)만큼 증가한다.

바. 배열 포인터와 포인터 배열
이름이 혼동될 수 있다. 배열 포인터는 위에서 설명한 것과 같이 배열 이름 자체가 주소 값을 나타내는 것을 말한다. 포인터 배열은 배열의 요소가 포인터인 것을 만한다. int형 배열은 배열의 요소가 int형인 것처럼 포인터 배열은 요소가 포인터이다.


(하) 에서 계속…