web-dev-qa-db-ja.com

ポインターを使用して別の関数からローカル変数にアクセスする方法は?

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

_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()関数内の配列全体をコピーして、最初よりも多くアクセスできるようにする必要がありますか?

48
Radek Simko

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

実際には、printfを呼び出すと、myArrayが使用するスタックの一部が上書きされ、他のデータが含まれることになります。

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

私が見逃した1つの選択肢(おそらく配列が大きすぎない限り、ここで最高のものでも)は、配列を構造体にラップして値型にすることです。次に、それを返すと、関数の戻りを生き延びたコピーが作成されます。詳細については、 tp1answer を参照してください。

56
CodesInChaos

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

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

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

18
James Gaunt

そのようなものを試してください。それを行う方法は、ローカルで定義されている場合に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/

Edit:コメントが正しく指摘しているように:より良い方法は次のとおりです:

#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;
}
8
user418748

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

2
John Pickup

myArraygetArray()が返り、se(逆参照)を試みるとすぐにスコープから外れ、ダングリングポインターがUBであるため、コードは未定義の動作を呼び出します。

2
Prasoon Saurav

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

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の意味がわからない場合は、 この質問と回答 を読んでください。

2
jweyrich

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

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()関数のスコープが消え、そのスコープからまだ必要なすべての値を呼び出し元のスコープにコピーする必要があります。

2
tp1

C++ソリューション:

「別の関数のローカル変数にアクセスできますか?その場合、どのように?」

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

C++返される配列を処理する方法は、 std :: array (固定サイズ)などのcontainerで配列を管理することです。 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番目の関数では、コンパイラーによって戻り値が最適化されるため、実際に配列をコピーする費用はかかりません。

1
Galik

このコードでは、ローカルオブジェクトへのポインターを使用しましたが、関数が返すとき、すべてのローカル変数はスコープ外になります。メモリを割り当てる場合(割り当てに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;
}

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

0
aquib