ページ 1 / 1
入れ替えの関数
Posted: 2009年1月04日(日) 01:00
by s-rush
いつもお世話になってます。
また分からないことが出てきたので、質問させていただきます。
2つの構造体をswap関数でメンバすべてを入れ替える関数を作ったのですが、
うまいこといきません。
メンバの中にまた構造体を組み込んでいるのが原因なのでしょうか?
ソースを載せておきます。
#include <stdio.h>
#include <string.H>
typedef struct{
int number;
char name[64];
}poke_skill;
typedef struct{
int number;
char name[64];
poke_skill tec;
}poke_tag;
void swap(poke_tag *a, poke_tag *b)
{
poke_tag *temp;
*temp = *a;
*a = *b;
*b = *temp;
}
int main()
{
poke_tag poke[2]={ {1,"フシギダネ",{5,"たいあたり"}},
{2,"フシギソウ",{6,"つるのむち"}}
};
printf("%d\n" ,poke[0].number);
printf("%s\n" ,poke[0].name);
printf("%d\n" ,poke[0].tec.number);
printf("%s\n" ,poke[0].tec.name);
swap(&poke[0], &poke[1]);
printf("%d\n" ,poke[0].number);
printf("%s\n" ,poke[0].name);
printf("%d\n" ,poke[0].tec.number);
printf("%s\n" ,poke[0].tec.name);
return 0;
}
コンパイルはエラーは出ないのですが、実行させるとこのようなエラーで止まってしまいます。
'System.NullReferenceException' のハンドルされていない例外が test.exe で発生しました。
追加情報: オブジェクト参照がオブジェクト インスタンスに設定されていません。
セグメントエラーのことだとは思うのですが、原因が分かりません。
お忙しいとは思いますが、またお願いします。
開発環境は前回同様、Visual C++ 2008 Express Editionです。
Re:入れ替えの関数
Posted: 2009年1月04日(日) 01:05
by SCI
構造体の実体 = 構造体の実体
という代入文になってますね。
メンバごとに代入するか、memcpy関数を使うといいのでは。
Re:入れ替えの関数
Posted: 2009年1月04日(日) 01:14
by s-rush
SCIさん、素早い回答ありがとうございます。
一応メンバごとに代入するように変更してみました。
以下変更部のソース
void swap(poke_tag *a, poke_tag *b)
{
poke_tag *temp;
temp->number = a->number;
strcpy(temp->name, a->name);
a->number = b->number;
strcpy(a->name, b->name);
b->number = temp->number;
strcpy(b->name, temp->name);
}
ですが、やはり実行中に同様のエラーが出てしまいます。
Re:入れ替えの関数
Posted: 2009年1月04日(日) 01:30
by SCI
「temp」という変数は実体がなければなりませんよ。
今ちゃんと見たら、最初のソースでも「temp」が未初期化のアドレスですね。
直接の原因はここのようです。
Re:入れ替えの関数
Posted: 2009年1月04日(日) 09:13
by box
>コンパイルはエラーは出ないのですが
最初に投稿されたコードのswap関数で、
ワーク変数tempに関する警告が出ませんか?
引数a,bはpoke_tag *型ですので、*a,*bはpoke_tag型ですね。
tempはpoke_tag型でないと勘定が合いません。
Re:入れ替えの関数
Posted: 2009年1月04日(日) 22:24
by s-rush
swap関数の部分を以下のように変更したところ、エラーが出ずに実行できました。
void swap(poke_tag *a, poke_tag *b)
{
poke_tag temp;
temp = *a;
*a = *b;
*b = temp;
}
poke_tag *temp;
とする必要がなかったのですかね?
boxさんのおっしゃる通り警告が出ていました。
初期化されていないとかなんとか・・・
以前大学で、同じswap関数を教わったのですが、
その時のソースはこうです。
void swap(int *a, int *b)
{
int *temp;
*temp = *a;
*a = *b;
*b = *temp;
}
構造体を扱う方との違いはtempに"*"がついているんですが、
双方の間で、違いっていうのは何なんですか?
もう少しお付き合いお願いします。
Re:入れ替えの関数
Posted: 2009年1月04日(日) 23:12
by kazuoni
まずポインタという概念を学んでください。
学校の教科書なり、C言語解説ページなり。
int *a;
これで変数aはintへのポイント型となります。
> poke_tag *temp;
> とする必要がなかったのですかね?
結論を言えばその通りです。
> 以前大学で、同じswap関数を教わったのですが、
> その時のソースはこうです。
>
> void swap(int *a, int *b)
> {
> int *temp;
> *temp = *a;
> *a = *b;
> *b = *temp;
> }
実行してみましたか?
同じように、アクセス違反が起こります。
なぜなら両方初期化がされていないからです。
int *temp;
と宣言するとします。
tempは簡単にいえば住所です。何番地みたいな。
*tempはその住所にいる人(数)です。
上のように初期化しないまま
*temp=
のように呼び出すと、住所不定のため、その人(数字)が誰か(何か)分かりません。
もしポインタの勉強をされたようであるなら、
もう一度復習してみてください。
Re:入れ替えの関数
Posted: 2009年1月04日(日) 23:13
by box
> poke_tag *temp;
> とする必要がなかったのですかね?
はい。してはいけません。
> 以前大学で、同じswap関数を教わったのですが、
> その時のソースはこうです。
そのときのソースには重大な誤りがあります。
> int *temp;
この状態は、int型へのポインタ変数tempを定義した、というだけです。
tempの中身(つまり、どこを指しているか)は不定です。
関数の中で
int n;
とローカル変数nを定義しただけではnの中身は不定であるのと同じことです。
さて、tempがどこを指しているかが不定の状態で
> *temp = *a;
これをやってしまうと、最悪、プログラムが落ちます。
void swap(int *a, int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
が、正しいソースです。
Re:入れ替えの関数
Posted: 2009年1月04日(日) 23:21
by box
swap関数のtempをどうしてもポインタ変数にしたければ、
#include <stdlib.h>
void swap(int *a, int *b)
{
int *temp;
temp = malloc(sizeof(int));
if (temp == NULL) {
/* 動的確保できなかったときの処理 */
}
else {
*temp = *a;
*a = *b;
*b = *temp;
}
}
のように書くことはできます。
でも、面倒だと思います。
Re:入れ替えの関数
Posted: 2009年1月04日(日) 23:31
by s-rush
返事ありがとうございます。
Cygwinで実行したことはありますが、普通に動きました。
このプログラムの虫食いが試験問題として出たのですが、
その時の私の答えは
int temp;
として"*"をつけていなかったのですが、
不正解として返却されました。
絶対に先生が間違ってると思い抗議したのですが、
その時の返答が
「引数としてポインタを使ってるんだから、
swap関数内で宣言する変数もポインタでないとだめだから"*"はいるぞ」
と言われて(多少言葉が違うかもしれませんが)結局不正解とされていたんです。
ポインタは一通りの基礎を勉強してある程度は使えるつもりですが、
考え方、概念として kazuoniさんやboxさんのおっしゃるイメージは持っていたので腑に落ちなかったのですが
やっぱり先生の言っていたことが間違っていたということでいいんですよね?
Re:入れ替えの関数
Posted: 2009年1月04日(日) 23:40
by box
> 「引数としてポインタを使ってるんだから、
> swap関数内で宣言する変数もポインタでないとだめだから"*"はいるぞ」
その論理は破綻しています。
引数がポインタ変数であることと、
当該関数でローカルに定義する変数がポインタ変数であるかないかは
全く別の話です。
> やっぱり先生の言っていたことが間違っていたということでいいんですよね?
先生の話は大間違いです。
Re:入れ替えの関数
Posted: 2009年1月04日(日) 23:51
by s-rush
> その論理は破綻しています。
> 引数がポインタ変数であることと、
> 当該関数でローカルに定義する変数がポインタ変数であるかないかは
> 全く別の話です。
そうですよね。
ありがとうございます。
これでやっとポインタに関するもやもやが取れました。
やっぱりポインタって、先生でも間違えるほど難しい概念なんでしょうかね??
今は別の関数を作っています。
内容的には簡単なのですが、もっとスマートに書くアルゴリズムを模索中です。
こちらの方もまた質問させていただくかもしれないです。
またその時はよろしくお願いします。
今回回答して下さった方、本当にありがとうございました。
Re:入れ替えの関数
Posted: 2009年1月05日(月) 09:49
by non
>やっぱりポインタって、先生でも間違えるほど難しい概念なんでしょうかね??
そこまで、難しい話ではありません。先生がうっかり間違えたのか、先生がいった意味を
間違えて捉えたものだと思います。先生に、再度確認すべきです。(先生の名誉のためにも)
Re:入れ替えの関数
Posted: 2009年1月05日(月) 13:18
by s-rush
>先生に、再度確認すべきです。(先生の名誉のためにも)
確認しておきたいのですが、その方は非常勤の方なので今は連絡がつかないです。
今の担当教授にもう一度話を聞いてみます。