web-dev-qa-db-ja.com

関数パラメーターとしての配列、固定サイズ配列、および配列のベースアドレスの受け渡しの違い

関数パラメーターとして既知または未知のサイズの配列を渡したい場合、どの構文を使用するかについて混乱しています。

目的のためにこれらのバリアントがあるとします:

void func1(char* str) {
    //print str
}

void func2(char str[]) {
    //print str
}

void func3(char str[10]) {
    //print str
}

これらのそれぞれを使用することの長所と短所は何ですか?

55
mr5

これらのバリアントはすべて同じです。 Cでは代替スペルを使用できますが、配列サイズで明示的に注釈が付けられた最後のバリアントでも、通常のポインターになります。

つまり、最後の実装でも、any sizeの配列で関数を呼び出すことができます。

void func3(char str[10]) { }

func("test"); // Works.
func("let's try something longer"); // Not a single f*ck given.

言うまでもなくnotを使用する必要があります:ユーザーに誤った安心感を与える可能性があります」)。

Henrikが言ったように、正しい方法C++でstd::stringstd::string&またはstd::string const&を使用することです(オブジェクトを変更する必要があるかどうか、およびコピーしたい)。

71
Konrad Rudolph

C++では、コンパイル時に配列の長さがわかっている場合(たとえば、文字列リテラルを渡した場合)、実際にそのサイズを取得できることに注意してください。

template<unsigned int N>
void func(const char(&str)[N])
{
    // Whatever...
}

int main()
{
    func("test"); // Works, N is 5
}
20
Morwenn

C++では、void func4(const std::string& str)を使用します。

11
Henrik

これらはすべて機能的に同一です。 Cの関数に配列を渡すと、配列は暗黙的に配列の最初の要素へのポインターに変換されます。したがって、これら3つの関数は同じ出力(つまり、charへのポインターのサイズ)を出力します。

_void func1(char* str) {
    printf("sizeof str: %zu\n", sizeof str);
}

void func2(char str[]) {
    printf("sizeof str: %zu\n", sizeof str);
}

void func3(char str[10]) {
    printf("sizeof str: %zu\n", sizeof str);
}
_

この変換は、配列の最初の次元にのみ適用されます。 _char[42][13]_はchar (*)[13]に変換されます。notは_char **_です。

_void func4(char (*str_array)[13]) {
    printf("sizeof str_array: %zu\n"
           "sizeof str_array[0]: %zu\n", sizeof str_array, sizeof str_array[0]);
}
_

char (*)[13]は_str_array_の型です。 「13 charsの配列へのポインター」の書き方です。これはvoid func4(char str_array[42][13]) { ... }と書くこともできますが、異なるサイズの配列を_func4_に渡して実験することでわかるように、42は機能的に無意味です。

C99およびC11(ただし、C89またはC++ではありません)では、サイズを可変サイズの配列にポインターを渡すことができます。サイズと一緒にサイズを渡し、サイズ識別子を_[square brackets]_に含めます。例えば:

_void func5(size_t size, char (*str_array)[size]) {
    printf("sizeof str_array: %zu\n"
           "sizeof str_array[0]: %zu\n", sizeof str_array, sizeof str_array[0]);
}
_

これは、sizecharsの配列へのポインターを宣言します。配列にアクセスする前に、pointerを逆参照する必要があることに注意してください。上記の例では、_sizeof str_array[0]_は、最初の要素のサイズではなく、配列のサイズに評価されます。例として、11番目の要素にアクセスするには、_(*str_array)[11]_または_str_array[0][11]_を使用します。

9
autistic

アドオンするには、ポイントで説明します。

1)皆が言ったように、それは同じです。

2)配列は、関数の引数に渡されると、ポインターに分解されます。

3)基本的な問題は、関数内の配列のサイズを見つけることです。そのためにマクロを使用できます。

   #define noOfElements(v) sizeof(v)/sizeof(0[v])

   int arr[100]
   myfunction ( arr, noOfElements(arr))

0 [v]またはv [0]のいずれかをマクロで使用できます。最初のマクロは、noOfElementsに渡されるユーザー定義のデータ型を回避するために使用されます。

お役に立てれば。

1
Whoami

1次元配列では、それらはすべてコンパイラーによって同じように扱われます。ただし、2次元以上の配列の場合(例:myArray[10][10])、配列の行/列の長さを決定するために使用できるので便利です。

1
user2301717