表紙
/
自作ソフト
/
日記
/
宝箱
/
サイト情報
/
検索
一般 / 新C言語 / 駄文 |
| |
|
|
4.1 可変長配列 ある関数に入る毎に違った大きさが必要になる配列(可変長配列)を、今まではどのように作成していました? その作成方法は、大体次に挙げる方法のどれかでしょう。 (1)あらかじめ最大の配列を作る方法。もし最大の大きさを見積もることができるのであれば、あらかじめその最大の大きさの配列を作ってそれを利用するという方法があります。しかしこの方法では最大が見積もれる時にしか使用することができず、また必ずしもメモリを有効に使用することができません。 (2)malloc を使う方法。必要となる大きさを malloc で確保し、ポインタに割り当てて使うという方法があります。しかし、この方法では、一般的に malloc 関数のオーバヘッドが自動変数の確保より遅くなりがちであるので効率がよくないという問題がありました。また最後に領域を開放するのを忘れがちという問題もありました。 (3)alloca を使う方法。プログラミング言語 C の標準関数ではないのですが、alloca という関数がよく使われていました。この方法では、この関数にいる間有効となるメモリ領域(通常スタック)のメモリを確保し、そのメモリを割り付けるという関数です。もちろんこの場合にはその関数を抜けると領域が自動開放されるので、領域の開放忘れはありません。しかし、alloca は演算子ではなく、単なるライブラリの関数なので、コンパイラが仮定するスタックの使い方に気をつけて使用する必要がありました。例えば、関数の引数の中で使用するとプログラムが正しく動かなくなる可能性がありました。
以上のように可変長な配列を作成するにはいくつかの方法とそれぞれの問題がありましたが、今度の新しい C 言語の機能を使うとこの問題は解決します。 (4)新しい C 言語の機能を使う方法。今度の C 言語では、配列の大きさの指定に数値定数だけではなく、数値変数も使用することができます。そのようなことから、今までの C 言語の表記と整合が取れたまま、かつ簡単に動的に可変長な配列を作成することができるようになります。 さて、このような新しい機能は従来の例えば sizeof といった文法にどのような影響を及ぼすのでしょうか? ということで、これからその影響について説明します。 (1) sizeof の適用 新しい C 言語で可変長配列が使用できることはわかりましたが、では C 言語の演算子 sizeof や、typedef を可変長配列に対して使用した場合にはどうなるでしょうか。まず始めに sizeof 演算子を適用するとどうなるかを示します。
getN 関数に引数 n を渡し、その大きさ分のバッファ buf を宣言します。そしてその大きさを sizeof で関数の戻り値として返します。main 関数では、引数に値を与え、戻り値を sz 変数に入れています。結果は、図からわかるように、引数と同じ値が sz に入ります。つまり、getN で sizeof している大きさは、静的に計算される値ではなく、実行時に動的に取られた値を返すことになります。 (2) typedef の適用 可変長配列は、直接的な変数宣言だけではなく、typedef にも適用可能です。表記方法は通常の typedef と同じです。サンプルプログラムを以下に示します。
ポイントは、typedef された時点の値が typedef の型とされるということであり、typedef された型を使用した時ではないということです。 ちなみに、n++ が宣言の後にあり、さらにそのあとで変数宣言が記述されているのを不思議に思った人もいることでしょう。今度の C 言語では、そのうち書きますが、C++ と同様にどこでも変数宣言をできるようになりました。したがってこのような記述も可能です。 (3) 関数引数での使用 関数ブロックの中に、可変長配列を宣言することができますが、もちろん関数の引数にも使うことができます。例えば、(1)のようにして使用します。可変長配列の中に使う変数は、そこで使用する以前に他で宣言されていて、コンパイラに対して可視になっている必要があります。したがって、void sumup(int data[m][n], int m, int n){ ... } のような関数定義は、ファイルスコープレベルで m や n という整数変数が宣言されていない限りエラーになります。
この関数に対するプロトタイプ宣言は、次の(2)のように記述することができます。特に、可変長配列であることを特に変数を示すことなく宣言できるようにするため、[*] という記述方式を使うことができます。この書き方はプロトタイプ宣言の中だけで使用することができ、関数定義では使用することができません。
しかし、(2)の例では、別に [*] を使わなくてもプロトタイプ宣言をすることができます。どうして、[*] という表記が必要なのでしょうか? そのための例を (3) に示します。
この関数のプロトタイプの宣言を単純に記述すると、(4.1) のように記述することになりますが、この場合、m や n は data の中で参照する前で宣言されていないので問題になります。そのような時、(4.2) のように [*] という記述方法を使用することでそのエラーを回避することができるのです。 (4) 多次元配列へのポインタ 可変長配列は、もちろんその型の配列を指し示すポインタにアドレスを代入することができます。しかし、従来の C 言語でそうだったとおり、配列の次元は一致している必要があり、さらに最上位次元の最大値を除いた他の最大値は一致している必要があります。例を示します。
(1) の場合は二番目の配列の要素の最大値が ar1 は 8、par1 は 3 というように一致していません。その結果これはエラーになります。(2) の場合は par2 は n なので、自由な値を入れることができます。最後の要素も ar1 は m、par2 は n+1 なので同様です。しかしこれが実際に有効なのは各次元の最大値が一致している時です。つまり n == 8 でかつ m == n + 1 の場合以外は、未定義の挙動となるのです。 (5) 宣言可能個所 可変長配列はどこでも使えるわけではありません。可変長配列が使えるのは、ブロックの中か関数引数/プロトタイプの中だけで、グローバル変数として宣言したり、struct や union の中のメンバとして宣言したりすることはできません。加えて、static や extern 付きの配列は、可変長配列にできません。
4.2 構造体中の 0 長配列メンバ プログラムを組んでいるとよくあるパターンの一つとして、可変長の配列を含んだ構造体が必要になることがありました。例えば以下のコードが挙げられます。
この記述では、sizeof(info_t) は多くの場合、attr, num, items[1] の大きさの合計を返します。だから、例えば、items が一つもいらなかった場合に対応するために、sizeof で大きさをとる時には、その要素から num-1 といったように 1 を引く必要がありました。これは、書くのが面倒な上、さらに後からプログラムを見る時にもわかりにくい記述方法となっていました。今度の C 言語では、[ ] の中の大きさを、構造体の最後のメンバである時に限って省略することができるようになりました。例えば、次のように書くことができます。
この書き方を用いれば、sizeof が返す値が、attr と num の合計になります。しだがって、ただ単純に num をかけるだけで必要な要素のメモリを確保することができます。その結果として、見た目にもすっきりした形態で記述できます。 4.3 配列要素中の記憶/型修飾子 配列を宣言する時には、今までは [ ] の中には整数定数を書くことしかできませんでしたが、今度の C 言語では、可変長配列を記述することができるようになったことは以前示しました。実は今度の C 言語ではさらに [ ] の中に static, restrict, const, volatile といった記憶修飾子、型修飾子を記述することができます。修飾子とその効果は次の通りです。
|
|
| ||||
表紙
-
著作権
-
注意事項
-
リンクについて
-
404 エラーについて
(c)1999-2014 seclan. All rights reserved. |