[C/C++] print를 하거나 문자열을 리턴하는 함수. 어떻게 구성하는게 좋은가? : string을 리턴하자.
보통 "input은 인자, output은 리턴"으로 작성하는 편이 알아보기 쉽기 때문에 함수 인자로 포인터를 넘겨 이 포인터에 쓰는 방식으로 간접적으로 값을 되돌려 주는 것 보다, 명확하게 어떤 값을 리턴하도록 작성하는 편이다.
그러나 C같이 garbage collection이 안되는 언어에서는 어떤 값을 리턴하도록 작성하면 여러가지 이슈를 겪는 경우가 있다.
*** 또한 GC가 안되는 언어에서는 in/output을 명확히 작성한다 해도 반드시 순수 함수라고는 볼 수 없다. 함수 내부에서 ``c malloc()`` 등을 호출하면 side-effect이 생기기 때문.
예를 들면 다음과 같은 상황이다.
malloc() : 문제점 - 바깥에서 free()
```c
char* parse_something(const char* dev) {
char* ret_str = (char*)malloc(PATH_LEN);
snprintf(ret_str, PATH_LEN, "/aaa/%s/ccc", dev);
return ret_str;
}
```
지역 변수로 쓰는건 당연히 안되니, 보통은 두 가지 방법을 생각하게 된다.
1. 안에서 ``c malloc()``하고 이를 리턴하거나,
2. 함수 argument에 ``c char*``를 하나 더 받아서 간접적으로 돌려주거나.
위 코드 같은 경우 1번 케이스에 해당하는데, C는 GC가 안되므로 이렇게 작성하면 상위 함수에서 반드시 ``c free()`` 해주어야 한다는 부담이 있다.
static __thread : 문제점 - Reentrancy
1번 케이스를 ``c static __thread``을 사용해 다음과 같이 바꿀 수 있으나 이 것도 좋은 방법은 아니다.
```c
char* parse_something(const char* dev) {
static __thread char ret_str[PATH_LEN];
snprintf(ret_str, PATH_LEN, "/aaa/%s/ccc", dev);
return ret_str;
}
```
*** ``c __thread``를 붙여주는 이유는, ``c static`` 변수는 전역적으로 1개만 존재하기 때문에 멀티 스레드 환경에서 safe하지 않기 때문이다. lock을 걸기에는 너무 비싼 경우 사용한다.
*** ``c __thread`` 지시어를 붙여주면 해당 변수 또는 객체를 스레드 마다 독립적으로 갖게 된다.
*** 그래서 생성자가 있는 객체에는 붙일 수 없다.
* 주의1 ) ``c static`` 변수의 크기를 지정하는 값 ``c PATH_LEN``은 반드시 ``c #define``이나 `` 15``같은 상수여야 한다.
``c static const PATH_LEN`` 로 선언되어 있는 경우 `` Storage size of ‘ret_str’ isn’t constant`` 에러가 발생한다.
thread-safe하게 만들었고, 바깥에서 ``c free()``해야 하는지 마는지를 알 수 없는 문제도 해결했다.
그러나 ``c static`` 변수 포인터를 반환하기 때문에 Reentrancy 문제가 발생한다.
```c
char* a = parse_something("kkk");
char* b = parse_something("ttt");
printf("%s %s\n", a, b);
>>> /aaa/ttt/ccc /aaa/ttt/ccc
```
이처럼 `` a`` 변수나 `` b``변수나 결국 ``c static`` 변수를 가리키기게 되고, 여기에는 제일 마지막에 호출된 ``c parse_something()`` 함수의 결과만 들어있기 때문에 `` a`` 변수의 값을 사용하기 위해서는 ``c memcpy()``를 써서 값을 옮겨두어야 한다는 번거로움이 존재한다.
이처럼 reentrancy 문제를 가지고 있는 함수들은 ``c ether_aton(), inet_ntoa()`` 등이 있으며,
재진입 문제를 해결한 버전으로 ``c ether_aton_r(), inet_ntop()`` 등이 있으니 이 쪽을 사용하는 편이 좋다.
포인터로 넘기기 : C에서의 해결책.
thread-safety, 바깥에서 ``c free()``해야 하는지 알 수 없는 문제도 해결, reentrancy 문제도 해결.
이 경우 바깥에서 힙에 안잡고 지역변수로 스택에([]) 잡으면 아예 ``c free()``를 하지 않아도 된다. (이 경우 포인터 변경은 불가능하기 때문에 copy 스타일의 작업만 가능하다는 제약이 있긴 하다.)
그래서 이런 경우 포인터로 넘기는 식으로 짜는게 여러 귀찮은 문제를 피할 수 있다.
[C++] string을 return해도 괜찮다.
C++의 경우 스마트 포인터를 사용하거나, STL을 사용하면 해결할 수 있다.
``cpp std::string``을 리턴하면, delete 부담도 해소되고 코드가 깔끔해진다는 장점이 있다.
단점은 `` string``을 리턴하면서 deep copy가 발생해서 느릴 것 같다는 점인데
요즘 컴파일러는 RVO(Return Value Optimization)을 잘 해주기 때문에 알아서 포인터 인자로 받아서 간접적으로 넘기는 것이나 크게 차이 없는 정도로 최적화해준다. 그래서 그냥 리턴해줘도 된다. 이런 부분은 컴파일러가 알아서 해준다고 믿는 것이 좋다.
'Languages & Frameworks > C C++' 카테고리의 다른 글
Qt 로 빌드한 바이너리 배포하기 (0) | 2019.02.22 |
---|---|
Effective Modern C++ (0) | 2019.01.22 |
More Effective C++ (0) | 2019.01.22 |
Effective C++ (0) | 2019.01.22 |
[C++] operator overloading (0) | 2019.01.22 |