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

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

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

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

Q&A

解決済

1回答

3152閲覧

関数の呼び出し元に戻った際に発生するsegmentation fault

notti1147

総合スコア1

C++

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

0グッド

0クリップ

投稿2021/12/09 05:21

前提・実現したいこと

cuda用にクラスのオブジェクトの持つメンバ変数をgpuに送れるようにデータの加工を行おうとしています。
コード内にあるクラスGame,Board,Stateは他ファイルにて定義されるクラスです。
Gameクラスはゲーム全体を管理するクラスでメンバの1つとしてBoardクラスの変数を持っています。
Boardクラスはゲームの盤面情報を持つクラスです。ここでのゲームはオセロを想定しています。
StateクラスはGameクラスを変数として持つクラスです。このStateクラスをcudaで使えるようなデータに加工しようとしています。

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

Segmentation fault (core dumped)

以下に示すvoidの戻り値の関数trans_data()を呼び出した後、呼び出し元に戻るときにsegmentation faultのエラーが発生します。

該当のソースコード

trans_data.cpp

c++

1 2#include "header/trans_data.hpp" 3 4void trans_data(State state, DATA_FOR_CUDA* dfc){ 5 Game game = state.game; 6 Board board = game.get_board(); 7 8 trans_board(board, dfc->board); 9 dfc->turn = game.get_turn(); 10 dfc->player = player_to_int(game.get_current_player()); 11 dfc->winner = -255; // PLAYER::NONE 12 dfc->was_passed = game.get_was_passed(); 13 14 cout << "end of trans_data()" << endl; 15 16 return; 17}

#test.cpp

c++

1#include "header/trans_data.hpp" 2 3int main(void){ 4 Game game; 5 State state = State(game); 6 7 DATA_FOR_CUDA* dfc = (DATA_FOR_CUDA*)malloc(sizeof(DATA_FOR_CUDA)); 8 trans_data(state, dfc); 9 cout << "ok trans_data()" << endl; 10 cout << dfc->turn << ", " << dfc->player << ", " << dfc->winner << ", " << dfc->was_passed << endl; 11}

#trans_data.hpp

#include <vector> #include <iostream> #include "state.hpp" #include "game.hpp" #include "board.hpp" #include "enum.hpp" #define BOARD_SIZE 8 typedef struct _DATA_FOR_CUDA { int board[BOARD_SIZE][BOARD_SIZE]; /* EMPTY = 0, WHITE = -1, BLACK = 1, WALL = 2, NONE = -255 */ int turn; int player, winner, start_player; /* WHITE = -1, BLACK = 1, DRAW = 0, NONE = -255 */ bool was_passed; } DATA_FOR_CUDA; void trans_data(State state, DATA_FOR_CUDA* dfc); void trans_board(Board board, int bArray[BOARD_SIZE][BOARD_SIZE]); int player_to_int(PLAYER p); int board_state_to_int(BOARD_STATE bs);

試したこと

標準出力std::coutを用いてどこでsegmentation faultが発生しているのか特定し、関数trans_data()から呼び出し元に戻るときにエラーが発生していることを突き止めました。
呼び出し元に戻った時に"ok trans_data()"と出力されず、また、関数内で"end of trans_data"と出力されることから呼び出し元に戻る際にエラーが発生していることがわかります。

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

コンパイラはnvccを用いており、その中でのg++のバージョンは9.3.0となっています。

今回、teratailでの初めての質問ですので、質問内容がわかりづらいかもしれません。

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

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

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

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

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

guest

回答1

0

ベストアンサー

関数trans_data()から呼び出し元に戻るときにエラーが発生していることを突き止めました。

スタック上の変数に対してバッファオーバーフローを起こしてスタックを破壊してしまっている可能性が高いです。

現象が起きるまでに実行されるコード(とくにtrans_data()に入ってから出るまで)を精査して、バグを見つけてください。

投稿2021/12/09 06:17

int32_t

総合スコア21695

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

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

notti1147

2021/12/09 08:25

回答ありがとうございます。 構造体DATA_FOR_CUDAのメンバについて調べたところ、それぞれのメンバが初期化されていないことがわかりました。そして、それぞれの値が-1となっていることがわかりました。ただし、-1となっているのはint型の変数のみでbool型のdfc->was_passedについては255という値が入っていました。 もう少し、ポインタについて調べる必要がありそうなのですが、int32_tさんとしましてはこの初期化されていない問題についてはどうお考えになりますでしょうか?
int32_t

2021/12/09 08:36

> 初期化されていない問題については 意味のある値を代入する前に値を参照してなければ問題ありません。
int32_t

2021/12/09 09:22

nvcc で使えるかどうかわかりませんが、gcc か clang なら、-fsanitize=address や -fstack-protector を付けてコンパイルするとなにかわかるかもしれません。
notti1147

2021/12/09 11:52

nvccでそのオプションを使うことはできませんでした。その他、バグ修正に使うべきテクニック等ありましたら教えていただけると助かります。 また、関数におけるポインタの引数への渡し方と関数内におけるポインタの使い方、記法等に謝りはありますでしょうか?特に、構造体の使い方については久々のc++でして、かなり曖昧な記憶のもと書いたので、間違っている可能性が高いのではないかと思います。 毎度、回答していただきありがとうございます。
notti1147

2021/12/09 12:32

その後、試行錯誤した結果、test.cppにおけるdfcの宣言を静的メモリ確保として、malloc呼び出しをやめたところ、プログラム末尾まで動くようになり、trans_data()関数内でsegmentation faultが発生することはなくなりました。しかし、静的メモリ確保に変えたプログラムにおいては、プログラムの最後にてsegmentation faultが発生するようになりました。 そして、別のアプローチを調べたところ、mallocを用いてメモリを動的に確保した場合、そのメモリを解放する必要があることを知りました。静的メモリ確保ではなくdfcを動的にmallocを用いて確保し、free関数で解放したところプログラムがエラーを吐くことなく動作しました。若干のモヤモヤは残るものの、動的に確保することでエラーがなくなるので、mallocを用いた実装で進めていくつもりです。 もし、静的メモリ確保の際に発生したsegmentation faultのエラーの原因についてわかりましたら、教えていただけると幸いです。
int32_t

2021/12/09 14:30

スタックの破壊よりヒープの破壊の可能性が高くなったように見えますね。 free()を追加することでこのバグが直る可能性はありません。たまたま症状が出なくなっただけです。 現在掲載されているコードの中にとくにおかしいところは見つかりません。 Game, State, Board 型のコードやtrans_board()のコードに何か問題があるのかもしれません。
SaitoAtsushi

2021/12/09 15:52

C++ では未定義の挙動に引っかかると前後の挙動を無視したデタラメな状態になってもよいというルールがあり、間違ったことをしている箇所とは全く違う場所で問題が顕現するのもよくあることです。 ときには間違っている箇所より前におかしくなることもあります。 強い最適化をかけなければそこまで極端なことはそう頻繁には起こりませんが、直接的に問題が起こる箇所を絞り込んでもそれだけではあまりあてになりません。 しかし問題が起こった箇所で何が起きたのかを観察するのは原因を特定する上でおおいに助けになります。 segmentation fault というのはメモリアクセスの違反なわけですが、どこにアクセスしてしまったのか、そのアドレスはどこから出てきたのかを追跡してそこを破壊したのはどこかを辿っていくわけです。 わかりやすい例を挙げれば、アドレス 0 にアクセスしていればたぶんヌルポインタのチェックがおかしいのだろうということが推察できます。 質問者の知識レベルがどの程度なのかわかりませんが、低レイヤについて十分な知識があるならばデバッガ (gdb など) で状況を観察するのは良い方法です。 結局のところ、こうすれば良いという万能の解はなくて「何が間違いなのか学ぶ」「間違っているところを探す」というごく当たり前で地味なことの積み重ねです。 それでもどうしても解決できなくて原因を探して欲しいということであれば問題を再現するのに十分な情報 (コンパイルして実行することが出来るソースコード全部) を提示すべきです。 といっても、 Teratail は質問の場であってデバッグ依頼の場ではないので好ましくはないですけどね。
notti1147

2021/12/10 05:20

皆さま、親切に回答ありがとうございます。 とりあえず、今回の質問における、segmentation faultの発生については解決しましたので、一旦この質問は閉じさせていただきます。 皆様のアドバイスをもとに変数への代入が無事に行われない原因を特定し、改善を目指したいところですが、現状原因が全くつかめていませんので、新しくteratailにて質問するつもりです。その際、ご助言いただけたら幸いです。 この度は回答いただきまして、ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問