22

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すでに配列の末尾に追加されています。この場合、回答(承認済みとしてマーク)は、いわばそのままで機能します。

4

12 に答える 12

45

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

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 関数と STLSoft のarray_proxyクラス テンプレートで使用されます。

于 2009-01-14T21:47:31.193 に答える
26

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

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要素の配列を予約し、常にセンチネル要素を設定していることを確認する必要があります。

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

于 2009-01-14T21:25:27.743 に答える
6

実際には、配列の最初の要素で長さを渡すのは非常に一般的な解決策でした。この種の構造は、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.
}
于 2009-01-14T22:03:35.017 に答える
6

一般に、C または低レベルの C++ を使用する場合、配列パラメーターを関数に書き込むことを決して考慮しないように脳を再訓練することを検討してください。本質的に、これらの角括弧を入力することで、サイズ情報を備えた実際の配列が渡されていると思い込んでいるのです。実際には、C ではポインターしか渡すことができません。関数

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

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

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

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

行き詰まっていて関数シグネチャを変更できない場合は、上記で提案した長さのプレフィックスの使用を検討してください。移植性はないが互換性のあるハックは、次のように、配列の前にある size_t フィールドに配列の長さを指定することです。

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

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

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

于 2009-01-14T22:44:41.577 に答える
-1

32 ビット PC で 4 を受け取ることが保証されており、それが正解です。ここここで説明されている理由のため。簡単に言えば、配列ではなくポインターのサイズを実際にテストしているということです。これは、「配列は暗黙的にポインターに変換または減衰するためです。残念ながら、ポインターは配列の次元を格納しません。そうではありません。問題の変数が配列であることさえ教えてくれます。」

C++ を使用するようになったので、生の配列よりもboost::arrayの方が適しています。これはオブジェクトであるため、寸法情報が失われることはありません。

于 2009-03-18T08:52:22.907 に答える
-2

私はあなたがこれを行うことができると思います:

size_t size = sizeof(array)/sizeof(array[0]);

PS: このトピックのタイトルも正しくないと思います。

于 2012-11-19T17:49:18.153 に答える
-3

プログラム全体でアクセスできる配列のサイズを格納するグローバル変数を使用できます。少なくとも、配列のサイズを main() 関数からグローバル変数に渡すことができます。サイズはグローバルに使用できるため、メソッド シグネチャを変更する必要さえありません。

例を参照してください:

#include<...>
using namespace std;

int size; //global variable

//your code

void doSomething(char charArray[])
{
    //size available

}
于 2013-01-16T08:36:22.510 に答える