【掲示板ご利用上の注意】

 ※題名は具体的に!
 ※学校の課題の丸投げ禁止!
 ※ソースの添付は「HTML変換ツール」で字下げ!
 ※返信の引用は最小限に!
 ※環境(OSとコンパイラ)や症状は具体的に詳しく!
 ※マルチポスト(多重投稿)は慎んで!

 詳しくはこちら



 本当はこんなに大きく書きたくはないのですが、なかなか守っていただけなくて…。
 守ってくださいね。お願いします。(by管理人)

C言語ソース⇒HTML形式ツール   掲示板2こちら


管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧

No.20225

ポインタを返す関数に関する質問です
投稿者---ファイ(2005/03/05 15:16:00)


ベクトルや行列を扱う関数を書こうとしたのですが、えらーが出てしまいます。例えば、ベクトルに定数をかけて出たベクトルを返す関数を作ろうとすると、

<pre>#include &lt;stdio.h&gt;

double *svmultiply(int,double,double *);

double *svmultiply(int n,double s,double *v)
{
  int i;
  double a[n]; <font color="#009900">/*エラー:定数式が必要*/</font>

  for(i=0;i&lt;n;++i)
    a[i]=s*(*v++);
  return a; <font color="#009900">/*警告:問題あるポインタの変換*/</font>
}

main()
{
  int i;
  double v[5];
  double s=2;

  for(i=0;i&lt;5;++i)
    v[i]=1;
  v=svmultiply(5,s,v); <font color="#009900">/*エラー:左辺値が必要*/</font>
  printf(<font color="#0000ff">&quot;%d=2?\n&quot;</font>,v[0]);
} <font color="#009900">/*警告:sに代入した値は使われていない*/</font>

</pre>


のようになると思うのですが、コメントに書いたようなエラーが出てしまいます。

自分なりにはもしかすると、関数で先頭のポインターを返す配列a[]に原因があるのかもって考えています。というのも、本で「関数で返されなかった値は全てごみになる」と読んだことがあるので、ポインタaを返してもそれが参照している配列内の値はごみとして扱われている可能性があると考えたからです。
つまり、ポインタを返す関数では、その参照先をその関数内で定義することはできない、ってことです。
もしこれが正しいなら、ベクトルのポインターを返す関数ではなくて、内積のように値を返す関数はうまくいくはず、と思い作ったところ、うまくいきました。

ただ、この原因が正しいとすれば、行列やベクトルの計算の関数は、結果を代入する配列まで全て引数に含んだvoid型の関数にするしかなく、数式そのままの表現は失われてしまいます。

上の原因は正しいでしょうか。

なにか行列などの計算に関してご存知の方いればお教えください。


この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:ポインタを返す関数に関する質問です 20228 もぐりん 2005/03/05 20:17:58
<子記事> Re:ポインタを返す関数に関する質問です 20230 RAPT 2005/03/05 20:24:40


No.20228

Re:ポインタを返す関数に関する質問です
投稿者---もぐりん(2005/03/05 20:17:58)


お願いですから、【掲示板ご利用上の注意】を熟読し、HTML形式ツールの使い方を読んでから質問してください。



この投稿にコメントする

削除パスワード

No.20230

Re:ポインタを返す関数に関する質問です
投稿者---RAPT(2005/03/05 20:24:40)


まず、C 言語では、
double a[n];
といった書き方は認められていません。
double *a;
a = malloc(n * sizeof(double));
とヒープメモリを用意し、終了時に
free(a);
としてメモリを解放する必要があります。

また、double *svmultiply() 関数は、(double *) 型を関数の
戻り値の型としていますが、
return a;
の a は、double[n] 型なので型が違います。

さらに、a は自動変数なので、関数を終了する際に値が無効となります。

v=svmultiply(5,s,v);
これは、v はdouble[5]型であり、関数は (double *)型を返すため、
型の不一致となります。


解決策としては、引数でバッファを渡すのが嫌な場合、関数内で
static 変数を用意し、そのアドレスを返すようにします。
呼び出し側ではmemcpy()で値をコピーするか、ポインタで受けて
から使用します。

時間がないのでざくっと書きました。内容は吟味していませんが、失礼。




この投稿にコメントする

削除パスワード

No.20231

Re:ポインタを返す関数に関する質問です
投稿者---あかま(2005/03/06 02:01:17)


>まず、C 言語では、
>double a[n];
>といった書き方は認められていません。

使えるようになったとか。コンパイラが対応してるかはわかりませんが。
http://seclan.dll.jp/c99d/c99d04.htm#dt19990719


この投稿にコメントする

削除パスワード

No.20233

Re:ポインタを返す関数に関する質問です
投稿者---RAPT(2005/03/06 04:48:02)


>>まず、C 言語では、
>>double a[n];
>>といった書き方は認められていません。
>
>使えるようになったとか。コンパイラが対応してるかはわかりませんが。
>http://seclan.dll.jp/c99d/c99d04.htm#dt19990719

あ、C99では、そうですね(確か)。

下記の通り訂正します。

今回は、/*エラー:定数式が必要*/ とのことで、C99規格に適合していない
コンパイラを使用していると思われます。よって、(以下同文)


この投稿にコメントする

削除パスワード

No.20236

Re:ポインタを返す関数に関する質問です
投稿者---Craft(2005/03/06 13:03:54)


>使えるようになったとか。コンパイラが対応してるかはわかりませんが。
>http://seclan.dll.jp/c99d/c99d04.htm#dt19990719

C99対応コンパイラでかかれたコードが一般的になったら、机上デバッグがますます難しくなりますね。
配列要素数だけでもやっかいなのに、sizeof()の値が動的な値になると、かなりつらいかも。


この投稿にコメントする

削除パスワード

No.20247

Re:ポインタを返す関数に関する質問です
投稿者---ファイ(2005/03/06 23:56:16)


RAPTさん、ありがとうございます。エラーの意味が分かりました。

どちらのエラーもメモリーの割り当ての様子を考えずに、うわべだけの解釈でプログラムを書いたためのえらーですね。あと配列の名前と、ポインタとを、全く同じもの(定数と変数の違いを除いて)と思ってました。型は別なんですね。

メモリが割り当てられる様子を考えながら書き直してみます。

もぐりんさん、昆布の使い方気をつけます。


この投稿にコメントする

削除パスワード

No.20248

Re:ポインタを返す関数に関する質問です
投稿者---RAPT(2005/03/07 02:52:48)


配列の名前と、ポインタとを、全く同じもの(定数と変数の違いを除いて)
ん? 提示のソースコードでは、配列もポインタも変数ですが。 ちょっとサンプルコードを書いてみました。 Windows2000sp4/VC++6.0sp6/Console-Appで動作確認済 #include <stdio.h> #include <stdlib.h> double *svmultiply(int n, double s, double *v) { static double* a = NULL; /* 結果を保存するポインタ */ double* p = NULL; /* バッファ確保用 */ int i; /* メモリの再確保:新規時は確保のみ */ p = realloc(a, n * sizeof(double)); if( p == NULL ){ /* メモリ確保失敗時はNULLを返す */ free(a); a = NULL; return NULL; } /* メモリ確保に成功 */ a = p; /* 必要な処理 */ for(i = 0; i < n; ++i){ a[i] = s * (*v++); } return a; } int main() { int i; double s = 2; double v[5]; double* ans = NULL; /* 結果格納用 */ for(i = 0; i < 5; ++i){ v[i] = 1; } ans = svmultiply(5, s, v); if (ans != NULL){ printf("%f=2?\n", ans[0]); } /* 使い終わったメモリを解放 */ free(ans); return 0; }



この投稿にコメントする

削除パスワード

No.20255

Re:ポインタを返す関数に関する質問です
投稿者---Sophist(2005/03/07 19:47:10)


>あと配列の名前と、ポインタとを、全く同じもの(定数と変数の違いを除いて)と思ってました。型は別なんですね。

「配列名は、当該配列の先頭要素へのポインターである」ということ以外の
答えはないと思います。

任意の型、任意の要素数の配列(ここでは、名前をaとします)について、
aの値と&a[0]の値とを比べてみてください。


この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧