• ベストアンサー

配列とその要素数をメンバにもつ構造体

C言語では,配列を引数とするときに,合わせて要素数を渡さなければいけないといわれます. そこで,配列とその要素数をメンバにもつ構造体を定義して,これを要素数つき配列のように扱えば,引数として渡す場合には,この構造体を渡すだけでよいと思うのですが,このような用法は一般的に用いられているでしょうか. 単純なアイデアだと思うんですが,あまり紹介されているのを見たことがありません. もしあまり用られない用法であれば,その理由を教えてください. 現在,プログラムを書いているのですが,引数の多さに閉口しており,上の考えで,引数を減らしたいと考えています.  よろしくお願いいたします.

質問者が選んだベストアンサー

  • ベストアンサー
  • lv4u
  • ベストアンサー率27% (1862/6715)
回答No.1

>>C言語では,配列を引数とするときに,合わせて要素数を渡さなければいけないといわれます. 「要素数を渡さなければいけない」という表現は誤解を生むと思います。渡したほうが楽なときもあるけど、渡さないでもいいです。(そういう言い方の文献って読んだことありません)そして、演習等で作るような小さなソースコードでは、そういう用法も使いますが、業務で使うようなソースでは、構造体で渡すほうが一般的です。これは、「引数が減る」という面もありますが、「引数が増えた、引数の属性が変更になった」等の変更による影響が少なくなるからです。 >>単純なアイデアだと思うんですが,あまり紹介されているのを見たことがありません. それは、一般的に「C言語学習用」として現在書店で売られている書籍のレベルが低いからです。ソースで「引数の多さに閉口」っていうようになるのは、(作り方の問題があるかもしれないけど)それなりに複雑な処理を行わせようとしているのだと思います。が、書籍では、そういった例題を出すと、ソースコードのページが増えるし、説明もやっかいになります。 つまり、「教科書」に実務の複雑さを持ち込む必要はないし、かえって有害であると考えているんだと思います。もしかすると、そうした「複雑なソース」を著者たちが書いた事が無いという可能性もあります。 C言語で書かれた業務用ソースを目にする機会は、著作権や機密保護の観点から一般ではほとんど無いと思いますが、一番いいのは、そういったもので学習することかもしれません。 お金に余裕があれば、海外からアプリをソースコードで購入する方法もあります。でも、金銭的な余裕が無い場合、オープンソースのデータベースやCコンパイラのソースコードを参考にされればいいと思います。 ちなみに私の場合は、アメリカからデータベースのソースを購入して日本語化を施したり、その作業中に日本語がうまく通らないため、GNUのCコンパイラに修正を施す等の作業を通じてC言語を学習しました。(お、いずれもローカライズの作業だ) P.S. 以前は、中上級クラスのC言語の本がいくらかあった気もするんですが、どうも最近は初級・中級クラスばかりのような気が・・・。まあ、「専門書は売れない」というのはどの業界でも同じかなあ。

lacalle683
質問者

お礼

わぁ!詳細なご説明ありがとうございます。 ”渡さなければいけない”は誤解を招く表現でした。申し訳ありません。 構造体で渡すほうが一般的ということをお聞きして、積極的に活用したいと思います。 教科書についてですが、私はようやく今、カーニハンの本を必要に応じて読むというレベルです。これから中上級の本も探してみようと思います。 コンパイラのソースを読むという発想自体、私にはたいへんなことですが、さらにその修正を通じてCを学ぶというのはすごい学習方法ですね。お勧めのとおり、暇をみて GNU C のソースを読んでみたいと思います。 どうもありがとうございました。

lacalle683
質問者

補足

ご回答に対する補足でなく、お礼の補足ですが、構造体を使うことで保守性が向上するということは、有用なお言葉でした。 どうもありがとうございました。

その他の回答 (4)

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.5

どなたもこの点に関しては触れていないようなので。 struct Array {int max; int body[ARRAYMAX];} のように「配列」を 構造体要素に持たせると、この型を引数や戻り値にしたときに操作する メモリ領域(≒スタック領域)がそれだけ大きくなります。 古のCでは構造体を戻り値や引数に使うことができなかったので (構造体へのポインタを使うのは問題ない)伝統的にこのような手法は 使われてこなかったのでしょう。 gawk という GNU によるawk処理系では、文字列を {int length; char *stptr;} のような構造体で管理しています(本当はもっと複雑ですが説明のため 簡単にしました)。 引数の多さに関してはまあプログラムを見てみないとなんとも。 たとえばWindows のAPIで描画のためにフォントを用意する関数は引数の数が14個あります(構造体を使うようにした類似バージョンもありますが)。 X Window system の描画関連も結構引数は多いものはあります。

参考URL:
http://yokohama.cool.ne.jp/chokuto/urawaza/api/CreateFont.html
lacalle683
質問者

お礼

>・・「配列」を構造体要素に持たせると、・・メモリ領域(≒スタック領域)がそれだけ大きくなります。 これも有益なご指摘です。 これから構造体を使うことが増えそうですが、サイズが大きいときには、構造体へのポインタを使うようにしたいと思います。 >gawk という ・・・ gawk も浅薄な使用をしていますが、内部の処理まで考えたことがなかったので、おもしろい話です。 また、要素数+配列の構造体が普通に使われていることを、改めて確認できました。 >引数の多さに関しては・・・ 残念ながらというか、作っているプログラムは xterm などでコマンドライン上で用いるものです。Windows のAPI や X Window System を扱うような複雑なものでないです。 (途中、計算結果を gnuplot に投げてプロットする部分があり、これも引数が多い関数の一つだったりしますが) やはり、設計のやり直しをしたほうが良さそうです。 URL で示していただいた例は、おもしろい例でした。 どうもありがとうございました。

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.4

一応参考までに回答します。 > C言語では,配列を引数とするときに,合わせて要素数を渡さなければいけないといわれます. 状況によります。 例えば、要素数が常に固定の場合、敢えて要素数を引数で渡す必要はありません。間違いを最小限に抑えるため、 void func(int a[10]); といった書き方をするのもよいでしょう。しかし、これではコンパイラのチェックは全く期待できないので、代わりに、 void func(int (*a)[10]); にした方がやや安全かもしれません。 もし、関数の利用者がaが配列であることを意識しなくてもよいのであれば、 typedef int something[10]; void func(something a); とすることで、ほぼ間違いがなくなります。 次に配列の要素が可変の場合ですが、要素数ではなく、配列の先頭へのポインタと、終端より一つ先へのポインタを渡す方法もよく使われます。具体的には、 int a[10]; func(a, a+10); といった感じです。 場合によっては、この方が融通が利くことがあります。 構造体でポインタと要素数を管理する方法もよく使われますが、それは引数に渡すためというよりは、配列と要素数を常にセットで扱いたいためだと思います。引数のためだけに専用の構造体を組み立てるのは面倒なだけです。 できれば、引数にしようがしまいが、その構造体を使って管理するようにすべきです。そうすると、その構造体を操作するための関数群が生まれ、次第にカプセル化が進んできます。 もう一歩進めて、ヘッダファイルでのその構造体の宣言を不完全宣言だけにすれば、情報隠蔽も実現することができます。

lacalle683
質問者

お礼

>コンパイラのチェックは全く期待できないので、代わりに、 >void func(int (*a)[10]); >にした方がやや安全かもしれません。 コンパイラの仕組みにまで配慮したプログラミングをあまりしたことがないので、こういう視点からのご指摘は、新鮮で勉強になります。 >typedef int something[10]; こういう typedef の使い方は知りませんでした。活用したいと思います。 >int a[10]; >func(a, a+10); これも場合によって、便利に使えそうですね。 ターミネータを使う方法と共に、あまり使ったことのない方法です。 最後のお話は ANo.3の方とともに、「オブジェクト実装」の一端だと思いますが(いや、よく分かっていないのですが)、とても惹かれる考えです。 保守性や拡張性の向上という期待から、飛びつきそうですが、私の場合、それ以前に改善すべき点がたくさんあります. とても勉強になりました。 どうもありがとうございました。

  • galluda
  • ベストアンサー率35% (440/1242)
回答No.3

がると申します。ちぃと色々な観点から。 まず「配列を引数とするときに,合わせて要素数を渡さなければいけない」ですが、概ねYesです。配列そのものは「要素数を含まないデータ」なので、何らかの形で要素数を把握する必要があるので。 ただ、もしこれが「ポインタの配列」ですと、最後にNULLを入れておいて、これをターミネータとして用いるって事はよくありますが。 「配列とその要素数をメンバにもつ構造体を定義して,これを要素数つき配列のように扱えば」については…知っているかぎりだと「割合よく用いられる手法」です。このあたりは業務系の系列にもよるのでしょうが。 ただ、構造体の内容を「動的にする」手法は、なれないとちょっとだけ難しいので。もしかするとそのあたりを「難しい」と感じるような現場だと、あまり用いられないのかもしれません。 あと。構造体の中にある構造体(俗に言う「構造体のネスト」)は、便利ですが同時に要注意なポイントでもあります。 大体…3~4ネスト以上だと、可視性が一気に落ちますので。知っているかぎりで7ネストというものがありましたが…追いかけるのに辟易とした記憶があります(苦笑 引数の多さについては………構造化でCで書いているかぎり、どうしてもある程度避けられない部分があると思います。 ただ、意識してできるだけ「減らそうとする」ことは大切なのではないか、と思います。そうすると「よりきれいな設計」が意識できるようになるので(このあたりは#2さんもおっしゃっている部分ですね)。 で。このあたりが見えてくると、ゆっくりと「オブジェクト指向」が見えてくるのかなぁと思うのが個人的雑感です。 Cを学ばれているのであれば、大変ではあるのですが「C言語でオブジェクト実装」をかけると、かなりいろいろと勉強になるかと思います。

lacalle683
質問者

お礼

>「ポインタの配列」ですと、最後にNULLを入れておいて、これをターミネータとして用いる なるほど、こういうやり方もあるんですね。 今つくっているプログラムの中にも、要素数を明示的に扱う必要がない/扱はない方が便利、というものもあると思います。 例えば、一時的に(添え字の最大値が決まった)行列を定義して、処理過程で(添え字の最大値を超えない範囲で)サイズが伸び縮みする。最後に全て捨てる。というものがあります。 その時々の要素数を管理してやりくりしていましたが、ターミネータをうまく使うとスマートかもしれないと思いました。 (一度、realloc を多用するソースを書きましたが、処理に時間がかかり、かつメモリ管理が複雑になるので破棄したりしました) >構造体の内容を「動的にする」・・・ >「構造体のネスト」は、・・・大体…3~4ネスト以上だと、可視性が一気に落ちます データの項目名(複数)をまとめた構造体を、動的に確保して使っておりますが、これもデータを格納した2次元配列、要素数と一緒にまとめてしまおうと考えています。 そこで、構造体の多すぎるネストが可視性を下げる、というご指摘はたいへん有益です。 > 引数の多さについては………構造化でCで書いているかぎり、どうしてもある程度避けられない部分があると思います。 仕様の拡大に伴って引数に増やす、あるいは、ほぼ同じだが少し異なる複数のサブルーチンを一つにまとめる際、異なる部分を制御するためのパラメータを無計画に入れるなど、私の場合はむしろ設計が悪いです。 >・・「C言語でオブジェクト実装」・・ 関数のポインタなどを使うことになるでしょうか。(そういうものがあると知ってるだけで、使ったことありません) 今は、後日のお楽しみということにしたいと思います。 どうもありがとうございました。

回答No.2

・引き数が多い *関数分割に失敗している 関数は依存関係(引き数や戻り値)が少なくなるように分割するべき。 *データ設計が巧くない 一まとめにできる(するべき)データは初めから構造体にするべき。 #ちょっとしたプログラムでは構造体の中に構造体があるなんてことはざらです。 ・配列を渡すには要素数は必要? char配列においてナル文字を終端とするように、適当な値を終端値として使えば要素数を渡す必要はありません。 #お勧めはしませんが。 ・ノウハウが紹介されていない 本を探しきれていないか、資料を探せていないのでしょう。 ソースを拾えるサイトは検索すればすぐ見つかりますし、 Unix系OSではフリーのソフトはソース公開が原則です。 各種ライブラリもソースが公開されていますね。

lacalle683
質問者

お礼

ご回答ありがとうございます。 ご指摘のとおり、引数が多いことに対して、対症療法的に解決しようとした面がありました。 例えば最も引数の多い関数のひとつは、引数の数が合計8個もあります。 設計が悪いというご指摘は、ごもっともです。 >構造体の中に構造体があるなんてことはざらです。 特に質問に即して、配列とその要素数をメンバにもつ構造体も、めずらしくないと理解しました。 ”配列を渡すには要素数は必要”は誤った文章でした。 例として挙げられた char配列は書いていただいたように使っています。 >本を探しきれていないか、資料を探せていないのでしょう。 質問に書いた用法が紹介されている記事を、探すことができませんでした。 また公開されているソースから、用法が広く用いられていることを直接確認することはしませんでした。 公開されているソースを読んでいないことが悪いとうことを痛感しました。 ある現象を調べるのための数値計算のプログラムを作っているのですが、目的に対して、プログラムの作成が副次的なものになりがちで、どうしても勉強がゆきとどきません。 今回は、プログラマーの方のご意見が聞けて、たいへん勉強になりました。 どうもありがとうございました。

関連するQ&A

  • データ数の多い構造体配列について

    visual c++で配列をメンバーに持つ構造体配列を定義したいと思っています。 しかし、以下のように定義してもプログラムが実行されません。 配列の容量が大きいのかもしれませんが、 大量のデータを扱いたいので容量を小さくはしたくありません。 大量のデータを扱うのに何かいい方法はないでしょうか。 struct X { double A[1000]; }; : : : struct X B[10000];

  • 動的に作成した構造体配列の中に、さらに動的に構造体配列を作るには?

    はじめまして。 この度、ちょっとした計算プログラムを作ることになったのですが、 タイトルにもありますように、 『動的に作成した構造体配列の中に、さらに動的に構造体配列を作る』 方法がわからずに困っています。 実際には下記のようにプログラムしたいのですが... ------------------------------------------------- 'a'の構造体の中に作られる'b'の配列数は、下記のように 'a'の配列の番号により違い、また'a'の配列数も最初の段階では未定です。 構造体'a'←配列数未定 構造体'b'←配列数未定 a[0]-----b[0]  |    b[1]  |    b[2]  | a[1]-----b[0]  |    b[1]  | a[2]-----b[0]  |    b[1]  .    b[2]  .    b[3]  . ------------------------------------------------- new演算子で'a'の配列は作れたのですが、その中の'b'の数の分だけ 配列を動的に作成する方法がわかりませんでした。 最初は'b'の配列を多めに取って計算すればよいと思っていましたが、 計算過程で'b'の配列数が10000を越えてしまう場合があり、 また'b'の構造体のサイズも大きめなので、断念せざるをえませんでした。 どなたか、このように配列を作成する方法を知っている方がいましたら、 是非教えていただきたいです。 下手な説明ですいません。

  • 配列の要素数

    配列の要素数を求めるプログラムを作りたいのですが

  • データ数の多い構造体配列

    メンバが配列である構造体配列を定義したいのですが、 struct hei { double *hight=(double*)malloc(sizeof(double) * 2*max + 1); }; int main() { struct hei h[10]; : : : という風にはできないのでしょうか。 「構文エラー : ';' が '=' の前にありません」とエラーが出てしまいます。

  • 配列の要素数に変数を入れたいときには

    よろしくお願いします。 配列の要素数には定数しか入れられないのですが,どうしても変数を入れたいときは,それを引数として関数を呼び出すしか方法はないでしょうか。 具体的には,scanfで手に入れたint型の変数を要素数とする配列を宣言したいのですが,どうすれば良いでしょうか。 ご教授ください。

  • Cなどで要素の数が固定できない配列はどうやって実現しますか?

    配列について質問します。 BASIC系では配列は動的配列で要素の数が自由に変えられます。 ですが、C等では宣言時に配列の要素の数を決めておかねばならなかったと思います。 もし、C等で要素の数がわからないけど、配列を使いたい場合、どうすればいいのでしょうか? どのように実現するのでしょうか? 配列と同じ使い勝手なら配列でなくともかまいません。 今はVBAでプログラムを組んでいるので、動的配列を使えばすむのですが、いずれ他の言語に移植したいので、できるだけ使わないようにしたいと思っています。 よろしくお願いいたします。

  • 配列の要素数を超えた参照のコンパイル

    C言語においては”配列の要素数を超えての参照もコンパイルエラーにはならない”という事がいえます。 例えば int a[10]={1}; とした時、a[-1] a[11]を参照しても、コンパイルエラーにはなりません。不定値が表示されるか、Red Hat Linuxに関しては”セグメンテーション違反です”とでるだけです。 しかし、わたしはCしか学んではいませんので解りませんが、配列の要素数を超えての参照はコンパイルエラーになる言語もあるのではないかと思います。 C言語はよく”暴走する言語”と言われます。規制をできる限り排除して軽くし、ミスはプログラマが取るという意味に考えています。 ”C言語においては配列の要素数を超えての参照もコンパイルエラーにはならないという事”はC言語のその様な設計思想に基ずいた仕様なのでしょうか。 それとも、違う考えに基ずいて、”配列の要素数を超えての参照もコンパイルエラーにはならない”という事に成っているのでしょうか。 宜しく願います。

  • 構造体配列のポインタを引数に

    こんばんは。 Visual C++ 2008 Express Edition を使っています。 C++のあるプログラム内の関数で、構造体の1次元配列のポインタを引数に取ろうと思っているのですが上手くいきません。 関数の目的としては、関数側と呼び出す側のファイルを分けているので、関数側のグローバルな構造体配列のアドレスに呼び出し側の構造体配列のアドレスを代入することです。 構造体はExampleと型を定義してあります。 ・関数側 Example *global_ex[10]; void GetEx(Example *ex[])  // ←2つ目のエラー {   global_ex = ex;  // ←1つ目のエラー } ・呼び出し側(呼び出し側の関数内)   Example ex[10];   (exの初期化)   GetEx(&ex); ・エラー  error C2440: '=' : 'Example *[]' から 'Example [15]' に変換できません。配列型への変換はありませんが、参照またはポインタから配列への変換があります。  error C2664: 'GetEx' : 1 番目の引数を 'Example (*)[15]' から 'Example *[]' に変換できません。 プログラムとエラーは以上です。 何をすれば解決できるのか、ご存じの方いらっしゃいましたらよろしくおねがいします。 CとC++は独学で学んでいるので偏りがあると思います。 「基本的な○○が分かっていないのでは?」と感じたらその点もご指摘頂けると嬉しいです。。

  • qsortを用いた構造体配列のソート

    お世話になります。 http://simd.jugem.jp/?eid=116 を参考にqsortを用いた構造体配列のソートをC言語で記述しようとしています。 上記のページは、構造体のメンバが配列でない場合です 今回は、メンバが配列のときの構造体配列のソートを実現したいと思っています。 つまり、 typedef struct{ int a; int b[1024]; int c[1024]; }TEST; という構造体配列があって、 TEST base[256]; と宣言し、メンバの配列の添え字を基準としてソートしたいときには、どのようにqsortを用いれば良いのでしょうか、ということです。 どうしたらよいかわからず途方にくれています。 つまり、下のようなソートが行われるには、どのようなプログラムを書けばいいかということです。 構造体でソートするものとします。 構造体でソートできれば、qsortを使っていなくても構いません。 プログラムの得意な方がおりましたら、ご教授下さい。 <ソート前> //************************************************ test[ 0].b[0] = 3; test[ 1].b[0] = 102; ... test[255].b[0] = 1; ------------ test[ 0].b[1] = 99; test[ 1].b[1] = 200; ... test[255].b[1] = 2; ------------ ... ------------ test[ 0].b[1023] = 99; test[ 1].b[1023] = 9; ... test[255].b[1023] = 200; //************************************************** <ソート後>:test[x]ではなく、b[y]を基準としてそれぞれのくくりをソートしたい //************************************************ test[ 0].b[0] = 1; test[ 1].b[0] = 3; ... test[255].b[0] = 102; ------------ test[ 0].b[1] = 2; test[ 1].b[1] = 99; ... test[255].b[1] = 200; ------------ ... ------------ test[ 0].b[1023] = 9; test[ 1].b[1023] = 99; ... test[255].b[1023] = 200; **************************************************

  • 要素数・要素の値が未定の配列の宣言について(C言語)

    要素数・要素の値が未定の配列の宣言について(C言語) タイトルの通りプログラムの内容上、要素の数とその数値がプログラムの途中で算出されるため 最初の宣言の段階では両方ともその数が未定です。 こういった場合、配列の宣言はどのようにすればよろしいのでしょうか? どなたか教えていただけるととても助かります。よろしくお願いします。

専門家に質問してみよう