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

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

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

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ポインタ

ポインタはアドレスを用いてメモリに格納された値を"参照する"変数です。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

3回答

1131閲覧

ポインタ配列受け渡し

shmpwk

総合スコア13

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ポインタ

ポインタはアドレスを用いてメモリに格納された値を"参照する"変数です。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

1クリップ

投稿2020/02/25 09:51

ポインタ配列の受け渡しがうまくいきません.
組み込み用(stm32)のプログラムを書いていて,一部コードを取り出してテストをしています.

以下のコード単体でテストをすると,意図した通り,321.500000,318.500000と出力されます.しかし,実際の組み込みの方ではfloat *motorval = pid(gyro);のところでうまく値が渡っていません.デバッガーで調べると値が受け渡されたとき,
motorvalはfloat*型で0x0を示し,*motorvalは1.12655382e-019を示しました.

#include <stdio.h> #include <math.h> float *pid(float arg); int main(void) { float gyro= 320; float *motorval = pid(gyro); printf("%f,%f", motorval[0],motorval[1]); return 0; } float *pid(float arg) { static float motor[2]; motor[0] = arg+1.5; motor[1] = arg-1.5; return motor; }

サイトによってはfloat *motorval = pid(gyro);と初期化するのはまずいとあり,別の方法も模索しましたがわかりませんでした.ご教授いただけたら幸いです.

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

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

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

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

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

shmpwk

2020/02/25 10:59

RTOSです.
pepperleaf

2020/02/25 11:11

デバッガで追えるのなら、 return の前の motor の値を見たら、どうでしょう。あとは、念のため、motor のアドレスも追跡。 あ、実際の組み込みでも 単体と同様に、pid() の宣言が先ですね。(間違えると、Cの場合、勝手に int)
shmpwk

2020/02/25 11:13

return前のmotor値は正常な値でした. pid宣言は組み込みの方でも最初にしております.
dodox86

2020/02/25 11:36 編集

stm32などの組み込み用のコンパイラ/リンカーですと、一般的にターゲットボードのI/Oマップに従ってアドレスをRAMやROMに割り当てるようにリンカースクリプトが用意されます。未初期化static変数はbssセクションへの割り当てになると思いますが、関数内のstaticfloat motor[2];へのアドレス割り当てがうまくされていないのかもしれません。ビルド時にmapファイルが出力できるのであれば、それを確認してみるのも良いです。あとは宣言位置を変えてみるとか。staticを除去してグローバル変数宣言すると状況が変わるかもしれません。
coco_bauer

2020/02/25 11:31

質問に書かれているコードは正常に動作するが、別のプログラムの部品として使うと、問題が起きるというのが質問の趣旨な訳ですね。 質問のコードを見て気になるのは、"static float motor[2];"のところです。ここはstaticにする必要があったのですか? staticにして関数の中で値が保持されるのを利用するより、素直にグローバル変数とする選択も可能だと思うのですが。
coco_bauer

2020/02/25 11:34

可能であればpid関数を使っているほうのプログラムを質問に追加して下さい。問題を起こしているプログラムのコードが無いと、具体的に問題が検討できず、助言もできませんから。
dodox86

2020/02/25 11:34

ランタイムライブラリは独自のものでしょうから、スタートアップルーチンに問題または制限があるとか。
dodox86

2020/02/25 11:39

※既にいただいているコメントや回答と交錯してしまいました。混乱すると思うので、私のコメントは適当に取捨選択してください。
shmpwk

2020/02/25 12:01

dodox84さん まずstaticを除去してグローバル変数宣言してみたいと思います. だめなら,ビルド時にmapファイルが出力,スタートアップルーチン等よくわかっていないので調べてみます.情報ありがとうございます.
shmpwk

2020/02/25 12:04

coco_bauerさん staticを除去してグローバル変数宣言してみたいと思います. 問題を起こしているプログラムは複雑で,他のエラーの可能性を排除した独立した部分を抽出した結果,質問文のコードとなりました. みなさんのアドバイスでも直らなかったら,また,コードを追加させていただきたいと思います.
dodox86

2020/02/26 02:45

>shmpwkさん dodox86ですw まぁ、84でも大差ないですけど。 スタートアップルーチンをあたるのは優先度超低めで良いです。組み込み系のある種のコンパイラ・リンカーは、初期化済みだけど書き換え可なグローバル変数は、ROM領域の初期値をスタートアップルーチンでRAMの該当変数に転送するようなケースもあります。お使いのstm32のビルド環境がどのようなものが分かりませんが、恐らくはスタートアップルーチンの問題ではなく、グローバル変数領域のアドレスマッピング、つまりリンカーの問題のような気がします。mapファイルを確認して、 static float motorのシンボルがアドレス0を示しているなら、そういうことです。mapファイルにレポートされるかどうかはオプションによると思うので、ご注意ください。コメントで書くべき内容を超えてきた気がするので、この辺で。
shmpwk

2020/03/03 09:47

dodox86さん 名前の件は失礼しました。 結局、staticをglocbalに変えたらできました。 コンパイラ・リンカ周りは今勉強中で、そのあたりが分かったら今回の問題の原因がわかるかなと思いました。色々詳しくありがとうございました。
guest

回答3

0

ベストアンサー

ポインタが 0x0 ということは NULL ですね。C 言語では「何もない場所」を表すアドレスで、そこにアクセスしてはいけないとされています。一方、組み込みではメモリアドレス 0 番地にも意味がある場合もありますので、禁止はされていません。Linux などのメモリ保護機能のある OS でユーザープログラムが 0 番地にアクセスすると普通はプログラムがクラッシュするはずなので、そういう環境ということですね。

さて、関数の中で static 変数を定義して、そのポインタを返すのは、ホスト環境(まともな OS がある環境)では動くはずですが、あまり良くないとされています。

ローカル変数は通常スタックに割り当てられるので、ローカル変数のポインタはスコープ(簡単に言うと、それを定義した関数のことと思ってください)を抜けると無効になります。これに対して、static 変数はグローバル変数と同様、専用のメモリ領域を割り当てられるので、スコープから抜けてもずっとそのまま存在しています。このため、pid 関数が motor のポインタを返しても有効なはずです。

が、一つの static 変数のメモリ領域は一箇所しかないので、 pid 関数を複数回呼び出しても毎回同じ値が返ります。このため、

C

1int main(void) { 2 float gyro1 = 320; 3 float *motorval1 = pid(gyro1); 4 float gyro2 = 100; 5 float *motorval2 = pid(gyro2); 6 printf("motorval1 = %f, %f\n", motorval1[0], motorval1[1]); 7 printf("motorval2 = %f, %f\n", motorval2[0], motorval2[1]); 8 return 0; 9}

の結果は以下のようになります。

motorval1 = 101.500000, 98.500000 motorval2 = 101.500000, 98.500000

また、マルチスレッド環境で複数のスレッドが pid を呼び出す場合は、メモリの競合が発生し、結果は不定になります。

これに対して、OS や言語処理系によっては static 変数をちゃんとサポートしていない場合もありえます。このような環境は フリースタンディング環境 と呼ばれ、組み込み Linux が普及する前の OS ではよくみられました。

この場合、お使いの OS や言語処理系の制約をよく理解した上で、開発を進める必要があるかと思います。

投稿2020/02/25 11:01

hoshi-takanori

総合スコア7895

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

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

shmpwk

2020/02/25 11:11

ご丁寧にご回答ありがとうございます. 実際の組み込みプログラミングでpidはwhileループにあり,何度も呼ばれます.そうなると,おっしゃる通りstaticはまずいですね. マルチスレッドか,フリースタンディングなどはよく知らないので,調べてみたいと思います. また,明日修正して実装したいと思います.
dodox86

2020/02/26 02:30

> pidはwhileループにあり,何度も呼ばれます.そうなると,おっしゃる通りstaticはまずいですね. ご提示のコードからするとstaticにした理由は「変数を関数内のstatic(静的)領域に確保することで変数名としての匿名性を持ちつつ、変数とポインターの有効性を確保したいということ」でしょうし、更にコードでは常にargの値で初期化しているので、「staticだからまずい」と単純には言えないはずです。既に指摘されているようにマルチスレッドや割り込みで使う場合は競合してしまうだろうからNGでしょう。しかしながら「motorvalはfloat*型で0x0を示し」ていることとは別の問題のはずです。
shmpwk

2020/03/03 09:53

hoshi-takanoriさんdodox86さん staticに問題がありました。未だに問題の原因は勉強中で完全に理解できていませんが、取り急ぎありがとうございました。
guest

0

float *motorval = pid(gyro);と初期化するのはまずいとあり

さあ。ローカル変数を初期化するために関数呼出しを行うことがまずいのかもしれませんが、普通は問題無くできることです。

別の方法も模索しましたがわかりませんでした

別の方法は、変数宣言と関数呼出しの、2行に分けることです。試しに次のようにしたらどうでしょうか。やってることは同じです。

C

1 float *motorval; // 変数宣言 2 motorval = pid(gyro); // 関数呼出しをして変数に代入する

投稿2020/02/25 13:29

rubato6809

総合スコア1380

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

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

shmpwk

2020/02/25 13:38

ご回答ありがとうございます.明日やってみます.,
shmpwk

2020/03/03 09:49

この方法では解決できませんでした。 変数をstaticからglobalに変えてできました。
guest

0

規格書は調べてませんが、動作がおかしいなら、定数式でない初期化は避けたほうがいいと思います。

C

1float *motorval; 2motorval = pid(gyro);

#追記
問題を切り分けるとしたらこんな感じでしょうか。

C

1#include <stdio.h> 2#include <math.h> 3 4float *pid(float arg); 5 6int main(void) { 7 8 float gyro= 320; 9 float *motorval = pid(gyro); 10 printf("in main motorval=%p\n",motorval); 11 printf("in main pid(gyro)=%p\n",pid(gyro)); 12 13 printf("%f,%f", motorval[0],motorval[1]); 14 return 0; 15} 16 17float *pid(float arg) { 18 19 static float motor[2]; 20 motor[0] = arg+1.5; 21 motor[1] = arg-1.5; 22 23 printf("in pid motor=%p\n",motor); 24 return motor; 25 26}

もちろん、デバッガで同等のことをやってもいいですが。

投稿2020/02/25 11:00

編集2020/02/25 11:16
otn

総合スコア84533

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

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

hoshi-takanori

2020/02/25 11:03

C 言語では float *motorval = pid(gyro); と一行で書くのと、宣言と代入を分けて書くのは同じ結果になるはずです。
otn

2020/02/25 11:05

動作がおかしいなら、疑ってもよいと思います。
hoshi-takanori

2020/02/25 11:07

確かに、可能性はあるし、試せば分かることですね。失礼しました。
shmpwk

2020/02/25 11:08

ご回答ありがとうございます.明日修正して試したいと思います.
shmpwk

2020/03/03 09:49

この方法では解決できませんでした。 変数をstaticからglobalに変えてできました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問