web-dev-qa-db-ja.com

C ++で関数パラメーターとして配列を渡す

C++では、配列を単にパラメーターとして渡すことはできません。私がそのような関数を作成した場合の意味:

void doSomething(char charArray[])
{
    // if I want the array size
    int size = sizeof(charArray);
    // NO GOOD, will always get 4 (as in 4 bytes in the pointer)
}

配列へのポインタしかないため、配列の大きさを知る方法がありません。

メソッドのシグネチャを変更せずに、配列のサイズを取得してそのデータを反復処理する方法はありますか?


編集:ソリューションに関する追加です。特に、char配列が次のように初期化された場合:

char charArray[] = "i am a string";

そうして \0はすでに配列の最後に追加されています。この場合、答えは(受け入れ済みとマークされている)そのままで機能します。

18
Yuval Adam

署名を変更せずに?センチネル要素を追加します。特にchar配列の場合、標準のC文字列に使用されるnullで終了する'\0'になる可能性があります。

void doSomething(char charArray[])
{
    char* p = charArray;
    for (; *p != '\0'; ++p)
    {
         // if '\0' happens to be valid data for your app, 
         // then you can (maybe) use some other value as
         // sentinel
    }
    int arraySize = p - charArray;

    // now we know the array size, so we can do some thing
}

もちろん、配列自体にセンチネル要素をコンテンツとして含めることはできません。他の種類の(つまり、char以外の)配列の場合、正当なデータではない任意の値である可能性があります。そのような値が存在しない場合、このメソッドは機能しません。

さらに、これには発信者側の協力が必要です。呼び出し元がarraySize + 1要素の配列を予約し、alwaysが監視要素を設定することを確認する必要があります。

ただし、署名を実際に変更できない場合、オプションはかなり制限されます。

24
Reunanen

テンプレートを使用します。これは、署名を変更するため、技術的には条件に合いませんが、呼び出しコードを変更する必要はありません。

void doSomething(char charArray[], size_t size)
{
   // do stuff here
}

template<size_t N>
inline void doSomething(char (&charArray)[N])
{
    doSomething(charArray, N);
}

この手法は、Microsoftの Secure CRT functions およびSTLSoftの array_proxy クラステンプレートで使用されています。

38
Josh Kelley

一般に、Cまたは低レベルのC++を使用する場合、Cコンパイラは常に配列パラメーターをポインターとして扱うため、配列パラメーターを関数に書き込むことを考慮しないように脳を再トレーニングすることを検討する場合があります。本質的に、これらの角括弧を入力することで、実際の配列がサイズ情報とともに渡されていると思い込んでいることになります。実際には、Cではポインタのみを渡すことができます。関数

void foo(char a[])
{
    // Do something...
}

cコンパイラの観点からは、以下とまったく同じです。

void foo(char * a)
{
    // Do something
}

そして明らかにそのnekkid charポインタには長さ情報が含まれていません。

コーナーで立ち往生していて、関数のシグネチャを変更できない場合は、上記のように長さのプレフィックスを使用することを検討してください。移植性はありませんが互換性のあるハックは、次のような配列beforeにあるsize_tフィールドに配列の長さを指定することです。

void foo(char * a)
{
    int cplusplus_len = reinterpret_cast<std::size_t *>(a)[-1];
    int c_len = ((size_t *)a)[-1];
}

明らかにあなたの呼び出し元はfooに渡す前に適切な方法で配列を作成する必要があります。

言うまでもなくこれは恐ろしいハックですが、このトリックはピンチでトラブルから抜け出すことができます。

6
John Källén

実際には、配列の最初の要素で長さを渡すための非常に一般的なソリューションでした。この種類の構造は、さまざまな(ただし類似した)型を示していても、しばしばBSTR(「BASIC文字列」)と呼ばれます。

受け入れられているソリューションに対する利点は、センチネルを使用して長さを決定するのが、大きな文字列では遅いことです。欠点は明らかに、これはタイプも構造も考慮しない、かなり低レベルのハックであることです。

以下に示す形式では、長さが255以下の文字列に対してのみ機能します。ただし、長さを複数のバイトに格納することで簡単に拡張できます。

void doSomething(char* charArray)
{
    // Cast unnecessary but I prefer explicit type conversions.
    std::size_t length = static_cast<std::size_t>(static_cast<unsigned char>(charArray[0]));
    // … do something.
}
6
Konrad Rudolph

nullで終了している場合は、strlen()が機能します。

4
Jimmy

charArrayだけではサイズを特定できません。その情報は関数に自動的に渡されません。

もちろん、それがnullで終了する文字列の場合はstrlen()を使用できますが、おそらくすでに検討済みです!

std::vector<char>&パラメータ、またはポインタのペア、またはポインタとサイズパラメータ。

2
finnw

これは実際にはC++よりもCが多く、C++ではおそらくstd :: vectorを使用します。ただし、Cでは配列のサイズを知る方法はありません。コンパイルでは、配列が現在のスコープで宣言されていて、サイズで明示的に宣言されている場合にのみ、sizeofを実行できます(編集: 「サイズあり」とは、それが整数サイズで宣言されたか、宣言時に初期化されたことを意味します。

Cでの一般的な解決策は、配列の要素数を表す2番目のパラメーターを渡すことです。

編集:
申し訳ありませんが、メソッドシグネチャを変更したくないという部分を逃しました。次に、他の人が説明している場合を除いて、解決策はありません。配列内で許可されていないデータがある場合は、ターミネーターとして使用できます(C文字列では0、-1もかなり一般的ですが、 char配列が仮説であると仮定した場合の実際のデータ型)

2
falstro

関数が、渡された配列内の項目の数を知るためには、次の2つのいずれかを行う必要があります。

  1. サイズパラメータを渡す
  2. どういうわけか配列にサイズ情報を入れてください。

後者はいくつかの方法で実行できます。

  • NULLまたは通常のデータでは発生しない他のセンチネルで終了します。
  • 配列が数値を保持している場合、最初のエントリに項目数を格納します
  • 配列にポインタが含まれている場合、最後のエントリへのポインタを格納します
1
AShelly

strlen(charArray);を使用してみてください。 cstringヘッダーファイルを使用します。これにより、スペースを含む文字数が終了に達するまで生成されます。

0
Percy Stainwall