0

ここに私の問題があります: 関数からローカル変数へのポインターを返すことは安全でない場合があることを StackOverflow で読みました。例えば:

#include<iostream>

using namespace std;

int *foo(void) {
    int x[] = {1,2,3};
    return x;
}

int main() {
    int *numbers;
    numbers = foo();
    return 0;
}

xローカル配列であるため、メモリが割り当てられていない可能性があることを考えると、これが安全でないかどうかを知りたいのですが、同じ結果を達成するためのより良い方法は何ですか?

4

6 に答える 6

10

関数からローカル変数へのポインターを返すことは安全でない場合があることを StackOverflow で読みました。

ポインターをローカル変数に返すことは常に安全ではありません。実際、そうするのは間違っており、このポインターを使用すると、未定義の動作が発生します。この素晴らしい投稿も参照してください。

呼び出しスコープにデータを返したい場合は、std::vectorをコピーとして使用できます。

std::vector<int> foo(void){
  std::vector<int> x = {1,2,3}; // using C++11 initializer list
  return x;
}

固定長配列 (常にサイズ 3) の場合は、代わりにstd::arrayを使用できます。


要件によっては、静的変数を使用することもできます。つまり、変数がスコープ外になることはなく、参照 (またはポインター) によって安全に返すことができます。コピーは 1 つしかないことに注意してください。変更すると、変更されたままになります。(const &読み取り専用の場合は作成してください。)

std::vector<int>& foo(void) {
  // this is only instantiated once when the function is first called
  static std::vector<int> x = {1,2,3}; 
  return x;
}
于 2012-07-02T10:40:08.103 に答える
0

次のいずれかを実行できます。

  1. x を次のように宣言します。static
  2. x を a として宣言します。pointer
于 2012-07-02T10:41:46.917 に答える
0

まずint x = {1,2,3}構文エラーです。そのはずint x[] = {1,2,3};

これは未定義の動作です。自動配列には、それが定義されているブロック内に有効期間があるため、それはfoo()関数内にあります。foo()そのため、ストレージから戻るたびにx、 が予約されている保証はなくなります。したがって、ポインターを介してその場所にアクセスする場合、動作は未定義です。

同じ結果を得るには、メモリを動的に割り当てます。

int *foo(void){
int x[] = {1,2,3}, *x_to_return;
x_to_return = new int [sizeof (x)/sizeof(x[0])];
memcpy (x_to_return, x, sizeof (x));
return x_to_return;
}

基本的に、必要なことは、を使用してストレージを動的に割り当て、newデータをによって割り当てられたメモリ ブロック (ベース) にコピーし、newそのメモリ アドレスを呼び出し元に返すことです。

使用が終了したら、割り当てられたメモリを解放することを忘れないでください。そうしないと、コードでメモリリークが発生する可能性があります。

また、次のような宣言がある場合はstatic int x[] = {1,2,3};、 のアドレスを返すことができます。xこの場合、 の有効期間はxプログラムの実行時間全体であるためです。

あなたの質問には c++ とタグ付けされているのでvector、moooeeeep の回答を確認してください。

于 2012-07-02T10:40:00.897 に答える
0

その配列へのポインターを返すことは安全ではありません (むしろ間違っています)。これは、関数が戻るときに存在しなくなっているためです。メモリの割り当てを解除するだけでなく . それでも偶発的に機能する可能性があることに注意してください。ただし、正直なところ、機能しない場合よりも悪いことになります (予測不能であり、デバッグが不可能であるため)。うまくいくように見えても、「でもうまくいくように見える」ことを決して試みないでください。

これは、ポインターまたは参照を返す場合と同じですが、const参照は例外です。参照は、参照先のconstオブジェクトをそれ自身の存続期間中存続させます。

staticポインターを返したい場合は、オブジェクトを動的に割り当てたり作成したりすることがオプションになります。または、一時オブジェクトを値で返し、コンパイラに依存して RVO を出力します。

于 2012-07-02T10:40:16.930 に答える
0

ローカル変数へのポインターを返すことは決して安全ではありません (実際には未定義の動作です)。ほとんどの実装では、それらはスタック上に存在するため、ポインターが使用されると上書きされる可能性があります。

動的に割り当てられた配列を返すことができます:

int* foo() { int* x = new int[3]; ..}

もちろん、ポインターを手動で削除する必要があるため、堅牢で例外セーフなコードを書くことが難しくなります。したがって、通常はベクターを使用することをお勧めします。

std::vector<int> foo() { 
   std::vector<int> x;
   x.push_back(1); 
   x.push_back(2); 
   x.push_back(3);
   return x;
}

C++11 を使用している場合は、初期化リストを使用してベクターを埋めることができ、コードがより適切になります。

std::vector<int> foo() { std::vector<int> x = {1,2,3};  return x; }

C++11 にはムーブ セマンティクスがあります。つまり、この場合、ベクトルを値で返すとパフォーマンスがほとんど犠牲になりません。C++03 の場合、パフォーマンスが重要な場合は、関数に vector への参照/ポインターをパラメーターとして指定し、次のように入力できます。

void foo(std::vector<int>& x) {x.clear(); x.push_back(1); ...}
于 2012-07-02T10:44:28.113 に答える
-1

はい、配列はスタックに割り当てられているため安全ではありません。そのため、関数が戻ったときに割り当てが解除されます。

関数内に配列を割り当てる代わりに、配列を関数内に作成し、その配列へのポインターを関数に渡します。これは単なる例です。

#include<iostream>
using namespace std;

void foo(int numbers[]){
    numbers[0] = 1;
    numbers[1] = 2;
    numbers[2] = 3;
}

int main(int args, char**argv) {
    int numbers[3];
    foo(numbers);
    cout << numbers[0] << numbers[1] << numbers[2];
    return 0;
}

これにより、「123」が出力されます。

于 2012-07-02T10:54:42.023 に答える