質問するログイン新規登録

Q&A

解決済

6回答

589閲覧

C++ スコープ外になったアドレスの有効性について

apa

総合スコア76

C++

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

0グッド

0クリップ

投稿2026/02/08 03:41

0

0

下記のように一度変数を保持させたあとにスコープ外にいった場合
x.xの値を参照するのは危険な行為になるのでしょうか?

c++

1class X 2{ 3public: 4 int* x; 5}; 6 7using std::cout; 8 9int main() 10{ 11 X x; 12 { 13 int n = 111; 14 x.x = &n; 15 16 cout << x.x << "\n"; 17 } 18 19 cout << x.x << "\n"; 20 21 getchar(); 22}

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

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

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

guest

回答6

0

int n = 111; ラインは、変数を一時的なスタックにアサインします。この変数は、仮想メモリ上のアドレスを持ち、ポインターの値として、x.xに保存されます。一方、このスタックは {で始まるラインから、で終わるラインまでが有効で、ライン19ではもう存在しません。そのため、ライン19は意味を持たないポインターの値をプリントすることになってしまいます。コンパイラーは間違った使い方をしていると警告を出すのでしょう。

投稿2026/02/11 00:18

mmaeda

総合スコア273

0

危険だと思いますが、実際のところ、どうなるか、コンパイラ次第と思いますが、
スタックメモリ上のローカル変数ですが、関数内に新たなローカル変数があるからといって、新たにスタックメモリを確保するのではなく、関数の頭で一括で確保し関数を抜けるところで一括で返します。
よって実際のところ、スコープを抜けたところでも関数を抜けるまでは、そのエリアは確保されていますが仕様ではありませんので保障はありません。で、一応動く。
コンパイラ次第なんて危なくて使う気しないと思います。一度大丈夫でも最適化などで変わってしまう可能性もある。

投稿2026/02/09 23:12

tmp

総合スコア366

0

Cppcheck - A tool for static C/C++ code analysis という、静的解析(static analysis)を行うツールがあります。これで質問文にあるソースコードを解析してみると、以下の様なエラー(invalidLifetime)が報告されます。

sh

1$ cppcheck invalid_life_time.cc 2 3invalid_life_time.cc:19:11: error: Using object that points to local variable 'n' that is out of scope. [invalidLifetime] 4 cout << x.x << "\n"; 5 ^ 6invalid_life_time.cc:16:11: note: Address of variable taken here. 7 x.x = &n; 8 ^ 9invalid_life_time.cc:15:9: note: Variable created here. 10 int n = 111; 11 ^

また、スコープ外から参照する対象をポインタアドレスではなく実体の値に変更してから gcc の Address Sanitizer でチェックすると、stack-use-after-scope | Microsoft Learnというエラーが検出されることが判ります。

c++

1 cout << *x.x << "\n"; 2 //cout << x.x << "\n"; 3 getchar();

sh

1$ g++ --version 2g++ (Ubuntu 15.2.0-4ubuntu4) 15.2.0 3 4$ g++ -fsanitize=address -Wall -Wextra -g invalid_life_time.cc -o invalid_life_time && ../invalid_life_time 5 6==569735==ERROR: AddressSanitizer: stack-use-after-scope on address 0x72b421200030 at pc 0x5770c8ae03f8 bp 0x7fffb42ba030 sp 0x7fffb42ba020 7READ of size 4 at 0x72b421200030 thread T0 8 #0 0x5770c8ae03f7 in main invalid_life_time.cc:19 9 #1 0x76b42342a574 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 10 #2 0x76b42342a627 in __libc_start_main_impl ../csu/libc-start.c:360 11 #3 0x5770c8ae01e4 in _start (invalid_life_time+0x11e4) (BuildId: 282ae10eecaab9190cb0f4ec5c1d7b5458b25ac3) 12 13Address 0x72b421200030 is located in stack of thread T0 at offset 48 in frame 14 #0 0x5770c8ae02b8 in main invalid_life_time.cc:12 15 16 This frame has 2 object(s): 17 [48, 52) 'n' (line 15) <== Memory access at offset 48 is inside this variable 18 [64, 72) 'x' (line 13) 19HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork 20 (longjmp and C++ exceptions *are* supported) 21SUMMARY: AddressSanitizer: stack-use-after-scope invalid_life_time.cc:19 in main 22

投稿2026/02/08 06:04

melian

総合スコア21695

apa

2026/02/08 13:27

返信ありがとうございます。 そちらのツールは存じ上げていないのですが エラーが出たということは構文的に正しくないという認識ですかね?
melian

2026/02/08 14:10

構文的には問題ないのですけれども、潜在的なリスク(stack-use-after-scope)が存在します。回答に示した Microsoft Learn の記事の "Example 4 - temporaries" を参照してみてください。
guest

0

寿命を終えたオブジェクトを指していたポインタは無効 (invalid) であり、無効なポインタを使用した効果は未定義です。 たとえポインタが指す先のオブジェクトにアクセスしなくてもポインタを使おうとすること自体が未定義の効果であると解されます。

投稿2026/02/08 05:25

編集2026/02/08 05:32
SaitoAtsushi

総合スコア5852

apa

2026/02/08 14:12 編集

返信ありがとうございます。 かなり使い勝手が悪い印象ですね... 以前下記のようなコードを書いていましたが、 一時オブジェクトを登録する際のデータの受け渡しが非常に難解な気がします この場合だと一時オブジェクトのdataがhit()処理後に未定義になるから registで登録されているけど未定義ってことですもんね struct damagedata{ actor* attacker; actor* deffencer; float damage; } class dmagemanager { void regist(damagedeta& data) {Data.Add(data)} TArray<damagedata> Data; } void Sphere::hit() { damagedata data; data.attacker = attacker; data.deffencer = deffencer; data.damage = 999; damagemanager* mgr = getter(); mgr->regist(data); }
Manabu

2026/02/09 00:56 編集

始めから値渡しでは駄目なのでしょうか void regist(damagedeta data) {Data.Add(data)}; TArray<damagedata> Data; void Sphere::hit() { damagemanager* mgr = getter(); mgr->regist( damagedata{attacker,defencer,999} ); }; 安全性が担保される範囲でなら始めからポインタでもいいでしょう void Sphere::hit() { struct damagedata *data=new damagedata; data->attacker = attacker; data->deffencer = deffencer; data->damage = 999; damagemanager* mgr = getter(); mgr->regist(*data); } スコープ外に移るということはスタックを解放して呼び出し元のアドレスに戻るということなのでdataに対する参照がダングリングと化します スタックなので解放された領域には新しい関数のローカル変数などがセットされます この状態のdataは不定値と変わらないので大変危険です
apa

2026/02/14 09:00

mgr->regist( damagedata{attacker,defencer,999} ); }; 上記でも問題なさそうですね 強いて言えば渡したいパラメータが増えるほど横にながくなってしまうくらいでしょう? またポインタでnewしておくのも一つの解決案ですね 業務で普通に下記のような書き方していたので気を付けないとですね... damagedata data; data.attacker = attacker; data.deffencer = deffencer; data.damage = 999; damagemanager* mgr = getter(); mgr->regist(data);
guest

0

xは初期化しておいたほうがよいでしょう。

X x = {};

n自体はスコープを外れると参照できなくなりますが、その111が入っているアドレスはx.xに代入されるので、問題無いと思います。

投稿2026/02/08 04:33

hiroki-o

総合スコア1747

0

自己解決

たくさんのコメントありがとうございました。
コンパイルエラーや動作に問題がありませんでしたので気にしていませんでしたが
よろしくない書き方ということが分かりました。

投稿2026/02/14 09:02

apa

総合スコア76

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.29%

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

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

質問する

関連した質問