関数からC文字列を返す 質問する

関数からC文字列を返す 質問する

関数から C 文字列を返そうとしていますが、うまくいきません。これが私のコードです。

char myFunction()
{
    return "My String";
}

私はmainこれを次のように呼んでいます:

int main()
{
  printf("%s", myFunction());
}

他の方法もいくつか試しましたmyFunctionが、うまくいきませんでした。例:

char myFunction()
{
  char array[] = "my string";
  return array;
}

注意: ポインターの使用は許可されていません。

この問題の背景を少し説明します。

何月かを調べる関数があります。たとえば、1 の場合は 1 月を返します。

したがって、印刷する場合は、次のようにします。printf("Month: %s",calculateMonth(month));ここでの問題は、関数からその文字列を返す方法ですcalculateMonth

ベストアンサー1

関数のシグネチャは次のようになります。

const char * myFunction()
{
    return "my String";
}

背景:

これは C および C++ にとって非常に基本的なことですが、もう少し議論する必要があるでしょう。

C (および C++) では、文字列はゼロバイトで終了するバイト配列に過ぎません。そのため、この特定の種類の文字列を表すために「文字列ゼロ」という用語が使用されます。他の種類の文字列もありますが、C (および C++) では、この種類は言語自体によって本質的に理解されます。他の言語 (Java、Pascal など) では、異なる方法論を使用して理解されます"my string"

Windows API (C++ で記述) を使用する場合、次のような関数パラメータが頻繁に表示されます: "LPCSTR lpszName"。「sz」の部分は、「文字列ゼロ」の概念、つまり null (/ゼロ) 終端文字を持つバイト配列を表します。

説明:

この「入門」では、「バイト」と「文字」という言葉を同じ意味で使います。この方が学習しやすいからです。他の方法(ワイド文字やマルチバイト文字システム(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;
}

つまり、inが呼び出されるszBufferまでにコンパイラが使用していたメモリを解放してしまうため、プログラムがクラッシュすることになります (解放してしまうかどうかはわかりません)。(コンパイラはこのような問題について事前に警告してくれるはずです。)printf()main()

簡単には吐き出されない文字列を返す方法は 2 つあります。

  1. 一定期間存続するバッファ(静的または動的に割り当てられたバッファ)を返します。C++では、データの存続期間を処理するために「ヘルパークラス」(たとえば、std::string)を使用します(関数の戻り値の変更が必要です)。
  2. 情報が入力されるバッファを関数に渡します。

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"
}

ここでが行うことはstatic(多くのプログラマーはこの種の「割り当て」を好みません)、文字列がプログラムのデータ セグメントに配置されることであり、つまり、永続的に割り当てられます。

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"
}

There are lots of reasons why the second method is better, particularly if you're writing a library to be used by others (you don't need to lock into a particular allocation/deallocation scheme, third parties can't break your code, and you don't need to link to a specific memory management library), but like all code, it's up to you on what you like best. For that reason, most people opt for example 1 until they've been burnt so many times that they refuse to write it that way anymore ;)

Disclaimer:

I retired several years back and my C is a bit rusty now. This demo code should all compile properly with C (it is OK for any C++ compiler though).

おすすめ記事