함수 서명은 다음과 같아야합니다.
const char * myFunction()
{
return "My String";
}
배경:
이것은 C & C ++에있어서 매우 기본적이지만 조금 더 논의가 필요합니다.
C (& C ++)에서 문자열은 0 바이트로 끝나는 바이트 배열 일뿐입니다. 따라서 "문자열 0"이라는 용어는이 특정 문자열의 특징을 나타내는 데 사용됩니다. 다른 종류의 문자열이 있지만 C (& C ++)에서이 풍미는 본질적으로 언어 자체에 의해 이해됩니다. 다른 언어 (Java, Pascal 등)는 "my string"을 이해하기 위해 다른 방법론을 사용합니다.
Windows API (C ++에 있음)를 사용하는 경우 "LPCSTR lpszName"과 같은 매우 정기적으로 함수 매개 변수가 표시됩니다. 'sz'부분은 '문자열 0'이라는 개념을 나타냅니다. 즉, 널 (/ 0) 종결자가있는 바이트 배열입니다.
설명:
이 '인트로'를 위해 '바이트'와 '문자'라는 단어를 같은 의미로 사용합니다. 이렇게 배우는 것이 더 쉽기 때문입니다. 국제 문자를 처리하는 데 사용되는 다른 방법 (와이드 문자 및 다중 바이트 문자 시스템 ( mbcs ))이 있다는 점에 유의하십시오 . UTF-8 은 mbcs의 예입니다. 소개를 위해이 모든 것을 조용히 '건너 뛰어'.
기억:
이것은 "my string"과 같은 문자열이 실제로 9 + 1 (= 10!) 바이트를 사용함을 의미합니다. 이것은 마지막으로 문자열을 동적으로 할당 할 때를 아는 것이 중요합니다.
따라서이 '종료 0'이 없으면 문자열이 없습니다. 메모리에는 문자 배열 (버퍼라고도 함)이 있습니다.
데이터 수명 :
이 방법으로 함수 사용 :
const char * myFunction()
{
return "My String";
}
int main()
{
const char* szSomeString = myFunction(); // Fraught with problems
printf("%s", szSomeString);
}
... 일반적으로 임의의 처리되지 않은 예외 / 세그먼트 오류 등, 특히 '길 아래'로 착지합니다.
요컨대, 내 대답은 맞지만-10 점 만점에 9 번은 그런 식으로 사용하면 충돌하는 프로그램으로 끝날 것입니다. 특히 그렇게하는 것이 '좋은 습관'이라고 생각하는 경우에 그렇습니다. 요약하자면 일반적으로 그렇지 않습니다.
예를 들어, 미래의 언젠가를 상상해보십시오. 이제 문자열을 어떤 방식 으로든 조작해야합니다. 일반적으로 코더는 '쉬운 길을 택'하고 다음과 같은 코드를 작성합니다.
const char * myFunction(const char* name)
{
char szBuffer[255];
snprintf(szBuffer, sizeof(szBuffer), "Hi %s", name);
return szBuffer;
}
즉, 컴파일러 szBuffer가 printf()in main()이 호출 될 때까지 사용 된 메모리를 해제했기 때문에 프로그램이 중단됩니다 . (컴파일러는 이러한 문제에 대해 미리 경고해야합니다.)
너무 쉽게 바 프지 않는 문자열을 반환하는 두 가지 방법이 있습니다.
- 잠시 동안 살아있는 버퍼 (정적 또는 동적 할당)를 반환합니다. C ++에서 '도우미 클래스'(예
std::string:)를 사용하여 데이터의 수명을 처리합니다 (함수의 반환 값을 변경해야 함).
- 정보로 채워지는 함수에 버퍼를 전달합니다.
C에서 포인터를 사용하지 않고 문자열을 사용하는 것은 불가능하다는 점에 유의하십시오. 내가 보여준 것처럼 그것들은 동의어입니다. 템플릿 클래스가있는 C ++에서도 백그라운드에서 사용되는 버퍼 (즉, 포인터)가 항상 있습니다.
따라서 (현재 수정 된 질문)에 더 잘 대답하십시오. (제공 할 수있는 다양한 '기타 답변'이있을 것입니다.)
더 안전한 답변 :
예 1, 정적으로 할당 된 문자열 사용 :
const char* calculateMonth(int month)
{
static char* months[] = {"Jan", "Feb", "Mar" .... };
static char badFood[] = "Unknown";
if (month<1 || month>12)
return badFood; // Choose whatever is appropriate for bad input. Crashing is never appropriate however.
else
return months[month-1];
}
int main()
{
printf("%s", calculateMonth(2)); // Prints "Feb"
}
여기서 '정적'이하는 일은 (많은 프로그래머가 이러한 '할당'유형을 좋아하지 않음) 문자열이 프로그램의 데이터 세그먼트에 들어가는 것입니다. 즉, 영구적으로 할당됩니다.
C ++로 이동하면 비슷한 전략을 사용하게됩니다.
class Foo
{
char _someData[12];
public:
const char* someFunction() const
{ // The final 'const' is to let the compiler know that nothing is changed in the class when this function is called.
return _someData;
}
}
...하지만 std::string다른 사람과 공유 할 라이브러리의 일부가 아닌 자신의 용도로 코드를 작성하는 경우 와 같은 도우미 클래스를 사용하는 것이 더 쉬울 것입니다 .
예제 2, 호출자 정의 버퍼 사용 :
이것은 문자열을 전달하는 더 '완전한'방법입니다. 반환 된 데이터는 발신자에 의해 조작되지 않습니다. 즉, 예제 1은 발신자에 의해 쉽게 악용 될 수 있으며 응용 프로그램 오류에 노출 될 수 있습니다. 이렇게하면 훨씬 더 안전합니다 (더 많은 코드 줄을 사용하지만).
void calculateMonth(int month, char* pszMonth, int buffersize)
{
const char* months[] = {"Jan", "Feb", "Mar" .... }; // Allocated dynamically during the function call. (Can be inefficient with a bad compiler)
if (!pszMonth || buffersize<1)
return; // Bad input. Let junk deal with junk data.
if (month<1 || month>12)
{
*pszMonth = '\0'; // Return an 'empty' string
// OR: strncpy(pszMonth, "Bad Month", buffersize-1);
}
else
{
strncpy(pszMonth, months[month-1], buffersize-1);
}
pszMonth[buffersize-1] = '\0'; // Ensure a valid terminating zero! Many people forget this!
}
int main()
{
char month[16]; // 16 bytes allocated here on the stack.
calculateMonth(3, month, sizeof(month));
printf("%s", month); // Prints "Mar"
}
두 번째 방법이 더 나은 이유는 여러 가지가 있습니다. 특히 다른 사람이 사용할 라이브러리를 작성하는 경우 (특정 할당 / 할당 해제 체계에 고정 할 필요가 없으며, 타사가 코드를 손상시킬 수 없습니다. 특정 메모리 관리 라이브러리에 연결할 필요는 없지만 모든 코드와 마찬가지로 가장 좋아하는 부분은 사용자에게 달려 있습니다. 이런 이유로 대부분의 사람들은 예 1을 선택하여 너무 많이 태워서 더 이상 그렇게 작성하는 것을 거부합니다.)
부인 성명:
나는 몇 년 전에 은퇴했고 내 C는 이제 약간 녹슬 었습니다. 이 데모 코드는 모두 C로 올바르게 컴파일되어야합니다 (모든 C ++ 컴파일러에서는 괜찮습니다).