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

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

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

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

Q&A

解決済

2回答

929閲覧

c++ ポインタとメモリの使い方に関して

momonokisansiro

総合スコア2

C++

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

0グッド

0クリップ

投稿2021/10/30 12:39

前提・実現したいこと

c++初学者です。
独学での知識しかない為、暖かい目で見ていただけると幸いです。
ポインタ、配列を使用する学習をしています。

実現したいことは、不動小数点の数値を入力して単位変換できるようなプログラムを作成したいと思っています。
できる限りメモリを使わないようにコードを組みたいと思っています。

発生している問題・エラーメッセージ

開発環境:x-code

main関数の"return 0"を返す直前で以下エラーメッセージが発生します。
何が理由なのかを知りたいと思っています。
エラー調査を進める対処方法のアドバイスも頂けると嬉しいです。

エラーメッセージ
Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)

該当のソースコード

#include <iostream> using namespace std; void power(double *ptr){ int number; double *p; cout << "元の単位を選択してください [1]N [2]kgf:"; cin >> number; switch (number) { case 1: p = ptr; *(p+1) = (*ptr)*0.101972; printf("[元の値] %f N\n", *p); printf("[1] %f kgf\n", *(p+1)); break; case 2: p = ptr; *(p+1) = (*ptr)*9.80665; printf("[元の値] %f kgf\n", *p); printf("[1] %f N\n", *(p+1)); break; default: break; } } void pressure(double *ptr){ int number; double *p; cout << "元の単位を選択してください [1]N/m^2 [2]N/cm^2 [3]N/mm^2 [4]kgf/m^2 [5]kgf/cm^2 [6]kgf/mm^2 [7]Pa [8]MPa:"; cin >> number; switch (number) { case 1: p = ptr; *(p+1) = (*ptr)*0.0001; *(p+2) = (*ptr)*0.000001; *(p+3) = (*ptr)*0.10197; *(p+4) = (*ptr)*0.0000102; *(p+5) = (*ptr)*0.000000102; *(p+6) = (*ptr)*1; *(p+7) = (*ptr)*0.000001; //printf("[元の値] %d doubleサイズ:", sizeof(double)); printf("[元の値] %p N/m^2\n", p); printf("[1] %p N/cm^2\n", (p+1)); printf("[2] %p N/mm^2\n", (p+2)); printf("[3] %p kgf/m^2\n", (p+3)); printf("[4] %p kgf/cm^2\n", (p+4)); printf("[5] %p kgf/mm^2\n", (p+5)); printf("[6] %p Pa\n", (p+6)); printf("[7] %p MPa\n", (p+7)); printf("[元の値] %f N/m^2\n", *p); printf("[1] %f N/cm^2\n", *(p+1)); printf("[2] %f N/mm^2\n", *(p+2)); printf("[3] %f kgf/m^2\n", *(p+3)); printf("[4] %f kgf/cm^2\n", *(p+4)); printf("[5] %f kgf/mm^2\n", *(p+5)); printf("[6] %f Pa\n", *(p+6)); printf("[7] %f MPa\n", *(p+7)); break; case 2: break; case 3: break; case 4: break; case 5: break; case 6: break; case 7: break; case 8: break; default: break; } } /* void torque(double *ptr){ int number; double *p; cout << "元の単位を選択してください [1]N/m^2 [2]N/cm^2 [3]N/mm^2 [4]Pa [5]MPa:"; cin >> number; switch (number) { case 1: break; case 2: break; case 3: break; case 4: break; case 5: break; default: break; } } */ int main() { double value; int num; cout << "値を入力してください:"; cin >> value; cout << "変換する単位系を選択してください [1]力 [2]圧力 [3]トルク:"; cin >> num; switch (num) { case 1: power(&value); break; case 2: pressure(&value); break; case 3: // torque(&value); break; default: break; } return 0; }

試したこと

入力した数値をポインタで関数(power,pressure等)に渡し、変換結果を表示してもらうようにしたいです。
power関数ではエラーなしで実行できたのですが、pressure関数ではエラーが起きたので原因を知りたいです。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

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

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

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

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

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

guest

回答2

0

gcc には AddressSanitizer という機能があります。

man gcc(1)

-fsanitize=address

Enable AddressSanitizer, a fast memory error detector. Memory access instructions are instrumented to detect out-of-bounds and use-after-free bugs. The option enables -fsanitize-address-use-after-scope. See https://github.com/google/sanitizers/wiki/AddressSanitizer for more details.

対象のソースコードを convert_unit.cc としてコンパイル、実行してみます。

bash

1$ g++ --version 2g++ (Ubuntu 11.1.0-1ubuntu1~21.04) 11.1.0 3$ g++ -fsanitize=address -Wall -Wextra -g convert_unit.cc -o convert_unit 4$ printf '10\n1\n1\n' | ./convert_unit 5値を入力してください:変換する単位系を選択してください [1][2]圧力 [3]トルク:元の単位を選択してください [1]N [2]kgf: 6================================================================= 7==524401==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffcf4317df8 at pc 0x555de79884e0 bp 0x7ffcf4317cf0 sp 0x7ffcf4317ce0 8WRITE of size 8 at 0x7ffcf4317df8 thread T0 9 #0 0x555de79884df in power(double*) convert_unit.cc:15 10 #1 0x555de7988ee6 in main convert_unit.cc:125 11 #2 0x7f346f001564 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x28564) 12 #3 0x555de79882ad in _start (convert_unit+0x22ad) 13 14Address 0x7ffcf4317df8 is located in stack of thread T0 at offset 72 in frame 15 #0 0x555de7988daa in main convert_unit.cc:115 16 17 This frame has 2 object(s): 18 [48, 52) 'num' (line 117) 19 [64, 72) 'value' (line 116) <== Memory access at offset 72 overflows this variable 20HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork 21 (longjmp and C++ exceptions *are* supported) 22 23SUMMARY: AddressSanitizer: stack-buffer-overflow convert_unit.cc:15 in power(double*)

15行目(power() 関数内)は以下になります。

c++

1 *(p+1) = (*ptr)*0.101972;

double value; をグローバル変数にしたらエラーが出なくなりました。

その通りに変更してコンパイル、実行してみます。

bash

1$ printf '10\n1\n1\n' | ./convert_unit 2値を入力してください:変換する単位系を選択してください [1][2]圧力 [3]トルク:元の単位を選択してください [1]N [2]kgf: 3================================================================= 4==524505==ERROR: AddressSanitizer: global-buffer-overflow on address 0x556ad2a15948 at pc 0x556ad2a114c0 bp 0x7ffc75bc3670 sp 0x7ffc75bc3660 5WRITE of size 8 at 0x556ad2a15948 thread T0 6 #0 0x556ad2a114bf in power(double*) convert_unit.cc:17 7 #1 0x556ad2a11eba in main convert_unit.cc:126 8 #2 0x7f6ad5b13564 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x28564) 9 #3 0x556ad2a1128d in _start (convert_unit+0x228d) 10 110x556ad2a15948 is located 0 bytes to the right of global variable 'value' defined in 'convert_unit.cc:5:8' (0x556ad2a15940) of size 8 12 13SUMMARY: AddressSanitizer: global-buffer-overflow convert_unit.cc:17 in power(double*)

この場合でも同じ場所でエラー(範囲外アクセス)が発生しています。そちらで実行した場合にエラーにならなくなったのは、変数 value が確保される領域がスタックから bss セクションへ移ったためかもしれません。

修正するとすれば、最大限必要な容量を確保しておく(double value[8];)ことかと思います(power(), pressure() 関数のシグネチャも変更しておくとよいかもしれません)。

c++

1int main() { 2 double value[8]; 3 int num; 4 cout << "値を入力してください:"; 5 cin >> value[0]; 6 cout << "変換する単位系を選択してください [1]力 [2]圧力 [3]トルク:"; 7 cin >> num; 8 9 switch (num) { 10 case 1: 11 power(value); 12 break; 13 case 2: 14 pressure(value); 15 break; 16 : 17

投稿2021/10/30 18:51

編集2021/10/30 19:04
melian

総合スコア20655

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

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

momonokisansiro

2021/10/31 04:29

>melian さん ご丁寧に説明頂きありがとうございます。 ”最大限必要な容量を確保しておく(double value[8];)”の方が書き方としては良いのですね。 ちなみになのですが、修正後のコードには”double value[8];”はメイン関数内に書かれています。グローバル変数は関数のブロック外(main関数のブロックの外)に書く必要があると認識していますが違うのでしょうか?
melian

2021/10/31 06:34

どちらに置いても結果に違いはありませんが、メイン関数の中に置くほうが良いかと思いました。
momonokisansiro

2021/10/31 12:31

>melian さん わかりました。 ご連絡頂きありがとうございました。
guest

0

ベストアンサー

  • 何が理由なのかを知りたいと思っています。

main関数のローカル変数であるvalueのアドレスを渡して、そのアドレスの先に書き込んでいるのですからエラーが出て当然です。

  • power関数ではエラーなしで実行できたのですが

たまたまエラーが検出されなかっただけで、領域破壊は起きています。

  • エラー調査を進める対処方法のアドバイスも頂けると嬉しいです。

IDEのデバッガ機能を使ってデバッグしましょう。

投稿2021/10/30 13:06

ppaul

総合スコア24670

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

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

momonokisansiro

2021/10/30 14:40

早速ご親切にご回答いただきありがとうございます。 double value; をグローバル変数にしたらエラーが出なくなりました。 IDEのデバッガ機能は一度どんなものがあるか調査してみたいと思います。 ネットで相談するのが初めてでしたので緊張していましたが、回答がとても早くてびっくりしました。 ご対応いただきありがとうございました。
yumetodo

2021/11/17 22:53

まあ下で言われていますがグローバル変数であるかは関係ないですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問