61

別の関数のローカル変数にアクセスできますか? もしそうなら、どのように?

void replaceNumberAndPrint(int array[3]) {
    printf("%i\n", array[1]);
    printf("%i\n", array[1]);
}

int * getArray() {
    int myArray[3] = {4, 65, 23};
    return myArray;
}

int main() {
    replaceNumberAndPrint(getArray());
}

上記のコードの出力:

65
4202656

私は何を間違っていますか?「4202656」とはどういう意味ですか?

replaceNumberAndPrint()初めてアクセスできるようにするには、関数内の配列全体をコピーする必要がありますか?

4

10 に答える 10

61

myArrayはローカル変数であるため、ポインターはそのスコープの最後 (この場合はそれを含む function getArray) が終了するまでのみ有効です。後でアクセスすると、未定義の動作が発生します。

実際には、 への呼び出しがprintfによって使用されるスタックの一部を上書きし、myArray他のデータが含まれます。

コードを修正するには、十分な長さのスコープ (main例の関数) で配列を宣言するか、ヒープに割り当てる必要があります。ヒープに割り当てる場合は、手動で解放するか、RAII を使用して C++ で解放する必要があります。

私が見逃した代替手段の 1 つ (配列が大きすぎなければ、おそらくここで最良のものでさえあります) は、配列を構造体にラップして値型にすることです。次に、それを返すと、関数が返された後もコピーが作成されます。詳細については、 tp1回答を参照してください。

于 2010-12-31T13:31:25.363 に答える
19

スコープ外になると、ローカル変数にアクセスできなくなります。これがローカル変数であることの意味です。

関数内で配列にアクセスしているreplaceNumberAndPrint場合、結果は未定義です。初めて機能するように見えるという事実は、幸運な偶然です。おそらく、あなたが指しているメモリの場所はスタック上で割り当てられておらず、最初の呼び出しに対してまだ正しく設定されていますが、 への呼び出しprintfは、操作中に値をスタックにプッシュすることによってこれを上書きしprintfます。

配列データをヒープに格納してポインターを渡すか、スコープ内にある変数 (たとえば、グローバルまたはメイン関数内でスコープ化されたもの) に渡す必要があります。

于 2010-12-31T13:32:00.190 に答える
8

そのようなことを試してみてください。myArrayローカルで定義されている場合、その方法は原因を「殺します」 。

#include <stdio.h>
#include <stdlib.h>

void replaceNumberAndPrint(int * array) {
 printf("%i\n", array[0]);
 printf("%i\n", array[1]);
 printf("%i\n" , array[2]);
 free(array);
}

int * getArray() {
 int * myArray = malloc(sizeof(int) * 3);
 myArray[0] = 4;
 myArray[1] = 64;
 myArray[2] = 23;
 //{4, 65, 23};
 return myArray;
}

int main() {
 replaceNumberAndPrint(getArray());
}

詳細 : http://www.cplusplus.com/reference/clibrary/cstdlib/malloc/

編集:コメントが正しく指摘したように:それを行うより良い方法は次のとおりです:

#include <stdio.h>
#include <stdlib.h>

void replaceNumberAndPrint(int * array) {
    if(!array)
        return;

    printf("%i\n", array[0]);
    printf("%i\n", array[1]);
    printf("%i\n" , array[2]);
}

int * createArray() {
    int * myArray = malloc(sizeof(int) * 3);

    if(!myArray)
        return 0;

    myArray[0] = 4;
    myArray[1] = 64;
    myArray[2] = 23;
    return myArray;
}

int main() {
    int * array = createArray();
    if(array)
    {
        replaceNumberAndPrint(array);
        free(array);
    }
    return 0;
}
于 2010-12-31T13:36:44.717 に答える
2

これを行う正しい方法は次のとおりです。

struct Arr {
   int array[3];
};
Arr get_array() {
   Arr a;
   a.array[0] = 4;
   a.array[1] = 65;
   a.array[2] = 23;
   return a;
}
int main(int argc, char **argv) {
   Arr a = get_array();
   for(size_t i=0; i<3; i++)
       printf("%d\n", a.array[i]);
   return 0;
}

これを行う必要がある理由を理解するには、sizeof(array) がどのように機能するかを知る必要があります。C (および c++) は、配列のコピーを回避しようと懸命に努力しており、それを超えるには構造体が必要です。コピーが必要な理由はスコープのためです。get_array() 関数のスコープが消え、そのスコープからまだ必要なすべての値を呼び出しスコープにコピーする必要があります。

于 2011-01-02T23:15:14.943 に答える
2

ローカル変数は戻り時にスコープ外になるため、ローカル変数へのポインターを返すことはできません。

mallocまたはを使用して、動的に (ヒープ上に) 割り当てる必要がありますnew。例:

int *create_array(void) {
    int *array = malloc(3 * sizeof(int));
    assert(array != NULL);
    array[0] = 4;
    array[1] = 65;
    array[2] = 23;
    return array;
 }
 void destroy_array(int *array) {
     free(array);
 }
 int main(int argc, char **argv) {
     int *array = create_array();
     for (size_t i = 0; i < 3; ++i)
         printf("%d\n", array[i]);
     destroy_array(array);
     return 0;
 }

または、セマンティクスが異なることに注意して、配列を静的として宣言することもできます。例:

int *get_array(void) {
    static int array[] = { 4, 65, 23 };
    return array;
 }
 int main(int argc, char **argv) {
     int *array = get_array();
     for (size_t i = 0; i < 3; ++i)
         printf("%d\n", array[i]);
     return 0;
 }

意味がわからない場合は、この質問と回答staticをお読みください。

于 2010-12-31T14:15:17.297 に答える
2

getArray を離れるとすぐに、myArray は範囲外になります。代わりに、ヒープにスペースを割り当てる必要があります。

于 2010-12-31T13:31:59.597 に答える
2

あなたのコードは Undefined Behavior を呼び出します。これは、返さmyArrayれるとすぐに範囲外になり、ダングリング ポインターを使用(逆参照) しようとすると UB になるためです。getArray()

于 2010-12-31T13:34:10.060 に答える
1

C++ ソリューション:

「別の関数のローカル変数にアクセスできますか? もしそうなら、どうやって?」

答えはノーです。関数が終了した後ではありません。ローカル変数はその時点で破棄されます。

C++返される配列を処理する方法は、std::array (固定サイズ) またはstd::vector (動的サイズ)のようなコンテナーでそれらを管理することです。

例えば:

void replaceNumberAndPrint(const std::array<int, 3>& array) {
    printf("%i\n", array[0]);
    printf("%i\n", array[1]);
    printf("%i\n", array[2]);
}

std::array<int, 3> getArray() {
    std::array<int, 3> myArray = {4, 65, 23};
    return myArray;
}

2 番目の関数では、戻り値がコンパイラによって最適化されるため、配列を実際にコピーする代償を払う必要はありません。

于 2016-03-20T11:16:12.330 に答える
0

このコードでは、ローカル オブジェクトへのポインターを使用していますが、関数が返されると、すべてのローカル変数がスコープ外になります。メモリを割り当てる場合 (割り当てmalloc()用の関数を使用)、データが失われたり上書きされたりすることはありません。

int* getArray(int size) {
    int *myArray = (int*)malloc(size*sizeof(int));
    myArray[0] = 4;
    myArray[1] = 65;
    myArray[2] = 23;
    return myArray;
}

int main() {
    int i;
    int *vector = getArray(3);
    for(i=0;i<3;i++)
    {
        printf("%i\n",vector[i]);
    }
    getch();
    return 0;
}

このコードはすべての配列要素を出力し、上書きは行われません。

于 2014-01-07T08:53:51.460 に答える