atoi(), itoa() 함수 사용법

|

문자열 변환 함수

atoi()itoa() 함수는 문자형과 정수형간 데이터를 변한하는 함수입니다. 두 함수는 stdlib.h에 선언되어 있습니다.


atoi()

atoi() 함수는 문자형을 정수형으로 변환합니다. 예를 들어 다음과 같은 코드 형태로 사용할 수 있습니다.

int a = atoi("123");


itoa()

itoa()는 반대로 숫자를 문자형으로 변환하는 함수입니다. 다만 itoa()는 표준 함수는 아닙니다. 플랫폼에 따라 쓸 수 없기도 합니다. 하지만 숫자를 문자로 바꾸는 것은 대부분의 언어에서는 아주 쉬운 일입니다. C++에서는 sprintf() 함수 등을 이용해서 쉽게 변경할 수 있습니다.


그 외

그 외에도 문자열을 double 형태나 long, long long 형태로 바꿔주는 함수들도 존재합니다.

double atof(const char* str);
long atol(const char* str);
long long atoll(const char* str);

연산자 오버로딩

|

연산자 오버로딩

연산자 오버로딩은 사용자가 직접 만든 클래스에 대해서도 이미 알려져 있는 연산자들(ex. +, -, *, / 등)을 쉽게 사용할 수 있게 해주는 기능입니다.

연산자 오버로딩은 클래스 작성자를 위한 기능은 아니며, 클래스를 사용하는 사람을 좀 더 편리하게 사용할 수 있도록 해주는 기능입니다.


연산자 오버로딩의 제약 사항

연산자 오버로딩은 다음과 같은 상황에서는 사용할 수 없습니다.

  • 새로운 연산자 기호를 적용할 수 없음.
  • 오버로딩할 수 없는 연산자도 있음. 대표적으로 ::, sizeof, 삼항 연산자 ?: 등은 오버로딩 못함.
  • 연산자의 인자의 개수는 변경 불가능. 예를 들어 ++는 한 개의 인자를 갖고, +는 두 개의 인자를 가짐.
  • 연산자의 우선 순위와 결합 규칙은 변경 안됨.
  • 내장 타입(기존에 이미 정의되어 있는 데이터 타입들. 예를 들어 int 등)에 대한 연산자 재정의 불가능.


연산자 오버로딩 구현 예제

단항 뺄셈, 덧셈 연산자 오버로딩

단항 뺄셈, 덧셈은 다음과 같은 연산입니다.

Point a(2, 3);
Point x = -a;
Point y = -(-a);

이 경우 단항 덧셈은 아무런 변화가 없는 연산이기 때문에 굳이 구현할 필요는 없고, 단항 뺄셈의 경우에만 구현을 해주면 됩니다.

class Point {
 public:
  Point(int _x, int _y) : x(_x), y(_y) {}

  // 단항 뺄셈
  const Point operator-() const;


  int getX() { return x; }
  int getY() { return y; }

 private:
  int x, y;
};

const Point Point::operator-() const {
  Point newPoint(*this);
  newPoint.x = -x;
  newPoint.y = -y;

  return newPoint;
}


증가, 감소 연산자의 오버로딩

증가, 감소 연산자는 다음과 같습니다.

Point a(2, 3);
a++;
++a;
a--;
--a;

++--의 위치에 따라 연산자 오버로딩이 다르게 적용됩니다. ++prefix로 붙는 경우에는 인자가 없고 postfix로 붙는 경우에는 int 타입의 더미(Dummy) 인자가 주어집니다. 그래서 증가, 감소 연산자 오버로딩을 지원하기 위해서는 함수 4개를 구현해주어야 합니다.

class Point {
 public:
  Point(int _x, int _y) : x(_x), y(_y) {}

  // 단항 뺄셈
  const Point operator-() const;

  // 증가, 감소 연산자
  Point &operator++();
  Point operator++(int);
  Point &operator--();
  Point operator--(int);

  int getX() { return x; }
  int getY() { return y; }

 private:
  int x, y;
};

Point &Point::operator++() {
  x += 1;
  y += 1;

  return *this;
}

Point Point::operator++(int) {
  Point newPoint(*this);
  x += 1;
  y += 1;

  return newPoint;
}

Point &Point::operator--() {
  x -= 1;
  y -= 1;

  return *this;
}

Point Point::operator--(int) {
  Point newPoint(*this);
  x -= 1;
  y -= 1;

  return newPoint;
}


사칙 연산자의 오버로딩

아무래도 가장 많이 사용하게 될 오버로딩일 것 같은데, +, -, *, /의 사칙 연산에 대한 오버로딩입니다.

Point a(2, 3);
Point b(5, 4);
Point c = a + b;

아래의 예제에서는 +에 대한 오버로딩만 구현했습니다.

class Point {
 public:
  Point(int _x, int _y) : x(_x), y(_y) {}

  // 단항 뺄셈
  const Point operator-() const;

  // 증가, 감소 연산자
  Point &operator++();
  Point operator++(int);
  Point &operator--();
  Point operator--(int);

  // 사칙 연산자
  Point operator+(Point &point);

  int getX() { return x; }
  int getY() { return y; }

 private:
  int x, y;
};

Point Point::operator+(Point& point) {
  Point newPoint(*this);

  newPoint.x += point.x;
  newPoint.y += point.y;

  return newPoint;
}


비트, 논리 연산자의 오버로딩

비트 연산자와 논리 연산자의 경우는 오버로딩하는 경우가 드물며, 특히 논리 연산자인 &&, ||의 오버로딩은 권장하지 않고 있습니다. 논리 연산자를 오버로딩할 경우 if 문과 같은 조건문 등에서 의도치 않은 결과가 발생하기도 합니다.


변환 연산자의 오버로딩

변환 연산자는 다음과 같이 데이터 타입의 변환을 의미합니다.

Point a(2, 3);
int intPoint = a;
string strPoint = a;
#include <string>

using namespace std;

class Point {
 public:
  Point(int _x, int _y) : x(_x), y(_y) {}

  // 단항 뺄셈
  const Point operator-() const;

  // 증가, 감소 연산자
  Point &operator++();
  Point operator++(int);
  Point &operator--();
  Point operator--(int);

  // 사칙 연산자
  Point operator+(Point &point);

  // 변환 연산자
  operator int() const;
  operator string() const;

  int getX() { return x; }
  int getY() { return y; }

 private:
  int x, y;
};

Point::operator int() const {
  return x + y;
}

Point::operator string() const {
  return "(" + to_string(x) + ", " + to_string(y) + ")";
}

Segmentation Fault 오류시 CallStack 확인 방법

|

Segmentation Fault

‘Segmentation Fault’ 오류는 잘못된 메모리 영역을 참조할 때 발생하는 오류입니다. 이 때 단순히 ‘Segmentation Fault’ 오류 메세지만 출력하기 때문에 어느 부분에서 오류가 발생했는지 디버그하기가 까다롭습니다. 물론, 디버깅 툴을 통해서 callstack을 보면서 디버그를 할 수 있으면 좋겠지만, 크로스 컴파일 환경에서는 디버깅이 쉽지 않은 경우가 대부분입니다.

다음 예제 코드는 라즈베리파이에서 돌아가는 Segmentation Fault시 Callstack을 츨력하는 코드입니다. ARM 계열 CPU를 타켓으로 했으며, x86이나 다른 계열 CPU에서는 코드에서

caller_address = (void *) uc->uc_mcontext.arm_pc;  // RIP: x86_64 specific     arm_pc: ARM

부분을 수정하면 됩니다.


예제 코드

#include <stdio.h>
#include <execinfo.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <cxxabi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <execinfo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <unistd.h>

using namespace std;

typedef struct _sig_ucontext {
  unsigned long uc_flags;
  struct ucontext *uc_link;
  stack_t uc_stack;
  struct sigcontext uc_mcontext;
  sigset_t uc_sigmask;
} sig_ucontext_t;

void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext) {
  void * array[50];
  void * caller_address;
  char ** messages;
  int size, i;
  sig_ucontext_t * uc;

  uc = (sig_ucontext_t *) ucontext;

  /* Get the address at the time the signal was raised */
  caller_address = (void *) uc->uc_mcontext.arm_pc;  // RIP: x86_64 specific     arm_pc: ARM

  fprintf(stderr, "\n");

  if (sig_num == SIGSEGV)
    printf("signal %d (%s), address is %p from %p\n", sig_num, strsignal(sig_num), info->si_addr,
           (void *) caller_address);
  else
    printf("signal %d (%s)\n", sig_num, strsignal(sig_num));

  size = backtrace(array, 50);
  /* overwrite sigaction with caller's address */
  array[1] = caller_address;
  messages = backtrace_symbols(array, size);

  /* skip first stack frame (points here) */
  for (i = 1; i < size && messages != NULL; ++i) {
    printf("[bt]: (%d) %s\n", i, messages[i]);
  }

  free(messages);

  exit(EXIT_FAILURE);
}

void installSignal(int __sig) {
  struct sigaction sigact;
  sigact.sa_sigaction = crit_err_hdlr;
  sigact.sa_flags = SA_RESTART | SA_SIGINFO;
  if (sigaction(__sig, &sigact, (struct sigaction *) NULL) != 0) {
    fprintf(stderr, "error setting signal handler for %d (%s)\n", __sig, strsignal(__sig));
    exit(EXIT_FAILURE);
  }
}

int crash() {
  char *s = "hello world";
  *s = 'H';

  printf("Crash~!!");
  return 0;
}

int foo4() {
  crash();
  return 0;
}

int foo3() {
  foo4();
  return 0;
}

int foo2() {
  foo3();
  return 0;
}

int foo1() {
  foo2();
  return 0;
}

int main(int argc, char *argv[]) {

  // For crashes, SIGSEV should be enough.
  installSignal(SIGSEGV);

  // But, you can install handlers for other signals as well:
  //installSignal(SIGBUS);
  //installSignal(SIGFPE);
  //installSignal(SIGILL);
  //installSignal(SIGINT);
  //installSignal(SIGPIPE);
  //installSignal(SIGQUIT);
  //installSignal(SIGTERM);
  //installSignal(SIGTSTP); # This is Ctrl+Z, we need to ignore it. This is to send the process to the background (suspend).
  //installSignal(SIGTTIN);
  //installSignal(SIGTTOU);
  //installSignal(SIGSYS);
  //installSignal(SIGXCPU);
  //installSignal(SIGXFSZ);

  foo1();

  return 0;
}


빌드시 옵션

빌드시 옵션으로 -g 옵션을 줘야 합니다. Eclipe에서 Debug 모드로 빌드할 경우 기본적으로 -g3 옵션을 주기 때문에 크게 신경쓸 필요가 없습니다.


실행 결과

실행 결과는 다음과 같습니다.

$ ./SegmentationFaultCapture

signal 11 (Segmentation fault), address is 0x10a64 from 0x108c8
[bt]: (1) ./SegmentationFaultCapture() [0x108c8]
[bt]: (2) ./SegmentationFaultCapture() [0x108c8]
[bt]: (3) ./SegmentationFaultCapture() [0x108f8]
[bt]: (4) ./SegmentationFaultCapture() [0x10910]
[bt]: (5) ./SegmentationFaultCapture() [0x10928]
[bt]: (6) ./SegmentationFaultCapture() [0x10940]
[bt]: (7) ./SegmentationFaultCapture() [0x1096c]
[bt]: (8) /lib/arm-linux-gnueabihf/libc.so.6(__libc_start_main+0x114) [0xb6c5f294]


addr2line으로 오류 위치 확인

위 결과창만으로는 아직 정확한 오류 위치를 알 수 없기 때문에 addr2line 명령어를 이용해서 결과를 조회합니다.

$ addr2line -f -e ./SegmentationFaultCapture 0x108c8
_Z5crashv
D:\Workspace\RobotLitoService\SegmentationFaultCapture\Debug/../Main.cpp:73

Ubuntu에 JDK 설치하는 방법

|

Ubuntu에 JDK 설치하는 방법

오라클 공식 사이트에서 다운받아 설치

오라클 공식 배포 사이트에서 JDK 설치 파일을 다운받아 설치하는 방법이 있습니다. 가장 확실한 방법 중의 하나입니다.

다만, 여기에서 내려 받는 파일은 확장자가 rpm으로 되어 있습니다. 이 경우, deb 확장자로 변경해주어야 설치를 할 수 있습니다.

rpm 파일을 deb 파일로 변경하고 나면 다음 명령어를 이용해서 JDK를 설치할 수 있습니다.

$ sudo dpkg -i jdk1.8.0-144_1.8.014401_amd64.deb


확장자 변환하지 않고 직접 설치

오라클 공식 사이트에서 tar.gz 형태의 파일을 내려 받은 뒤 직접 설치하는 방법입니다.

먼저 다음 명령어를 이용해서 압축을 해제하고 파일들을 /usr/lib/jvm 폴더 아래로 이동시켜줍니다.

$ tar zxvf jdk-8u144-linux-x64.tar.gz
$ sudo mkdir /usr/lib/jvm
$ sudo mv jdk1.8.0_144 /usr/lib/jvm

그리고 다음 명령어를 통해 명령어 등록을 해 줍니다.

$ sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk1.8.0_144/bin/java 1
$ sudo update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk1.8.0_144/bin/javac 1
$ sudo update-alternatives --install /usr/bin/javaws javaws /usr/lib/jvm/jdk1.8.0_144/bin/javaws 1
$ sudo update-alternatives --config java
$ sudo update-alternatives --config javac
$ sudo update-alternatives --config javaws


PPA를 이용한 방법

PPA(Personal Package Archive)에서 JDK를 내려 받아 설치하는 방법입니다. apt-get 명령어를 이용한 설치라서 간편합니다.

$ sudo apt-add-repository ppa:webupd8team/java
$ sudo apt-get update
$ sudo apt-get install oracle-java8-installer

장소 자동 완성 예제

|

장소 검색 텍스트 박스를 자동으로 완성하는 방법입니다. 구글에서 해당 기능을 쉽게 사용할 수 있도록 컴포넌트를 제공하고 있습니다. Place Picker와 마찬가지로 ‘Google Places API for Android’ 라이브러리에대한 Google API Key 등록을 해야 합니다.

장소 자동 완성은 크게 Fragment를 사용하는 방법과 Intent 호출을 이용하는 방법의 두 가지가 있습니다.


Fragment 사용하는 코드

public class PlaceAutocompleteActivity extends AppCompatActivity {

  static final String TAG = "PlaceAutocomplete";
  PlaceAutocompleteFragment autocompleteFragment;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_place_autocomplete);

    autocompleteFragment = (PlaceAutocompleteFragment)
        getFragmentManager().findFragmentById(R.id.place_autocomplete_fragment);

    autocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() {
      @Override
      public void onPlaceSelected(Place place) {
        Log.i(TAG, "Place: " + place.getName());
      }

      @Override
      public void onError(Status status) {
        Log.i(TAG, "An error occurred: " + status);
      }
    });
  }
}


Intent를 이용하는 방법

public class PlaceAutocompleteActivity extends AppCompatActivity {

  int PLACE_AUTOCOMPLETE_REQUEST_CODE = 1;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_place_autocomplete);

    findViewById(R.id.btn_autocomplete).setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        try {
          Intent intent = new PlaceAutocomplete.IntentBuilder(PlaceAutocomplete.MODE_FULLSCREEN)
              .build(PlaceAutocompleteActivity.this);
          startActivityForResult(intent, PLACE_AUTOCOMPLETE_REQUEST_CODE);
        } catch (GooglePlayServicesRepairableException e) {
          // TODO: Handle the error.
        } catch (GooglePlayServicesNotAvailableException e) {
          // TODO: Handle the error.
        }
      }
    });
  }

  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == PLACE_AUTOCOMPLETE_REQUEST_CODE) {
      if (resultCode == RESULT_OK) {
        Place place = PlacePicker.getPlace(data, this);
        String toastMsg = String.format("Place: %s", place.getName());
        Toast.makeText(this, toastMsg, Toast.LENGTH_LONG).show();
      }
    }
  }

}