関数のシグネチャは次のようにする必要があります。
const char * myFunction()
{
return "My String";
}
バックグラウンド:
これはCとC ++にとって非常に基本的なことですが、それ以上の議論は必要です。
C(およびC ++)では、文字列は、ゼロバイトで終了する単なるバイトの配列です。したがって、「string-zero」という用語は、この特定の文字列のフレーバーを表すために使用されます。他の種類の文字列がありますが、C(およびC ++)では、このフレーバーは言語自体によって本質的に理解されます。他の言語(Java、Pascalなど)は、「私の文字列」を理解するためにさまざまな方法論を使用しています。
Windows API(C ++に含まれています)を使用したことがある場合、「LPCSTR lpszName」のような非常に定期的に機能するパラメーターが表示されます。「sz」の部分は、「string-zero」のこの概念を表します。ヌル(/ zero)ターミネーターを持つバイトの配列です。
明確化:
この「イントロ」のために、「バイト」と「文字」という言葉を交換可能に使用します。この方法を学ぶ方が簡単だからです。国際文字に対処するために使用される他の方法(ワイド文字、マルチバイト文字システム(mbcs))があることに注意してください。UTF-8はmbcsの例です。イントロのために、私はこれらすべてを静かに「スキップ」します。
メモリ:
つまり、「my string」のような文字列は実際には9 + 1(= 10!)バイトを使用します。これは、最終的に文字列を動的に割り当てるときを知っておくことが重要です。
したがって、この「終了ゼロ」がなければ、文字列はありません。メモリ内にぶら下がっている文字の配列(バッファとも呼ばれます)があります。
データの寿命:
この方法での関数の使用:
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()
が呼び出されるまでに使用されていたメモリーを解放している場合とそうでない場合があるため、プログラムがクラッシュします。(コンパイラーもこのような問題を事前に警告する必要があります。)
文字列を返す方法は2つあります。
- しばらく存続する(静的または動的に割り当てられた)バッファを返します。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"
}
特に他の人が使用するライブラリを作成している場合は、2番目の方法が優れている理由がたくさんあります(特定の割り当て/割り当て解除スキームにロックする必要がないため、サードパーティがコードを壊すことはできません。また、特定のメモリ管理ライブラリにリンクする必要はありません)が、すべてのコードと同様に、何が一番好きかはあなた次第です。そのため、ほとんどの人は、何度も焼き付けられるまで、たとえば1を選択して、それ以上書くことを拒否します;)
免責事項:
私は数年前に引退しました、そして私のCは今少し錆びています。このデモコードはすべてCで正しくコンパイルされます(ただし、どのC ++コンパイラでも問題ありません)。