質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

6回答

1401閲覧

staticの非共有化

strike1217

総合スコア651

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2018/03/05 07:58

簡単な実験関数です。

配列にstaticを付けています。
関数が終了するとwstrが消えてしまうためです。

C

1char *test(int wd){ 2 static char wstr[][10]{ 3 "Sunday", "Monday" .... 4 }; 5 return ((wd >= 0 && wd < 7) ? wstr[wd] : NULL); 6}

しかし、staticにしておくと問題を起こす場合があります。
再帰関数です。

staticが共有されていることを確認してみます。↓

C++

1#include<iostream> 2#include<string> 3#include<memory> 4 5struct node{ 6 std::string name; 7 int no; 8}; 9 10struct node* test_func(){ 11 auto my_del = [](struct node *pInvest){ 12 std::cout << "called deleter!!" << pInvest << std::endl; 13 delete pInvest; 14 }; 15 16 static std::unique_ptr<struct node, decltype(my_del)> ptr(new struct node, my_del); 17 18 std::printf("ptr address = %p\n", ptr.get()); 19 std::cin >> ptr->name; 20 ptr->no = 10; 21 22 return ptr.get(); 23} 24 25int main(){ 26 struct node *str, *wtr; 27 str = test_func(); 28 29 std::cout << str->name << std::endl; 30 std::cout << str->no << std::endl; 31 32 wtr = test_func(); 33 34 std::cout << wtr->name << std::endl; 35 std::cout << wtr->no << std::endl; 36 37 return 0; 38} 39

[結果]
ptr address = 0x5585759fce70
jreoeau
jreoeau
10
ptr address = 0x5585759fce70
reiwu
reiwu
10
called deleter!!0x5585759fce70

同じアドレスが表示されるため、staticが共有されていることが確認できます。

しかし、
再帰関数にするとこの共有された変数が呼び出された関数によって崩されてしまいます。

そこで、staticを関数毎に確保する方法ってあるんでしょうか??
関数内のstaticの非共有化できれば良いのですが・・・

[自分が考えた方法]
staticを配列にしておいて、関数が呼び出されるときに参照カウンタをインクリメントする。その関数内では配列のカウンタが指す要素を利用する。
関数が終了する時に、カウンタをデクリメントする。

・・・・面倒です。!

関数内のstaticの非共有化は調べてみたのですが、でてきません。

分かる方いますか??
教えてください。

gcc 7.3.0 Linuxです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

純粋なC言語の場合、言語内で扱える生存期間はauto(C++の「型推論」という意味ではなく、自動変数という意味です)とstaticしかなく、それとは別にやろうとすれば、自力でメモリ管理する、という選択肢しかありませんでした。

C++では、クラスのコンストラクタ・デストラクタとともに生存範囲を持てるような、スマートポインタが登場しています。

…というより、後者の例で、スマートポインタをstaticにしてしまっては、スマートポインタの持つ意味を全力でぶち壊しにしている感じしかありません。スマートポインタからはstaticを外した上で、struct node *を返さずに、スマートポインタのインスタンスをそのまま返すようにすれば、問題は解決します。

投稿2018/03/05 08:12

編集2018/03/05 08:16
maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2018/03/05 08:17 編集

スマートポインタについては・・・・まぁ・・・確かにそうですね。 スマートポインタに頼らない場合、質問中のtest関数の場合、staticに頼らないといけませんが・・・ staticの非共有化は不可能ということでしょうか??
maisumakun

2018/03/05 08:19

はい、staticは「関数の実行と無関係に」メモリを確保するものなので、本質的に共有するものとなります。
strike1217

2018/03/05 08:21

むむむ〜〜〜。 そうですか!! ・・・
strike1217

2018/03/05 08:25

std::unique_ptrを採用すると、あっちもこっちもスマートポインタにしなくてはいけなく、面倒ではないですか? 書くのが結構めんどくさい。 都合がいい位置でget()を使用しrawポインタを取得した方が・・・楽なんですよね。 deleteやfreeしなくて良いので・・・
maisumakun

2018/03/05 08:26

C言語の場合、ガベージコレクタがないので、変数管理は「起動から終了まで確保している(static)」「ブロックに入ったら確保、出たら開放(自動変数)」「mallocとfreeで自前管理」の3つしかありません。 ガベージコレクタのない環境で「関数に入ったら確保→使い続ける限りは生き残る」というようにするには、(メモリリークを許すという危険な選択肢を除けば)「必要なくなったときに開放する」しか手段がありません。
strike1217

2018/03/05 08:29

C++でもCと同様にstaticの非共有化はできないんですかね?? C++みたいに機能がたくさんあるような言語であれば、できるかなぁ・・・と思ったのですが・・・
maisumakun

2018/03/05 08:29

「unique_ptrから取得した生ポインタを引き回す」というのも、かなり危険な行為です。 というのも、(上の例みたいにstaticにしているのでもなければ)unique_ptrがスコープを抜けた時点で、生ポインタの指す先は無効となるので、せっかくスマートポインタを導入したにも関わらずメモリ安全が確保できなくなってしまうからです。
maisumakun

2018/03/05 08:33

>> C++でもCと同様にstaticの非共有化 「ガベージコレクタを導入したC/C++環境」など、特殊な拡張としてなら存在するかもしれませんが、そうでなければ「使い終わればよしなに開放する」という手段がないですので、そんなことをやったとすればメモリリークへ一直線です。
strike1217

2018/03/05 08:34 編集

では、static以外で、静的記憶期間に配置しつつ、共有化されないオブジェクト(変数)って作れないんですかね? C++にそんな機能ありませんかね?
maisumakun

2018/03/05 08:35

> 静的記憶期間に配置しつつ、共有化されないオブジェクト newやmallocで確保したものも、自分から開放しない限りずっと残り続けるという意味では同じです。
strike1217

2018/03/05 08:35

ん? でも待ってください。 static std::unique_ptr はmain()終了時にデストラクタが呼び出されているので、メモリリーク危険性はないのでは?? メモリリークを防ぐためにスマートポインタを採用しているので・・・
maisumakun

2018/03/05 08:37

ずっとスマートポインタで引き回せば、もちろん問題ありません(スコープから抜けたところで開放されます)。
strike1217

2018/03/05 08:37

確保した関数内で解放しなくても、main()終了時にまとめて解放すれば、結果的にはメモリリークにはならないような・・・
maisumakun

2018/03/05 08:41

使い捨てのプログラムなら、「どうせ終了すれば開放される」と割り切ってもいいのかもしれません。 一方で、OSやサーバプログラムなど、ずっと動き続けるものの場合、少しのメモリリークでも続けば致命傷となりかねません。
strike1217

2018/03/05 09:26

ん?? そうなんですか?? OSように終了しないプログラムはそもそもメモリリークという概念は登場しないのでは?という疑問があるのですが・・・
rubato6809

2018/03/05 09:44

OSにもメモリリークバグはありますよw 動的にメモリ確保・返却って、OS内部には何箇所もあるので。
strike1217

2018/03/05 10:02

そうなんですね。 「動的確保をして、解放せずにプログラムが終了した場合」をメモリリークとしていると思っていました。 確かに、 「すでに利用していない領域を長期間ほったらかしにする」でもメモリリークと考えてよさそうですね。
asm

2018/03/05 10:04

> 静的記憶期間に配置しつつ、共有化されないオブジェクト ただのメモリリークじゃないですかそれ……
strike1217

2018/03/05 10:07

え? そうなんですか?
asm

2018/03/05 10:08

関数を呼ぶ度にメモリを消費して、決して開放されない メモリリーク以外の何でしょうか?
strike1217

2018/03/05 10:12 編集

静的記憶期間に配置されるので、そもそもプログラム終了時に消されるはずですが・・・ プログラム終了時まで残っているのが・・・静的記憶期間の特徴ですよね? 動的記憶期間であれば、メモリリークですが・・・
asm

2018/03/05 10:13

OSにもよりますが静的動的問わず、メモリ/リソースはプロセス終了時に解放されます OSの機能として特別に解放されないメモリを作る事ができる事もありますが
strike1217

2018/03/05 10:22

ええ・・・ それは知ってます。 大抵の場合はOS側が動的記憶期間は自動的に解放してくれますね。
maisumakun

2018/03/05 10:59 編集

「メモリリーク」は、「コンピュータで実行中のプログラムが確保したメモリ領域の解放を忘れたまま放置してしまうこと」全般を指す語です(http://e-words.jp/w/%E3%83%A1%E3%83%A2%E3%83%AA%E3%83%AA%E3%83%BC%E3%82%AF.html)。 「プログラムは動き続けているけど、使いもしないメモリを確保したままになっている」という状況もメモリリークです。
strike1217

2018/03/05 11:01

ああ! なるほど! 長期間ほったらかしていてもメモリリークと見なせるんですね。
maisumakun

2018/03/05 11:09

一般的なC言語環境であれば、staticに必要なメモリはコンパイル時に決まるので、プログラムを起動してから終了するまで変化することはありません(だからこそ「静的」記憶域です)。 関数の実行ごとに確保するメモリは、分量も決まらないので、当然コンパイル時に静的に確保する訳にはいきません。
strike1217

2018/03/05 11:17

その通りですね。
guest

0

ベストアンサー

主にCについてですが、C++についても後ほど述べます。(特に明記しない限り、C11、C++17前提です)

staticローカル変数はスコープが狭いグローバル変数である。

staticローカル変数はプログラムの実行時に領域が確保され、プログラムの終了時まで維持されます。ぶっちゃけて言うと、staticローカル変数は、スコープがそのブロック内に限られているだけで、グローバル変数やファイルスコープ変数(トップレベルでstaticがついた変数)と同じと言っても過言ではありません。いずれも、静的記憶期間(static storage duraiton)の変数であり、プログラム内でそれぞれの変数についてただ一つだけ存在し、プログラムの開始から終了まで増えることも減ることもありません。

つまり、staticローカル変数を使う限り、同じ関数を使えば、その関数内のstaticローカル変数も常に同じ存在となります。関数の呼び出し事に異なる変数であることが求められるのであれば、自動変数やスレッドローカル変数を使う以外に方法はありません(スレッドローカル変数はマルチスレッド環境でのみ意味があるのでこの後は言及しません)。

間違いは関数が終了しても確保された領域を使いたいからstaticを使うという所です。確かにstaticを使っても確保された領域が削除されずに関数終了後も参照することができますが、どんな呼び出しの時も常に同じ領域を使うことになることから、関数が二回以上呼ばれる場合を考慮する必要があります。再帰関数に関係なく、この点を考慮しなければ、staticローカル変数の領域を関数外で使うことは思わぬバグになります。

C

1#include <stdio.h> 2#include <string.h> 3char *true_or_false(int x) 4{ 5 static char str[10] = ""; 6 if (x) 7 strcpy(str, "true"); 8 else 9 strcpy(str, "false"); 10 return str; 11} 12int main(void) 13{ 14 char *ans0 = true_or_false(0); 15 printf("%s\n", ans0); 16 char *ans1 = true_or_false(1); 17 printf("%s, %s\n", ans0, ans1); 18 return 0; 19}

ans0について何もしていないのに、二回目のtrue_or_false()の呼び出しの後はその中身は変わってしまっています。

別の物として扱いたいなら動的に確保する必要がある。

これを防ぐには関数が呼び出されるときに動的に領域を確保するしかありません。

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4char *true_or_false(int x) 5{ 6 char *str = (char *)malloc(10); 7 if (str == NULL) 8 abort(); 9 if (x) 10 strcpy(str, "true"); 11 else 12 strcpy(str, "false"); 13 return str; 14} 15int main(void) 16{ 17 char *ans0 = true_or_false(0); 18 printf("%s\n", ans0); 19 char *ans1 = true_or_false(1); 20 printf("%s, %s\n", ans0, ans1); 21 free(ans0); 22 free(ans1); 23 return 0; 24}

今度はans0は二回目呼び出しで影響を受けません。一回目の二回目で確保される領域が異なるからです。ただ、malloc()等で動的に領域を確保した場合、確保失敗時の処理の追加やfree()での解放が必要になります。

この方法を採用しているライブラリではfree()ではなくを専用の関数を用意している場合があります。たとえば、libcurlでは、curl_easy_escape()等で動的に確保された文字列はcurl_free()で解放する事になっています。

領域は呼び出し側で用意する。

慣れない内はメモリ管理は面倒です。メモリリークも注意しなくてはなりません。では、それ以外の方法はないのかというと、発想を変えて、関数側が領域を用意するのではなく、呼び出し側が領域を用意するというのがあります。

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4char *true_or_false(int x, char *str, size_t n) 5{ 6 if (x) 7 strncpy(str, "true", n); 8 else 9 strncpy(str, "false", n); 10 str[n - 1] = '\0'; // 必ずnull終端になるようにする。 11 return str; 12} 13int main(void) 14{ 15 char ans0[10]; 16 true_or_false(0, ans0, 10); 17 printf("%s\n", ans0); 18 char ans1[10]; 19 true_or_false(1, ans1, 10); 20 printf("%s, %s\n", ans0, ans1); 21 return 0; 22}

こちらの場合は呼び出し側が領域を完全に管理しているので、呼び出し側で自動変数をつかうという手段が使えます。ただ、大きさなどの情報も一緒に渡してバッファオーバーランしないように注意が必要です。

上の実装では確保された領域が小さかったら途中までしか入らないとしていますが、ライブラリ等では必要な大きさを返す仕組みを持つ物もあります。返り値にしたり、サイズ部分をポインタにしたり等で実装が可能です。Win32 APIの多くの関数がこの方法を採用しています。

[C++] スマートポインタで返す。

malloc()等を用いた問題はメモリ管理の猥雑さでした。それを少しだけ緩和してくれるのがスマートポインタです(GCのような完全な管理を期待してはいけません)。

C++

1#include <cstdio> 2#include <cstring> 3#include <memory> 4std::unique_ptr<char[]> true_or_false(int x) 5{ 6 std::unique_ptr<char[]> str = std::make_unique<char[]>(10); 7 if (x) 8 strcpy(str.get(), "true"); 9 else 10 strcpy(str.get(), "false"); 11 return str; 12} 13int main(void) 14{ 15 auto ans0 = true_or_false(0); 16 printf("%s\n", ans0.get()); 17 auto ans1 = true_or_false(1); 18 printf("%s, %s\n", ans0.get(), ans1.get()); 19 return 0; 20}

違いがわかりやすいようにレガシーなCの関数はそのままでスマートポインタを使ってみました。この場合はfree()は必要なく、ローカル変数ans0ans1の寿命が消えると自動的にstd::make_unique()で確保された領域も削除されます。

[C++] 右辺値参照としてムーブする。

最後に右辺値参照を使う方法です。

C++

1#include <iostream> 2#include <string> 3#include <utility> 4std::string true_or_false(int x) 5{ 6 std::string str; 7 if (x) 8 str = "true"; 9 else 10 str = "false"; 11 return std::move(str); 12} 13int main(void) 14{ 15 auto ans0 = true_or_false(0); 16 std::cout << ans0 << std::endl; 17 auto ans1 = true_or_false(1); 18 std::cout << ans0 << ", " << ans1 << std::endl; 19 return 0; 20}

右辺値参照を使わずに値で返すという方法もあります。しかし、この場合は大きなメモリコピーが発生するため、場合によってはパフォーマンスに重大な影響を与えます。そこで、std::move()を用いて右辺値参照として返すようにします。例え文字列が大きくなっても、関数から戻ってきて代入(実際はムーブ)されるところの速度は変わりません。

上のコードですが、std::move()を外した方が効率がいいです。自動ローカル変数をそのまま返す場合、その変数が戻り値の領域に初めからセットされるからです。戻り値は右辺値ですので、呼び出し側が対応していれば、右辺値参照によるムーブセマンティクスによって、値のムーブになります。(詳しくはChironianさんのコメント参照)

C/C++で実装することをやめる。

暴論を言うと、GCがある言語を使えば、そんな悩みはしなくて済みます。どうしてもC/C++でないと嫌だというのであれば、Boehm GC等を使うという方法も一応あります(Crystalもこのライブラリ使っているし、そこそこいけているんじゃないのかなー、使ったことないけど)。


ということで、まずは安易にstaticローカル変数を使うという選択肢を外して下さい。staticローカル変数は結構扱いが難しい(というより、使う場面が限られている)ものです。面倒でもCであれば動的領域確保や呼び出しが領域を用意するという手段を取って下さい。C++であれば、スマートポインタやムーブセマンティクスを活用すると良いでしょう。

投稿2018/03/05 14:25

編集2018/03/05 22:06
raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2018/03/05 14:28

CrystalだけでなくCRubyもBoehm GCが入っている環境なので、CRuby用のC拡張を書いていて、「変数を確保して使って、あとは知らない」という、C言語らしくないメモリ管理で楽をできた記憶があります。
raccy

2018/03/05 14:37

CRubyもBoehm GCでしたっけ?バージョンが上がる度に世代別やインクリメントをゴニョゴニョ実装していたりするのでCRubyは独自で作っていると思ったんですが。
maisumakun

2018/03/05 14:41

もとはそうだった、ということですね…ちょっと失礼しました。
strike1217

2018/03/05 14:44

ほぉぉ・・・ そもそもstatic変数を「関数が終了時に値が消えないから」という理由で使うべきではないんですね!! ふむーーー。 なるほど!! では、やはりmaisumakunさんの仰るとおり、std::unique_ptrをreturnさせる場合は、static かつ get()を使用するのではなく、std::unique_ptrを返り値の型とした方が良いわけですね。
strike1217

2018/03/05 14:48

「関数の呼び出し事に異なる変数であることが求められるのであれば、自動変数やスレッドローカル変数を使う以外に方法はありません」 ああ!!やはりC++でもstaticの非共有化は不可能なのですね。 どうしても、staticを使いたい場合は、回避策を自分で作るしか無さそうですね。
raccy

2018/03/05 14:59

絶対に使うなと言うわけではありませんが、標準関数の`strerror()`のようにもう一回呼び出すと違う物に置きかわっているかもよって言う扱いにくい関数になってしまうと言うことです(この関数がstaticローカル変数を使っているとは限りませんが)。`malloc()`が高コストで、メモリが潤沢で無かった時代では良い方法だったかも知れませんが、マルチスレッドなどを考えると、失敗した実装と言わざるを得ません。
strike1217

2018/03/05 15:20

GCC拡張か何かに、静的記憶期間に配置される文字列リテラルを共有、共有しないを指定できる・・・みたいな情報を聞いたことがあるので、もしかしたらstaticの非共有化もできるのでは??と思ったのですが、 できないようなので諦めることにしました。 自分で作るか、おとなしくstd::unique_ptrを返り値の型にします。
Chironian

2018/03/05 18:16 編集

> 自動ローカル変数をそのまま返す場合、その型が対応していれば、右辺値参照によるムーブセマンティクスによって、値のコピーではなく、値のムーブになります。(あってますよね?ちょっと不安・・・-д-;) あ、間違ってます。 returnする非staticなローカル変数が1つに決まっている時は、NRVOやRVOにより最初から戻り値用の領域へコンストラクトされます。 つまりムーブではありません。コピーの省略(Copy elision)と呼ばれるようです。 更にC++17ではNRVOやRVOによるコピーの省略が保証されるようになったそうです。 http://faithandbrave.hateblo.jp/entry/2017/01/24/161342 因みに、ムーブは大抵の場合メンバ変数をコピーしますから、コピー自体が省略される場合より効率は落ちます。だからと思いますが、clangはNRVOが機能するケースでstd::moveを付けると警告されます。
raccy

2018/03/05 22:02

> Chironrianさん ここら辺、C++17とかもまだ調べて無くてよくわかってなかったのですが、なんとなくよくわかりました。そっか、戻り値の所に書いちゃって、そっちの先で単純な代入とかだったら、ムーブになったりするって事なのかー。ムーブに回もされるのは無駄っちゃ無駄ですしね。
strike1217

2018/03/05 22:37

最適化を施した場合は、コピーの省略が行われる・・・ということでしょうか??
strike1217

2018/03/05 22:57

ん?RVOって最適化しなくても発動するんですかね? 普通にコンパイルしても「コピーの省略」が行われるんですか?
Chironian

2018/03/06 02:05

raccyさん。 > そっちの先で単純な代入とかだったら、ムーブになったりするって事なのかー。 ですね。この辺、ややこしいですよね。 代入構文(node aNode; aNode=test_func();)なら、ムーブ代入演算子があればムーブになります。 初期化構文(node aNode=test_func();)の時は、aNode上に直接コンストラクトされるようです。(gcc, clangの場合。msvcは未確認) strike1217さん。 > 普通にコンパイルしても「コピーの省略」が行われるんですか? 少なくともC++17より前の規格の場合は、コピーの省略をするかしないかは処理系依存です。 因みに、wandboxで確認した時はgcc, clang共に最適化無しでもコピーは省略されていました。 ↓でそこそこ詳しく解説してますので、よかったら参考にしてみて下さい。 https://theolizer.com/cpp-school1/cpp-school1-37/
strike1217

2018/03/06 04:45

なるほど!! わかりました。
guest

0

こんにちは。

関数から戻っても有効な値を返却したいなら、普通にオブジェクトをコピーして返却すればよいですよ。
呼び出す度に同じメモリを返却するのなら元の関数で良いですし、呼び出す度に異なるメモリを返却するならコピーが必要ですから。

C++

1#include <iostream> 2#include <string> 3 4std::string test(int wd) 5{ 6 static char const* wstr[]{ 7 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" 8 }; 9 return ((wd >= 0 && wd < 7) ? wstr[wd] : NULL); 10} 11 12int main() 13{ 14 auto weekday0 = test(0); 15 std::cout << "weekday0=" << weekday0 << " : " << &weekday0 << "\n"; 16 auto weekday1 = test(1); 17 std::cout << "weekday1=" << weekday1 << " : " << &weekday1 << "\n"; 18}

実行結果:

weekday0=Sunday : 0x7ffdc1db7e80 weekday1=Monday : 0x7ffdc1db7e60

wandbox

投稿2018/03/05 08:54

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2018/03/05 09:12 編集

ポインタを使わない方法を取るのですね。 しかし、どうしたもポインタを返さなくてはならない場合はどうするのでしょうか?? 例えば、質問中に出ている。 return ptr.get(); みたいに・・・ ヒープ領域のアドレスを返す場合ですね。
strike1217

2018/03/05 09:17

std::unique_ptr::getを返す場合、どうしてもstaticにしないとおかしくなります。 maisumakunさんの言うとおり、戻り型の方をstd::unique_ptrにすれば良いのですが・・・ その方法を取りたくない場合・・・どうするか・・・ということですね。
Chironian

2018/03/05 10:40

関数が返却するメモリを誰がいつ開放するかの問題です。それを決めないと、そもそも検討のしようがないですよ。 誰かが使っている間は開放せず、使われていないもののみ開放するような仕組みを設ける場合、使っているのか使っていないのかを管理する必要があります。 そのスマートな方法の1つが、std::unique_ptrです。これは誰か1つだけが管理できます。複数の場所から使われていることを管理したい場合はstd::shared_ptrですね。 しかし、生ポインタにはそのような管理機能はありません。もし、どうしても生ポインタで返却したい場合は、関数が返却したメモリを全部開放しても良いタイミングを決定できれば解はあります。関数側で獲得したメモリをグローバルなstd::vector<std::unique_ptr<型>>に登録しておき、開放タイミングが来たらclear()するわけです。 いずれにせよ、メモリの借り方と返し方について両方の要件を明確にしましょう。それができれば解がある、もしくは、解が存在しないことを決定できると思います。現在は返し方の要件が不明確なので結論を得ることは不可能です。
strike1217

2018/03/05 10:56

「関数側で獲得したメモリをグローバルなstd::vector<std::unique_ptr<型>>に登録しておき、開放タイミングが来たらclear()するわけです。」 ああ〜〜やはりこの方法ですよね。 自分もこれに近い考えです。
strike1217

2018/03/05 11:08

static std::unique_ptrが解放されるのは、main()終了時のデストラクタですね。 上記の質問のコードにもありますね。
Chironian

2018/03/05 13:26

> ああ〜〜やはりこの方法ですよね。 ということは、全てを一度に開放できるタイミングが存在するのですね。 であれば、簡単に作れますし、特に問題はないかと。 https://wandbox.org/permlink/Jzu2aGjV0KxUjSfE
strike1217

2018/03/05 13:36

なるほど! 今、思ったのですが、配列でなくても、std::stackを使って再帰関数呼び出し前と後でpush pop してstatic std::uniqueを復活させるという方法も思いつきました。 まぁ、方法は色々あるのでいいんですが・・・ ところで、staticの非共有化自体の方はC++で可能なんですか? HogeAnimalLoverさんのところでもコメントしたのですが・・・
Chironian

2018/03/05 13:59

> ところで、staticの非共有化自体の方はC++で可能なんですか? 必要なstaticローカル変数の数に上限があるなら、配列で確保して先頭から順に使う手はあります。 https://wandbox.org/permlink/QYtgTF7KASfzLNTZ しかし、メモリの許す限り使いたい場合は不可能です。静的なメモリ(static変数含む)はコンパイル時にサイズが決まっている必要がありますから。 メモリの許す限り使いたい時は動的なメモリ(通常はヒープ)を使います。
Chironian

2018/03/05 14:07 編集

> std::stackを使って再帰関数呼び出し前と後でpush pop してstatic std::uniqueを復活させるという方法も思いつきました。 これはダメでは? popしたstd::unique_ptrがスコープから外れた時点でポインタの先のメモリが開放されますから。(次にpopしたものを上書きした時です。)
strike1217

2018/03/05 14:25

なるほど! わかりました。 C/C++ではstaticの非共有化は不可能で、自分で作るしかないわけですね。 ケイロニアンさんの方法を採用します。
guest

0

staticを付与しない場合は、関数終了時に変数が利用できなくなります。次の関数呼び出しまで値を保存し続けることはありません(ローカル変数の性質上当たり前)。

staticを付与した場合は、関数終了後も値を保存します。ただし、再帰関数等の場合、値を共有するので、値を書き換えるとほかにも影響が出る(これも当たり前)

結局、どのようなことがしたいのでしょうか?

共有する変数を作りたいのか、共有しない変数をつくりたいのか、そもそも目的が見えません。ちなみにグローバル変数を使うとか呼び出し元の変数で状態管理する(引数で状態を受け取る)といった運用も考えられます。

投稿2018/03/05 12:23

HogeAnimalLover

総合スコア4830

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2018/03/05 12:30

超簡単に言いますと、 静的記憶期間に配置しつつ、値を共有しない変数の作成ですね。 仰る通りで、 staticなしー>関数終了時に変数は利用できなくなる。共有はされない。 staticありー>関数終了時に消えないが、共有される。 つまり、関数終了時に消えないで、共有されないような変数です。 staticの非共有化です。 そのようなことは可能でしょうか??という質問です。
strike1217

2018/03/05 12:41 編集

C言語では不可能であることはmaisumakunさんが言っているのですが、C++にそのような機能がありそうな感じはしますが・・・
guest

0

文字列の方
曜日の名前を取得するだけならconst char*貰えばいいですね。
書き換えたいのなら貰った側がコピーすればいいのです。

c++

1const char *test(int wd){ 2 static const char wstr[][10]{ 3 "Sunday", "Monday" .... 4 }; 5 return ((wd >= 0 && wd < 7) ? wstr[wd] : NULL); 6}

nodeの方
ポインタいりますか?

#include<iostream> #include<string> #include <utility> struct node{ std::string name; int no; }; struct node test_func(){ std::string name; std::cin >> name; int no = 10; return node{std::move(name), no}; } int main(){ node str = test_func(); node wtr = test_func(); std::cout << str.name << std::endl; std::cout << str.no << std::endl; std::cout << wtr.name << std::endl; std::cout << wtr.no << std::endl; return 0; }

ポインタをできるだけ排斥してSTLやスマートポインタ、参照を使うのがモダンなC++だと思います。

投稿2018/03/05 14:59

編集2018/03/05 15:02
asm

総合スコア15147

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

普通にAUTO変数にして初期化しましょう

って、関数が終わっても破棄されたら困るのか。
なら、呼び出されるたびに malloc で領域確保して初期化するってことぐらいだけど、
そうなければならないってところに設計が悪い気がしますねー
#freeするタイミングはどーする?

投稿2018/03/05 08:04

編集2018/03/05 08:08
y_waiwai

総合スコア87749

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2018/03/05 08:10

「関数が終わっても破棄されたら困るのか。」 そうですね。 staticは付けないとダメです。
y_waiwai

2018/03/05 08:13

しかし、提示されている例だとstaticのままでも不具合はないですな。そのテーブルは変更されないんでしょ?
strike1217

2018/03/05 08:14

質問中のコードは問題ありません。 が・・・例えば、再帰関数を自分で作った場合に問題になる・・・ということです。
y_waiwai

2018/03/05 08:17

なら、回答にあるようにmallocでヒープ確保していく手だけど、これ、完全に設計が悪いですぜ。 考え直したほうがよろしいかと。
strike1217

2018/03/05 08:22

ああ! wstrのような配列をmallocにすれば、staticじゃなくてもreturnできるよ! って言いたいんですね。 んー。それもそうですね。
y_waiwai

2018/03/05 08:28

まあ、どのみち関数を再帰するばあいは無限に再起しないように注意しましょう、ってことでw
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問