2016-07-12 7 views
-6

関数内で宣言されたポインタを返すことは、悪い習慣とみなされていますか?例:私はあなたが使用することを想定していると信じてポインタの戻り値とスコープ

int* foo(void) 
{ 
    int * ret_val = 1234; 

    return ret_val; 
} 

static int * ret_val = 1234; 

しかし、その後、再び、まだあなたのスコープの外のメモリを返すために悪い習慣とはみなされないでしょうか?

+1

返却するメモリを割り当てていませんでした。だからそれは悪い習慣です。しかし、 'malloc()'を使ってメモリを割り当てると、 'malloc'のメモリは関数呼び出しの間に使用できるので、悪い習慣ではありませんでした。 – Haris

+0

'* ret_val = 1234;'はすべて単独では悪いです。あなたが質問する限り、あなたが 'new'を使用しないと行くと、それはhttp://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-の欺瞞です。スコープ – NathanOliver

+1

はい、動的に割り当てられたオブジェクトを返すのは、悪い習慣です。メモリリークの可能性を追跡するのが難しいためです。 – meJustAndrew

答えて

2

ポインタを返すことは、動的に割り当てられたメモリ、グローバルデータ、またはコールスタックの上にまだ存在するローカル変数など、スコープ内にあるメモリを指している限り問題にはなりません。

上記のコードの問題は、ポインタを何も割り当てずに逆参照していることです。これは:*ret_val = 1234;ret_valが指すアドレスに値1234を割り当てますが、ret_valには何も割り当てられませんでした。

一方で、あなたがこれをしなかった場合:あなたはメモリを動的に割り当てられたポインタを返すしているので、大丈夫です

int* foo(void) 
{ 
    int * ret_val; 

    ret_val = malloc(sizeof(int)); 
    *ret_val = 1234; 

    return ret_val; 
} 

+1

"メモリはまだ有効範囲にあります":これはどういう意味ですか? –

+0

@ScottHunter詳細を追加しました。 – dbush

2

主要原因は

*ret_val = 1234; 

では、初期化されていないポインタが印加されます。

static int* ret_val; 

ありませんが、あなたが

int* foo() { 
    static int_val = 1234; 
    return &int_val; 
} 

を使用することができることを修正するためにしかし、それはあなたが本当に欲しいものだかどうかは議論の余地があります:

私はあなたが使用することを想定していると信じています。その機能のすべての呼び出し元はint_valの同じインスタンスを共有します。

+2

なぜそれをしますか?初心者がそのような「静的」なものを乱用するように教えてはいけません。あなたがしていることを正確に知っていない限り、それはひどい習慣です。 – interjay

+0

@interjay私は[ローカル変数のメモリはそのスコープの外にアクセスできますか?](http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-その質問は状況に正確に一致していないためです)。 –

+0

ええ、それは同じ質問のようには思われません。しかし、あなたの答えでは、あなたの提案されたコードが、ポインタを返すほとんどの関数がすると思われるものではない、シングルトン(すべての呼び出し元が同じpointeeを共有する)を作成することに言及するべきでしょう。 – interjay

0

ダングリングポインタがあるので、ローカル変数へのポインタを返すのは本当に悪い習慣です。

int* copy_array(int *array, int size) 
{ 
    int *v = (int*) malloc (size * sizeof (int)); 
    //..copy the array.. 
    return v; 
} 

とにかく、あなたはポインタを返す避ける必要があり、そのメモリリークのリスクはありません。

int* foo(void) 
{ 
    int a = 1337; 
    return &a; //Don't do this! 
} 

しかし、時にはそれが役に立つかもしれません。それは特別な注意を要するものの

+0

私は動的に割り当てられたメモリへの生ポインタをC++の悪い練習に戻すことを検討します – Slava

+0

はい、代わりに、生の配列の代わりにSTLコンテナまたは他のソリューションを使用する必要があります。 –

+0

彼の答えにはタグ「c」があるので、私はそれを使用しました。また、その関数はC言語ではtipicalですが、C++ではSTLを使うべきです。とにかく私はそれのためにこの答えをdownvotingの理由はありません。 –

0

は別として、それが割り当てされることなくret_valのご利用から、関数からポインタを返すことは、公正な慣行である:

  • はそれとして、ローカル変数へのポインタを返しません。参照が解除された場合に関数が終了し、未定義の動作が発生すると破棄されます。
  • 関数がメモリを割り当てた場合、メモリリークを避けるためにポインタが不要になったときにポインタを解放することが呼び出し側関数の役割です。
  • 関数がfoの配列を処理している場合あなたが考慮すべきポインタのRM、それは一般的に、余分なsizeパラメータとして、未定義の動作を避けるために、配列のサイズを意識する必要がありますまたはグローバル定数
0

int *foo() { 
     int *ret_val = new int(1234); 
     return ret_val; 
} 

の代わりに、 (悪いコード):

int* foo(void) 
{ 
    int * ret_val; 

    *ret_val = 1234; 

    return ret_val; // dangling pointer! 
} 

そうでなければ、ポインタ(第2例)をぶら下がっています。 明らかにメモリを解放することを忘れないでください。 また、スマートポインタを使用して検討する必要があります。 https://en.wikipedia.org/wiki/Smart_pointer

あなたはANSI Cの上記のコードはC++(new演算子)であり、(例えば)を参照してください@Mattia F.の答えの両方C++やC言語を指定されているように

+0

@Amadeus明らかにそうではありません。 ANSI Cでは、mallocを考慮する必要があります。質問にC++タグがあるので問題にはならないはずです。 – nosbor

+0

@Amadeus今はうまくいくはず – nosbor

0

ポインタは、ショートカットまたはハイパーリンクのようなものです。ショートカットを作成しても、プログラムはインストールされません。ハイパーリンクを作成しても、ウェブサイト全体が魔法のように設定されるわけではありません。

ポインタと同じ:int*は整数へのポインタです。これは整数そのものではありません。そのため、ポインタをたどる前に、ポインタが意味のあるものを指していることを確認する必要があります。

int* ret_val = new int; 

これは整数(newオペレータ)を作成し、その後指すようにret_valポインタを設定します。そうすることの

一つの方法は、それを指すようにポインタ整数との組を作成することですそれに。

newによって作成されたオブジェクトは、明示的に破棄するまで存在します(delete)。そのため、関数内にnew intがある場合、関数が終了してもそれにアクセスすることは安全です。 dangereousだろう何

、おそらくあなたの懸念を引き起こし、あなたは、このようなシナリオで

int myInteger; 
int* ret_val = &myInteger; 

すなわち、ない新しい整数に、しかし、既存のローカル変数にret_valポイントをポインタret_valを設定するならばです宣言を含むスコープが終了したときに破棄されるローカル変数myIntegerに書き換えます。あなたは存在しないものを参照して、ぶら下がったポインタで終わります(壊れたハイパーリンクを考えてください)。このようなポインタを使用すると、未定義の動作につながる可能性があります。プログラムがクラッシュする可能性もありますが、メモリ内のランダムな領域を変更して静かに受け入れます。


ので、形状の機能:

int* foo(void) 
{ 
    int* ret_val = new int; 
    *ret_val = 1234; 
    return ret_val; 
} 

を使用しても安全です。それは必ずしも良い習慣ではありません。そのような関数のユーザーは、新しい整数を作成することを知っていなければなりません。ある時点で、誰かがdeleteでなければなりません。これを行う関数は、通常、そのような振る舞いを何らかの方法で強調表示します。その名前によって。たとえば、allocateInt()は、後で削除する必要があるものを作成したことを明確にします。対照的に、getInt()は、整数がすでに存在し、新しいものは作成されないことを示唆しています。

関連する問題