トップページ > 記事閲覧
配列で別のアドレスを示してしまう
名前:BayLeaf 日時: 2017/10/16 04:59

いつもお世話になっております。 以下のようなクラスによる配列で取得しているのですが、 SHandle[MAXMUSIC].titleで配列の要素数(MAXMUSIC)が大きくなる(経験上6より大きい場合)と 文字化け or 別のアドレスを示してしまうことがあります。 原因はわからないのですが、同じものを再度読み込むと直ります。 配列で定義すると別のアドレスを示して文字化けやエラーを起こすことが Windows版でやっていた頃からもあったので、端末自体のメモリの問題ではないと考えています。 そのため最近まで要素数の多い配列を使うことをできるだけ控えていたのですが、 クラスや構造体を多用するようになってからは必要不可欠になっているのでどうにかしたいです。 備考 ・非同期関数の使用有無は関係なかったです ・new や malloc といったメモリの動的確保はほぼしていません(しても変わりませんでした) ・構造体で同様の処理に変えても同じ結果になりました ・音声が絡むときにのみ発生します(画像などほかの処理は問題ないです) /* Headerファイル */ const int MAXMUSIC = 10; class Music{ public: const char *title; // 曲タイトル int id; // LoadSoundMemで取得した識別番号     const char *text; // 説明 }; extern Music SHandle[]; // 曲(cppでも宣言する)
メンテ

Page: 1 |

Re: 配列で別のアドレスを示してしまう ( No.1 )
名前:管理人 日時:2017/10/17 00:30

> extern Music SHandle[]; // 曲(cppでも宣言する) cpp でも宣言する、とありますが、cpp の方ではどのように記述されているのでしょうか? もし cpp でも Music SHandle[]; と記述されていましたら SHandle は長さ 0 の配列となってしまいますので、 この配列を使用してしまうと必ずメモリの不正なアクセスになってしまいます この場合 cpp に方に記述すべきは Music SHandle[MAXMUSIC]; となります もし既にこのように記述されていたらすみません
メンテ
Re: 配列で別のアドレスを示してしまう ( No.2 )
名前:BayLeaf 日時:2017/10/17 12:20

> cpp でも宣言する、とありますが、cpp の方ではどのように記述されているのでしょうか? 先程確認したのですが、 Music SHandle[MAXMUSIC]; でした。 ヘッダーを上記と同じ記述にしても変わらなかったです。 仕方なくもう一度同じデータを読み込む方法で行なっているのですが、 この方法だと前に読み込んだデータが何らかの影響で別のアドレス上に残っているようで 同じハンドルに代入しているのに止める処理を行なっても(前に読み込んだ分の曲の)再生が止まらないという不具合が発生しています。 再度読み込まない方法でエラーが起きた際の出力状況を書いておきます。(○○は正常に読み込まれたファイル) for(i=0;i<MAXMUSIC;i++){ DrawFormatString(0,i*GetFontSize(),-1,"%d %d %s",i,Shandle[i].id,SHandle[i].title) } // エラー 0 201330552 ○○ 1 201457689 ○○ 2 201523202 ○○ 3 201588739 ○○ 4 201654276 ○○ 5 201719813 ○○ 6 201785350 ple.wav(なぜかファイル名の一部が表示された) 7 201850887 (以降タイトルは表示されない) 8 201916424 9 201981961 // 本来表示すべき内容 0 201330552 ○○ 1 201457689 ○○ 2 201523202 ○○ 3 201588739 ○○ 4 201654276 ○○ 5 201719813 ○○ 6 201785350 ○○ 7 201850887 ○○ 8 201916424 ○○ 9 -1 -1([MAXMUSIC-1] は NULL を渡している) こうして見てみると、6以降のtitleが思うように表示されていなかったです。 個人的に気になることは6あたりのtitleでassets内のサウンドファイル名の一部を取得してしまっているということです。 6あたりのtitle文字列は実行するたびに微妙に位置が変わります(もちろん通常は表示されるはずのない文字列ですが...)。 また、NULLを渡しているのに-1でない値を返しているということもわかりました。 もしかして、バッファオーバーフローが起きている可能性があるのかなと思いつつも 実装の仕方が分からないので確認手段がないです。 ここまで書いていて思い出したのですが、2,3年前に文字列でも似たような例がありました。 /* cpp内で記述 */ static const char *StringTotalDataLib[] = { "/* ----- 全般 ----- */", "総起動回数", "カラー", "/* ---- ストーリー ---- */", "進み具合", "倒した敵の総数", "/* ---- ミニゲーム ---- */", "総合プレイ回数", "最後", NULL }; こちらを表示すると、やはり6あたりから表示がおかしくなることがありました。 (いつだかのバージョンアップで直ったので現在は問題ないです)
メンテ
Re: 配列で別のアドレスを示してしまう ( No.3 )
名前:管理人 日時:2017/10/18 00:52

> 仕方なくもう一度同じデータを読み込む方法で行なっているのですが、 > この方法だと前に読み込んだデータが何らかの影響で別のアドレス上に残っているようで > 同じハンドルに代入しているのに止める処理を行なっても(前に読み込んだ分の曲の)再生が止まらないという不具合が発生しています。 サウンドハンドルを代入した変数 id に新たに読み込んだファイルのサウンドハンドルを代入しても、 もともと id に代入されていたサウンドハンドルのデータが解放されるわけではありませんので、 もともと id に代入されていたサウンドハンドルのデータを解放するには新しいサウンドハンドルを代入する前に DeleteSound の引数として id を渡して呼び、サウンドハンドルのデータを削除する必要があります > 再度読み込まない方法でエラーが起きた際の出力状況を書いておきます。(○○は正常に読み込まれたファイル) メンバー変数 text は char型の配列ではなくポインタなので、この場合 text に代入したアドレスが示す 文字列の内容がずっと保持されている必要があります よろしければメンバー変数 text にアドレスを代入する処理がどのようになっているか教えていただけないでしょうか? ( ローカル変数に代入された文字列のアドレスなどの場合、内容が保持されないので処理の経過と共に内容は失われます )
メンテ
Re: 配列で別のアドレスを示してしまう ( No.4 )
名前:BayLeaf 日時:2017/10/25 19:33

> もともと id に代入されていたサウンドハンドルのデータを解放するには新しいサウンドハンドルを代入する前に > DeleteSound の引数として id を渡して呼び、サウンドハンドルのデータを削除する必要があります 今までは一度ロードしたサウンドハンドルは実行終了まで消すことをしていなかったので すっかりDeleteSoundの存在を忘れていました。ありがとうございます。 > よろしければメンバー変数 text にアドレスを代入する処理がどのようになっているか教えていただけないでしょうか? 口で説明することが難しいのでファイルごと置いておきます。よろしければご確認をお願いします。 https://drive.google.com/open?id=0B_K61x6YTJWbNmUtVm1lQjJvTU0 上記のファイルを順を追っていけば分かると思います。 因みに検証はいつも実機で行なっているのですが、アンインストールせずに更新すると要素の多い配列のアドレスが一部バグることがあります。 実機内でアプリ自体を一度アンインストールし、再インストールすることである程度正常に動くようになるのですが、 配信を行う際はアンインストールを促すのは不得手ですし直したいと思っています。 これで原因が分かれば良いのですが...
メンテ
Re: 配列で別のアドレスを示してしまう ( No.5 )
名前:管理人 日時:2017/10/26 01:12

検証用のファイルをアップしていただきありがとうございます そのままでは実行できないプログラムとの事でしたので、コンパイルエラーが発生する 箇所をコメントアウトさせていただいた上で実行したところ、問題なく 6 以降のタイトルも表示されました 存在しないファイルのパスで LoadSoundMem をしている 9 もハンドルの値が代入されるのは SetUseASyncLoadFlag(TRUE); を実行して非同期読み込みとなっているからです ( 非同期読み込みの場合はファイルが存在するかどうかのチェックも非同期で行っているので、  読み込みに失敗したかどうかは CheckHandleASyncLoad を使用して確認する必要があります ) 実行した範囲のプログラムを拝見した限りでは、気になっていた Music クラスのメンバー変数 title や text に代入されていたアドレスも文字列リテラルでしたので、特にエラーの原因と なりそうな箇所はありませんでした( for 文で int i が定義されていないのは少し気になりましたが… ) なので、アップしていただいたプログラムで試せる範囲では原因は分かりませんでした… お手数で申し訳ないのですが、No.2 のエラーを確認するために必要なファイルを頂けないでしょうか? m(_ _;m
メンテ
Re: 配列で別のアドレスを示してしまう ( No.6 )
名前:BayLeaf 日時:2017/10/26 02:12

> for 文で int i が定義されていないのは少し気になりましたが… 違うファイルで既にグローバル変数として定義しています。 (本当はファイルごとにローカル変数で定義したほうが良いでしょうけど...) > お手数で申し訳ないのですが、No.2 のエラーを確認するために必要なファイルを頂けないでしょうか? m(_ _;m 一つでも消すと色々とエラーが出るのでNativeActivity内のファイルをまとめてお渡ししておきます。 取り敢えずNo.4に使用しているファイルを入れておきました。 画像は「Graphicフォルダ→Otherフォルダ→Click_Next00.png」(次へ進む用の画像)があれば最低限のことはできると思います。 似たような別件のエラーもあるのですが、こちらはメモに載せておくのでそちらもできれば確認をお願いします。
メンテ
Re: 配列で別のアドレスを示してしまう ( No.7 )
名前:管理人 日時:2017/10/27 00:43

> 違うファイルで既にグローバル変数として定義しています。 > (本当はファイルごとにローカル変数で定義したほうが良いでしょうけど...) そうですね、例えば以下のプログラムのように i を使ったループの中で呼び出した関数の中でも i を使ったループをしていた場合に正常に動作しなくなってしまうので、 ローカル変数で定義したほうが安全です int i; void FunctionA( int num ) { for( i = 0; i < num; i++ ) { printf( "%d\n" ); } } void main() { for( i = 0; i < 100; i++ ) { FunctionA( i * 100 ); } } > 画像は「Graphicフォルダ→Otherフォルダ→Click_Next00.png」(次へ進む用の画像)があれば最低限のことはできると思います。 > 似たような別件のエラーもあるのですが、こちらはメモに載せておくのでそちらもできれば確認をお願いします。 Click_Next00.png を用意してアップしていただいたプログラムを実行してみたのですが、 Click_Next00.png をタッチした後『日付が変わったのでタイトルへ戻ります』と表示され、 「サウンドテスト」が表示されずタッチすることができません すみませんがご確認をお願いします m(_ _;m
メンテ
Re: 配列で別のアドレスを示してしまう ( No.8 )
名前:BayLeaf 日時:2017/10/27 01:24

> Click_Next00.png を用意してアップしていただいたプログラムを実行してみたのですが、 > Click_Next00.png をタッチした後『日付が変わったのでタイトルへ戻ります』と表示され、 > 「サウンドテスト」が表示されずタッチすることができません あ、すみません! 日付変更の確認をするためのプログラムのままでした。 変えておきましたのでお手数ですが再度ご確認をお願いします。 (変更箇所は「メモ」に書いてあります) > そうですね、例えば以下のプログラムのように i を使ったループの中で呼び出した関数の中でも > i を使ったループをしていた場合に正常に動作しなくなってしまうので、 > ローカル変数で定義したほうが安全です 分かりました、時間がある時に変更してみます(膨大な量の繰り返し用i,j,kがあるのでどのくらいかかるかわかりませんが...)。
メンテ
Re: 配列で別のアドレスを示してしまう ( No.9 )
名前:管理人 日時:2017/10/29 01:20

ファイルの更新ありがとうございます、手元でも文字列が正常ではなくなる現象を確認できました 原因を調べたところ、SaveData.cpp の関数 MusicSaveData の中の 994行目の fread(&SHandle[i].flag, sizeof(bool), i + 1, fp); // 読み込み こちらで SHandle の Music クラス内の title に値を上書きしてしまっているのが原因でした、 こちらは正しくは fread(&SHandle[i].flag, sizeof(bool), 1, fp); // 読み込み となります( 第三引数は第二引数のサイズのデータを何個読み込むか、という引数なので )、 同様に 1009行目の fwrite(&SHandle[i].flag, sizeof(bool), i + 1, fp); // 保存 こちらも正しくは fwrite(&SHandle[i].flag, sizeof(bool), 1, fp); // 保存 となります この変更を行うことで手元で試した限りでは現象が発生しなくなりましたので、よろしければお試しください m(_ _)m
メンテ
Re: 配列で別のアドレスを示してしまう ( No.10 )
名前:BayLeaf(解決) 日時:2017/10/29 07:19

第三引数の値は読み込む個数だったのですね。 数年間、行数のことだと勘違いしていました...。 (アプリ開発するまで構造体やクラスでセーブデータを管理したことがなかったのもあるのですが) ちゃんと関数の確認をしないとダメですね。 色々とお手数をかけましたが、セーブデータ諸々はこれで解決しました。 ありがとうございました!
メンテ

Page: 1 |

題名
名前
コメント
パスワード (記事メンテ時に使用)

   クッキー保存