다음과 같은 코드가 있다. 현 시점에서 add_exceptionadd_assert 함수의 로직에 대해서는 고려하지 않는다.

int main() {
    int size = 1000000000;

    // x, y의 입력값은 각각 정상범위 (-2,147,483,648 ~ 2,147,483,647)라고 가정.
    int32_t x = 20000;
    int32_t y = 10000;
    int32_t *result_exception = new int32_t[size];
    int32_t *result_assert = new int32_t[size];

    std::chrono::time_point<std::chrono::system_clock> start, end;
    std::chrono::duration<double> elapsed;
    start = std::chrono::system_clock::now();
    for(int i=0; i<size; ++i) {
        try {
             result_exception[i] = add2_exception(x, y);
        } catch(invalid_argument& ia) {
            cout << ia.what() << endl;
        }
    }
    end = std::chrono::system_clock::now();
    elapsed = end - start;
    std::cout << "using exception: " << elapsed.count() << " seconds" << std::endl;

    std::chrono::time_point<std::chrono::system_clock> start2, end2;
    std::chrono::duration<double> elapsed2;
    start2 = std::chrono::system_clock::now();
    for(int i=0; i<size; ++i) {
        result_assert[i] = add2_assert(x, y);
    }
    end2 = std::chrono::system_clock::now();
    elapsed2 = end2 - start2;
    std::cout << "using assert: " << elapsed2.count() << " seconds" << std::endl;

    // 작업이 다 끝난 후 힙메모리에 할당한 변수를 반드시 지워준다.
    delete[] result_exception;
    delete[] result_assert;
    return 0;
}

이 코드에는 중요한 버그가 하나 있는데, new를 통해 힙메모리 할당을 해주는 부분에서 결함이 있다.

변수를 함수 호출 초기에 선언한 후 실행이 끝나기 직전에 delete를 통해서 메모리 해제를 해주도록 하였는데, 달리 말해서 실행이 도중에 멈추면, 즉 delete를 호출하기 전에 프로그램이 죽는 문제가 발생하면?

프로세스가 죽는 이유로는, 내부적인 문제가 원인이 될 수도 있지만 프로세스 외부의 컨디션 등에 문제가 있어서 프로세스가 죽는 원인이 될 수도 있다. 외부의 문제로 인해 프로세스가 죽는다고 가정해 보면, delete 근처로 가기도 전에 프로세스가 죽을 수도 있고, 그렇게 되면 이 프로그램은 메모리 누수를 발생시킨다. 실제 현업에서도 메모리 누수가 발생하여 주기적으로 메모리를 비워주기 위해 물리적으로 서버를 재부팅 하는 경우가 많다.

이런 문제는 네이키드 포인터를 활용해서는 해결이 어렵기 때문에, 스마트 포인터의 활용이 권장된다.

스마트 포인터는 어떤 원리로 동작하는가?

간단하게 말하자면, 스마트 포인터는 소멸자 안에다가 delete를 삽입한 개념이다. 즉 함수 안에서 생성한 변수가 스택 프레임이 날라가면서 지워질 때, 자동적으로 소멸자 호출을 통해 메모리를 해제해 주는 것이다. 그렇기 때문에 스마트 포인터를 사용할 때는 예상치 못한 문제로 인해 프로세스가 도중에 죽더라도 메모리 해제는 하고 죽게 된다.

요새 운영체제에서는 메모리 누수를 주기적으로 감지해서 해제해 준다는데?

사실, 요새라고 하기엔 이미 10년 가까이 지난 이슈이긴 하다. 어쨌든 이것을 고려했을 때는 굳이 네이키드 포인터를 사용하는 것을 주의할 필요가 없지 않은가?

다음 경우를 보자.

(1) 운영체제에서 주기적으로 메모리 누수를 해제한다고 하는데, 그 주기보다 프로세스가 죽었다가 다시 실행되는 주기가 더 빠른 경우
(2) 메모리 누수를 운영체제에서 바로잡는데 발생하는 별도의 오버헤드 발생

이를 봤을 때 안정적으로 운영하려면 그래도 스마트 포인터를 쓰는게 맞다.

최종: 위의 코드를 그럼 어떻게 고치나?

우선 스마트 포인터를 이용한 동적 할당을 다음과 같이 선언해 줄 수 있다.

unique_ptr<int32_t[]> result_exception(new int32_t[size]);
unique_ptr<int32_t[]> result_assert(new int32_t[size]);

그리고 나서 delete 부분은 필요가 없어졌으므로 지워주면 된다.

반응형

네이키드 포인터 (naked pointer)

우리가 흔히 말하는 *를 이용하여 생성된 그 포인터를 말함 (plain pointer).

#include <memory>

struct X { int a,b,c; };

int main()
{
    X* np = new X;
    delete np;
    return 0;
}

여기서 np 변수가 네이키드 포인터를 말한다. np는 동적으로 할당되어 힙메모리 안에 저장이 되는데, 자원의 활용이 끝나면 delete를 활용하여 객체에 할당된 메모리를 반드시 해제해 줘야만 메모리 누수를 막을 수 있다. 해제하지 않으면 힙메모리가 자원을 계속 들고 있어서 성능상 문제가 발생하기 때문이다.

이렇게 개발자가 직접 메모리 해제에 관여해줘야 하는 네이키드 포인터와 달리, 개발자가 직접 메모리 해제에 관여하지 않아도 되도록 설계된 포인터가 스마트 포인터.

스마트 포인터

C++11부터 등장한 새로운 포인터 선언 방식.

스마트 포인터 사용을 위해서는 위의 코드에서 보이듯 #include <memory> 선언을 해줘야 한다.

[예제 1-1] 스마트 포인터를 활용한 예제 코드

#include <iostream>
#include <memory>

using namespace std;

class Blablabla {

    public:
        Blablabla() {
            cout << "자원 획득" << endl;
        }

        void call() {
            cout << "did you call me?" << endl;
        }

        ~Blablabla() {
            cout << "자원 해제" << endl;
        }
};

int main() {
    unique_ptr<Blablabla> mB1(new Blablabla());
    mB1->call();
    return 0;
}

여기서 unique_ptr이 스마트 포인터인데, 스마트 포인터 종류로는 unique_ptr, shared_ptr, weak_ptr 세가지가 있다. 우선 이 문서에서는 unique_ptr, shared_ptr 둘만 다루도록 하겠다.

(1) unique_ptr

여기서 unique는 유일하다는 의미로 쓰여서 '리소스에 대한 유일한 소유권'을 갖는 포인터라는 뜻. 달리 말해서 다른 객체가 unique_ptr로 선언된 리소스에 대한 소유권을 가질 수 없다. 여기서 말하는 소유권을 가질 수 없다는 것은 "타 객체가 이 객체로의 접근을 할 수 없다"라는 뜻 정도 되는것 같다.

[예제 1-2] unique_ptr로 선언된 리소스에 대하여 다른 객체로부터 접근 시도

unique_ptr<Blablabla> mB2 = mB1;
mB2->call();

위의 예제에서는 mB2 객체를 선언한 후, 이 객체가 mB1에 접근하도록 선언해 주고 있는데, 이를 컴파일 하면 에러가 발생한다.

marshall@marshall-IdeaPad-5-15ITL05:~/learn-cpp$ g++ smart_pointer.cpp 
smart_pointer.cpp: In function ‘int main()’:
smart_pointer.cpp:26:30: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Blablabla; _Dp = std::default_delete<Blablabla>]’
   26 |  unique_ptr<Blablabla> mB2 = mB1;
      |                              ^~~
In file included from /usr/include/c++/9/memory:80,
                 from smart_pointer.cpp:2:
/usr/include/c++/9/bits/unique_ptr.h:414:7: note: declared here
  414 |       unique_ptr(const unique_ptr&) = delete;
      |       ^~~~~~~~~~

에러 메시지에서 말하길 삭제된 함수를 사용하려 한다고 하는데, 이 메시지의 내용을 좀 더 들여다볼 필요가 있다.

(2) shared_ptr (공유 포인터)

unique_ptr과 다르게 shared_ptr을 이용하면 객체 공유가 가능해진다. 예제 1-1의 메인함수 내용을 다음과 같이 고친다.

shared_ptr<Blablabla> mB1(new Blablabla());
mB1->call();

shared_ptr<Blablabla> mB2 = mB1;
mB2->call();

이를 실행하면 다음과 같이 출력한다.

자원 획득
did you call me?
did you call me?
자원 해제

mB1, mB2가 동일한 객체를 소유하고 있다 보니, 함수 종료 후 소멸자 호출도 하나의 객체에서만 이루어짐으로써 '자원 해제'라는 메시지가 한번만 나오는 것을 확인할 수 있다.

shared_ptr를 쓸 때 유의할 점

공유 포인터의 특징은 앞에서 말했듯 한 객체에 여러 공유 포인터가 접근할 수 있다는 점이다.

ClassA* a = new ClassA();
shared_ptr<A> pA1(a);
shared_ptr<A> pA2(a);

이렇게 선언을 하면 pA1, pA2 둘 다 a를 바라보게 된다. 각 공유 포인터 pA1, pA2는 어떤 다른 공유 포인터가 같은 객체를 공유하고 있는지를 알 수 없다. 다시 말해서 각 공유 포인터는 각자의 고유 레퍼런스 카운트를 갖고 있게 된다. 만일 pA1의 레퍼런스 카운트가 0이 된다면 프로그램에서는 pA1이 가리키고 있는 객체 a를 소멸시켜 버릴텐데, 그러면 pA2가 아직 레퍼런스 카운트가 0이 아닌 상태인데, 여기서 a에 대한 접근을 시도하게 될 경우 참조 에러가 발생한다.

[예제 2] 두 공유 포인터

#include <iostream>
#include <memory>

using namespace std;

class Blablabla {

    public:
        Blablabla() {
            cout << "자원 획득" << endl;
        }

        void call() {
            cout << "did you call me?" << endl;
        }

        ~Blablabla() {
            cout << "자원 해제" << endl;
        }
};

int main() {
    Blablabla *b = new Blablabla();
    shared_ptr<Blablabla> mB1(b);
    shared_ptr<Blablabla> mB2(b);

    cout << mB1.use_count() << endl;
    cout << mB2.use_count() << endl;
    return 0;
}

위 코드 자체는 문제없이 컴파일 된다. use_count 함수는 동일하게 참조되는 객체를 참조하는 공유포인터의 수를 리턴한다. [https://en.cppreference.com/w/cpp/memory/shared_ptr/use_count](여기 참조). 다만 실행해 보면 다음과 같이 정상적으로 실행되지 않는 것을 볼 수 있다.

marshall@marshall-IdeaPad-5-15ITL05:~/learn-cpp$ ./a.out 
자원 획득
1
1
자원 해제
자원 해제
free(): double free detected in tcache 2
Aborted (core dumped)

같은 객체에 대해 메모리 해제를 두번 해주려 하다가 문제가 발생했다는 메시지다.

다음 시간에는 weak_ptr 개념에 대한 소개, shared_ptr 포인터 사용시 발생하는 문제점을 해결하는 방법 등에 대한 내용을 싣겠다.

반응형

최초에 주어진 함수는 다음과 같다.

int32_t add2(int32_t x, int32_t y) {
    if(x + y > INT32_MAX) {
        throw std::invalid_argument("Overflow!!");
    } else {
        return (x + y);
    }
}

이에 대한 개선된 함수는 다음과 같다고 한다. 그러나 여전히 문제가 있다.

int32_t add2(int32_t x, int32_t y) {
    if(x + y > INT32_MAX || x + y < INT32_MIN) {
        throw std::invalid_argument("Overflow!!");
    } else {
        return (x + y);
    }
}

위 코드에서 문제가 되는 점

x + y가 int32_t 데이터 타입이 허용하는 범위를 벗어나면 이미 오버플로우가 발생한다. 예를 들어 x가 2,147,483,647, y가 1이면 x + y은 이미 오버플로우가 발생하여 -2,147,483,648이 되어있기 때문에 위의 로직으로는 걸러지지가 않는다.

따라서

  • x, y의 합이 오버플로우가 발생하는 로직을 개선한 함수를 구현하고,
  • 추가적으로 x, y의 차에 대한 함수도 구현하기.

1. 최초에 제출한 코드

#include <iostream>
#include <cstdint>

using namespace std;

int32_t add(int32_t, int32_t);
int32_t sub(int32_t, int32_t);


int main() {
    int32_t x = -2000000000;
    int32_t y = 2000000000;

    try {
        cout << sub(x, y) << endl;
    } catch(invalid_argument& ia) {
        cout << ia.what() << endl;
    }

    return 0;
}

int32_t add(int32_t x, int32_t y) {
    if(x >= 0) {
        if(y > (INT32_MAX - x)) {
            throw invalid_argument("Overflow: 두 수의 합이 허용하는 범위를 넘었습니다.");
        }
    } else {
        if(y < (INT32_MIN - x)) {
            throw invalid_argument("Overflow: 두 수의 합이 허용하는 범위보다 작습니다.");
        }
    }
    return x + y;
}

int32_t sub(int32_t x, int32_t y) {
    if(x >= 0) {
        if(y < (INT32_MAX - x)) {
            throw invalid_argument("Overflow: 두 수의 합이 허용하는 범위를 넘었습니다.");
        }
    } else {
        if(y > (INT32_MIN - x)) {
            throw invalid_argument("Overflow: 두 수의 합이 허용하는 범위보다 작습니다.");
        }
    }
    return x - y;
}

2. 그럼 위의 코드 실행은 정상적으로 동작하는가?

sub2 함수 실행시 문제가 있었음. 기본적으로 다음과 같은 값 갖고 테스트를 해봤을 때

x = 0, y = 2,147,483,646, 기대값 = -2,147,483,646
x = 0, y = 0, 기대값 = 0
x = 2, y = 0, 기대값 = 2

모든 경우에서 발생해서는 안되는 에러가 발생함.

x 값을 입력하세요 (허용범위: -2,147,483,648 ~ 2,147,483,647). 2
y 값을 입력하세요 (허용범위: -2,147,483,648 ~ 2,147,483,647). 0
입력받은 x값: 2
입력받은 y값: 0
x - y = Overflow: 두 수의 합이 허용하는 범위를 넘었습니다.

add2 함수를 구현한 후, 단순히 부호만 뒤집어주면 sub2 함수가 실행이 될 것이라고 생각을 했는데, 이 부분에 대해 아예 잘못 생각하고 테스트도 하지 않고 넘어가서 전혀 엉뚱하게 작동함. 따라서 새로 로직을 다음과 같이 구성함.

(1) 두 변수의 차를 구할 때 오버플로우가 발생할 가능성이 있는 경우

0도 양수로 포함

  1. x가 음수, y가 양수인 경우
  2. x가 양수, y가 음수인 경우

1번 경우에서 x - y를 하는 경우, 음수 오버플로우가 발생하여 연산 값이 양으로 나오게 되고, 2번 경우에서는 양수 오버플로우가 발생하여 음의 경우가 나오게 됩니다. 따라서 이 두 부분을 걸러내야 함.

(2) 위의 코드 자체의 문제점

쓸데없이 if문 안에 if문을 들여서 가독성을 떨어뜨리는 문제

두가지를 고려했을 때 수정한 코드는 다음과 같음.

int32_t sub2(int32_t x, int32_t y) {

    // 두 변수의 차를 계산하여 담을 변수
    int32_t subResult = x - y;

    // x가 음수, y가 양수인 경우에 subResult가 양수인 경우
    if(x < 0 && y > 0 && subResult > 0) {
        throw invalid_argument("negative overflow");
    } 

    // x가 양수, y가 음수인 경우에 subResult가 음수인 경우
    else if(x > 0 && y < 0 && subResult < 0) {
        throw invalid_argument("positive overflow");
    }

    return subResult;
}

한편, add2 함수 또한 가독성 측면에서 문제가 있어서 새로이 로직을 구성하기로 함.

0도 양수로 포함

  1. x가 양수인데 y도 양수인 경우 둘의 합이 음수이면 positive overflow
  2. x가 음수인데 y도 음수인 경우 둘의 합이 양수이면 negative overflow
int32_t add2(int32_t x, int32_t y) {
    // 두 변수의 합을 계산하여 담을 변수
    int32_t addResult = x + y;

    // x 및 y가 양수인데 그 합이 음수인 경우 positive overflow 예외처리
    if(x >= 0 && y >= 0 && addResult < 0) {
        throw invalid_argument("positive overflow");
    }

    // x 및 y가 음수인데 그 합이 양수인 경우 negative overflow 예외처리
    else if(x < 0 && y < 0 && addResult >= 0) {
        throw invalid_argument("negative overflow");
    }

    return addResult;
}

3. 재구성한 코드에 대한 테스트

위에서 재구성한 코드 전체 내용은 다음과 같음.

#include <iostream>
#include <cstdint>

using namespace std;

int32_t add2(int32_t, int32_t);
int32_t sub2(int32_t, int32_t);


int main() {
    // x, y의 입력값은 각각 정상범위 (-2,147,483,648 ~ 2,147,483,647)라고 가정.
    int32_t x;
    int32_t y;

    cout << "x 값을 입력하세요 (허용범위: -2,147,483,648 ~ 2,147,483,647). ";
    cin >> x;

    cout << "y 값을 입력하세요 (허용범위: -2,147,483,648 ~ 2,147,483,647). ";
    cin >> y; 

    try {
        cout << "입력받은 x값: " << x << endl;
        cout << "입력받은 y값: " << y << endl;
         cout << "x + y = " << add2(x, y) << endl;   
        cout << "x - y = " << sub2(x, y) << endl;
    } catch(invalid_argument& ia) {
        cout << ia.what() << endl;
    }

    return 0;
}

int32_t add2(int32_t x, int32_t y) {
    // 두 변수의 합을 계산하여 담을 변수
    int32_t addResult = x + y;

    // x 및 y가 양수인데 그 합이 음수인 경우 positive overflow 예외처리
    if(x >= 0 && y >= 0 && addResult < 0) {
        throw invalid_argument("positive overflow");
    }

    // x 및 y가 음수인데 그 합이 양수인 경우 negative overflow 예외처리
    else if(x < 0 && y < 0 && addResult >= 0) {
        throw invalid_argument("negative overflow");
    }

    return addResult;
}

int32_t sub2(int32_t x, int32_t y) {

    // 두 변수의 차를 계산하여 담을 변수
    int32_t subResult = x - y;

    // x가 음수, y가 양수인 경우에 subResult가 양수인 경우
    if(x < 0 && y >= 0 && subResult > 0) {
        throw invalid_argument("negative overflow");
    } 

    // x가 양수, y가 음수인 경우에 subResult가 음수인 경우
    else if(x >= 0 && y < 0 && subResult < 0) {
        throw invalid_argument("positive overflow");
    }

    return subResult;
}

다음 변수를 입력하여 각각 테스트를 진행한 결과

x y add2 연산 기대값 add2 실제 결과값 sub2 연산 기대값 sub2 실제 결과값
0 0 0 0 0 0
0 1 1 1 -1 -1
1 0 1 1 1 1
1 1 2 2 0 0
2,147,483,647 -2,147,483,648 -1 -1 positive overflow positive overflow
-2,147,483,648 2,147,483,647 -1 -1 negative overflow negative overflow
1 2,147,483,647 positive overflow positive overflow -2,147,483,646 -2,147,483,646
2,147,483,647 1 positive overflow positive overflow 2,147,483,646 2,147,483,616
0 -2,147,483,648 -2,147,483,648 -2,147,483,648 positive overflow positive overflow
-1 -2,147,483,648 negative overflow negative overflow 2,147,483,647 2,147,483,647
2,147,483,647 2,147,483,647 positive overflow positive overflow 0 0
-2,147,483,648 -2,147,483,648 negative overflow negative overflow 0 0

4. 수행 중 배운 점

언더플로우와 오버플로우 개념의 혼동

기존에는 특정 데이터타입의 최대값보다 큰 값이 주어진 것을 오버플로우, 최소값보다 작은 값이 주어진 것을 언더플로우라고 착각하고 있었다.

  • 오버플로우: 특정 데이터타입이 담을 수 있는 범위를 넘어선 값이 주어진 경우
  • 언더플로우: 부동소수점 연산시에 precision 허용 범위보다 더 적은 값을 표현하지 못하는 경우

5. 이어서 수행할 과제

예외처리를 하지 않고 처리하는 방법을 적용하여 코드 개선하기

반응형

다음과 같은 함수가 선언되어 있다.

이 함수 내부의 로직을 보면, 문제점이 하나 보인다.

if (x + y > INT32_MAX) 부분을 보면, 이 부분이 과연 정상적으로 실행될 수 있는 부분인지 먼저 확인해볼 필요가 있다.

예를 들어서 x의 값을 2000000000, y의 값도 마찬가지로 2000000000이라고 해보자. 이 둘을 합하면 어떤 결과가 나올지 다음 코드를 통해 확인해 보자.

#include <iostream>
#include <cstdint>

using namespace std;

int main() {
    int32_t x = 2000000000;
    int32_t y = 2000000000;

    cout << x + y << endl;
    return 0;
}

실행 결과:
-294967296

20억과 20억을 더했는데 전혀 엉뚱한 결과가 나왔다. 왜일까?

int32_t 데이터 타입에서 제공하는 값의 범위는 -2,147,483,648 ~ 2,147,483,647 이다. 여기서 최대값 + 1을 하게 되면 int32_t 타입의 변수 입장에서는 담을 수 있는 범위의 최대치를 넘겨버리게 된다. 이를 정수 오버플로우 (integer overflow)라고 한다.

'끝과 끝은 맞닿아 있다'라는 말처럼, 데이터 타입의 최소값과 최대값은 서로 맞닿아 있다. 그렇기 때문에 오버플로우가 발생한 경우 컴파일러는 증가분에 대한 값을 INT32_MAX의 다음 값인 최소 값으로 넘긴다. 따라서 -2,147,483,648을 리턴하게 된다.

만일 최대값 + 2를 하게 되면 위의 논리에 따라 -2,147,483,647을 리턴하게 된다.

이러한 문제는 역으로 최소값에 1을 빼려고 시도하는 경우에도 마찬가지로 나타난다.

#include <iostream>
#include <cstdint>

using namespace std;

int main() {
    int32_t x = INT32_MIN;
    int32_t y = -1;

    cout << x + y << endl;
    return 0;
}

위의 코드에서 출력되는 x + y의 값은 2,147,483,647가 된다. 최소값인 -2,147,483,648에서 1을 더 빼서 맞닿아 있는 최대값을 리턴하기 때문이다.

주의

이번 문제에 대해 찾아보던 중, 용어를 헷갈려 해서 다음과 같이 정리해 보고자 한다. 난 최대값의 범위를 넘어가서 계산하는 것을 오버플로우, 최소값보다 작은 값을 계산하는 것을 언더플로우라고 생각하고 있었는데 그동안에 용어에 대한 개념을 완전 잘못 알고 있었던 것이다. 두 용어를 다시 한번 더 정리하자면 다음과 같다.

  • 오버플로우: 특정 데이터 타입의 값 범위 밖의 연산이 일어나는 경우 (그게 최소값이던 최대값이던간에 관계없음)
  • 언더플로우: 나무위키에서 정리한 내용이 있어서 다음과 같이 인용해 본다 (https://namu.wiki/w/%EC%96%B8%EB%8D%94%ED%94%8C%EB%A1%9C)
    부동소수점 언더플로(floating point underflow) 또는 산술 언더플로(arithmetic underflow)는 컴퓨터에서 부동소수점으로 표현된 수에서, 지수(E)가 최솟값보다 낮아지는 상황을 뜻한다. 예를 들어 IEEE 754의 단정밀도 실수(single-precision)에서 표현가능한 지수의 값은 −126 ~ +127 이다. 그런데, 2-100 * 2-100 을 계산하면 2-200 이 되어야 하는데, 단정밀도에서는 지수의 값을 -126까지만 처리할 수 있으므로 범위를 벗어나는 값이 된다.

즉 언더플로우는 부동소수점의 정밀도와 관련된 개념으로, 표현이 가능한 지수 범위를 초과한 경우를 일컫는 말이다.

그렇다면 위의 함수를 개선하여 올바른 코드를 짜는 방법은?

그건 다음 시간에 이어서 올리겠다.

반응형

환경: macOS Sierra


C++에서 제공하는 int형 변수 혹은 unsigned long long int 등의 최대값을 넘어서는 매우 큰 숫자를 활용해야 할 경우가 개발하는 도중에 있을지도 모르겠다.


그런 경우에 활용 가능한 라이브러리가 바로 GMP 라이브러리이다. GMP는 'the GNU Multiple Precision Arithmetic Library'를 말하며 이름에서 알 수 있듯 GNU 프로젝트에서 만든 라이브러리이다.


이 라이브러리를 활용하기 위해서는 우선 현재 사용하고 있는 컴퓨터에 gmp 라이브러리가 설치되어 있는지를 확인해야 한다. 맥 환경 기준으로 gmp 라이브러리를 설치하기 위해서는 다음과 같은 명령어를 터미널에서 실행하여 설치한다.


$brew install gmp


다음은 gmp 라이브러리를 활용한 test.cpp라는 이름의 예제이다. 


#include <iostream>
#include <gmpxx.h>
using namespace std;
mpz_class getFactorial(int);
int main(int argc, char **argv) {
int num = 100;
mpz_class factorial = getFactorial(num);
cout << "Factorial of " << num << ": " << factorial << endl;
return 0;
}
mpz_class getFactorial(int num) {
int i;
mpz_class factorial = 1;
for(i=1; i<=num; i++) {
factorial *= i;
}
return factorial;
}

위의 코드를 gcc를 통해 컴파일 할 경우, 커맨드 내에 별도의 플래그가 필요하다.


$gcc test.cpp -lstdc++ -lgmpxx -lgmp -o test


성공적으로 컴파일 되었을 경우 test라는 이름의 실행 파일이 만들어진다. 이를 실행시켜 보면 그 결과를 알 수 있을 것이다.

반응형

환경: 리눅스 / 맥 OS X


방법: 터미널에서 다음 두가지 명령어 중 하나를 실행하면 된다.


testing.cpp라는 파일이 있을 때


1. $gcc testing.cpp -lstdc++

이 명령어를 실행하면 a.out이라는 파일이 생성된다. 생성된 파일에 대해 ./a,out 명령어를 실행하면 된다.


2. $gcc -o testing testing.cpp -lstdc++

이 명령어를 실행하면 a.out 파일 대신에 'testing'이라는 실행 파일이 컴파일 되어 생성된다. 여기서 testing이라는 이름은 임의로 지은 것이므로 원하는 실행파일 이름을 입력하면 된다. 생성된 파일에 대해 ./testing 명령어를 실행하면 된다.

반응형

+ Recent posts