C++에서 문자열의 일부를 리턴하는 방법 문의

parkon의 이미지

안녕하세요,

문자열을 리턴 할 때마다 자신도 없고 골치도 아프군요.
예를 들어 80개의 char를 가지는 문자열  char fStrData[80+1]
global 변수로 지정되어 있다고 할때요,
이 문자열의 처음 10자와 20칸부터 두자를 합한 문자열을 리턴하는 함수를 만드려고 합니다.

const char* foo() {
   char* s1= new char[12+1];
   strncpy(s1, fStrData, 10);
   strncat(s1, fStrData+20, 2);
   return s1;
}

이런식으로 하면 잘 돌아가긴 할 것 같은데
new를 쓰니 속도면에서도 불리해 보이고, 또 반한된 s1을 나중에 free해 줘야 하는 부담때문에
이렇게는 쓰고 싶지 않습니다.

const char* foo() {
   char s2[12+1];
   strncpy(s2, fStrData, 10);
   strncat(s2, fStrData+20, 2);
   return s2;
}

이런 식으로 하니 컴파일러나 환경에 따라 잘 되기도 하고 쓰레기 값이 나오기도
하고 그러더군요.
아마 s2가 휘발성이 있는 stack 변수여서 그렇겠지요,

저는 CERN의 ROOT라는 툴의 TString을 사용하는 데 아마 MSDN의 CString하고
문법이 거의 같을 거예요.
그럼

const char* foo() {
   TString s(fStrData, 10);
   s.Append(fStrData+20, 2);
   return s.Data();
}

이런 식으로 해도 문제가 발생하려나요 ?

문자열을 반환하는 좋은 해답 알려주시면 감사하겠습니다.

twinwings의 이미지

1. new 에는 delete(free 아니여요)

2. 지역변수의 주소는 절대로 리턴하면 안됩니다.

3. 저라면 전형적인 C 함수 인터페이스를 사용할 것 같네요.

int foo(char* str1, char *str2, char *buf, int buf_len);
parkon의 이미지

답변 감사합니다.
특히 전형적인 c함수 인터페이스가 안전하긴 하겠군요.

jick의 이미지

1. Profile을 떠보기 전에 "이 부분이 느릴 것 같아요" 하는 것은 삽질로 이어질 확률이 대단히 높습니다. 다시 말해, 프로그램을 다 짠 다음 프로파일러를 돌려서 실제로 new/delete에서 시간을 많이 소모하는 걸 확인하기 전까지는 거기서 시간이 많이 걸릴지 걱정할 필요가 없습니다. 그때 가서 걱정하세요.

new/delete는 생각하는 것보다 빠릅니다. 제 컴퓨터에서 테스트해 보니 초당 500만개쯤 쉽게 만들었다 지웠다 하네요.

2. C++을 쓰신다면 특별한 사정이 없는 한 std::string을 사용하시는 걸 추천합니다.

parkon의 이미지

500만개도 금방 되는군요, 답변 감사합니다.

HDNua의 이미지

1. 꼭 TString이라는 것을 사용하지 않고 두 번째 예제처럼 작성해도 된다면,
정적 변수를 이용하는 것도 괜찮은 답이라고 생각합니다.

const char* foo() {
	static char s2[12 + 1];
	strncpy(s2, fStrData, 10);
	strncat(s2, fStrData + 20, 2);
	return s2;
}

C++이니 std::string을 이용하는 것도 가능한데 이 경우는 고려하지 않으셨나요?

std::string foo2() {
	std::string s = fStrData;
	return s.substr(0, 10) + s.substr(20, 2);
}

예제 코드입니다.

#include <iostream>
#include <string>
#pragma warning(disable:4996)
 
char fStrData[80 + 1];
const char* foo();
std::string foo2();
 
int main(void) {
	std::cin >> fStrData;
 
	const char *ss1 = foo();
	std::string ss2 = foo2();
	std::cout << ss1 << std::endl;
	std::cout << ss2 << std::endl;
 
	return 0;
}
 
const char* foo() {
	static char s2[12 + 1];
	strncpy(s2, fStrData, 10);
	strncat(s2, fStrData + 20, 2);
	return s2;
}
 
std::string foo2() {
	std::string s = fStrData;
	return s.substr(0, 10) + s.substr(20, 2);
}

저는 이렇게 생각했습니다.

parkon의 이미지

보여주신 foo2의 std::string의 substr을 사용하는 마지막 예는
stack 변수의 휘발성 문제가 없는 건가요 ?
그렇다면 꽤나 매력적인 해법으로 보입니다.

parkon의 이미지

아 그러고 보니 return형을
const char*가 아니라 string으로 해서
그 로컬 변수의 휘발성 문제를 해결하신 것으로 보이는 군요.

HDNua의 이미지

네. C++에서는 스택에 생성한 객체를 반환할 때
반환할 객체를 임시 객체에 복사해서 반환합니다.

그래서 멤버에 포인터 변수가 있는 경우 주의해야 하는데 이 경우와는 관계없는 이야기고,
std::string에 대해서는 휘발성 문제가 없습니다.

저는 이렇게 생각했습니다.

twinwings의 이미지

덧붙이자면 함수 내 정적변수를 이용한 구현은 thread 안전성이 없습니다.
(단, 해당 정적변수가 상수라면 상관없습니다. 너무 당연한건가;)

multi-thread라면 이러한 구현은 지양하시구요.

single thread라면 좋은 해법이 될 듯 합니다.

최근에 bluez 라이브러리 뜯어보고 있었는데, hciconfig이었나? hcitool 이었나

single thread라 위와 비슷한 구현이 눈에 띄더군요.

(보통 지역변수조차 스택에 올리는 시간 아끼려고하는 건지

전역변수 / 지역 정적변수로 대체하더군요.)

parkon의 이미지

답변 감사드립니다.
이리 저리 여러 해법이 있는건 알겠는데 딱 맘에 드는 게 없는게 문제네요.
(이건 현재 제 이해가 짧아서 생기는 현상이겠지만 말이죠)

현재 (ROOT에만 있는 녀석인지 몰라도) Form이라는 함수를 써서 코딩했고,
나중 문제가 생기면 정적 변수로 전환할까 생각하고 있습니다.

klyx의 이미지

멀티 스레드라면 thread_local 변수를 이용할 수 있습니다.

klyx의 이미지

클래스의 객체에 대해서 stack 변수의 휘발성 운운하시는 걸로봐서는 C++을 공부한 적이 없으신 것같습니다.
C++은 생긴게 C같다고해서 C하던 사람이 C++공부안하고 쓸수있는 언어가 아닙니다.
먼저 C++입문서와 활용서를 한두권 정도 정독하시고 도전하시는걸 추천합니다.

twinwings의 이미지

-댓글 위치 이동

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.