2018年7月29日日曜日

C言語ポインタ完全制覇

「C言語ポインタ完全制覇」の第二版が発売されています。古い表紙版は既に持っているのですが、内容も更新されているので、嬉しくて買ってしまいました。

学校に置いて行ったり、友人に渡したり、買いまくってましたw

この本がなかったらC言語を扱うことを諦めていたような気がします。

「OS自作入門」や「CPUの創り方」の影響ももちろん大きかったと思うのですが、この本を読んでいなければC言語をそれなりに扱えるようにはなれなかったと思います。

C言語疑問とメモ:構造体の宣言と定義

以下の内容は個人的メモであり、誤りが含まれている可能性があります。ご注意ください。
CについてはN1570を、C++についてはN3337を参照しています。

構造体の宣言と定義

struct Tag {
    char member1;
    int member2;
};

これは何を意味するか考える。

構造体・共有体指定子中の{...}部分(の存在)は、その翻訳単位内で新しい型の宣言をする。(6.7.2.1p8)

{...}を含む構造体、共有体、列挙型の型指定子は、それぞれの型を宣言する。
{...}の部分は、それらのコンテンツを定義する。
型指定子に識別子が提供された場合、その型指定子はその型のタグとなる識別子も宣言する。(6.7.2.3p6)

(6.7.2.3 Tagsの制約として)ある型は、最大一回だけ定義されたコンテンツ(内容)を持たなければならない(持てる?)(shall have)(6.7.2.3p1)

コンテンツとは、(6.7.2.3p6)から、構造体の持つ要素であると言える。

同じタグを持ち、同じスコープを持つ構造体型、共有体型、列挙型の宣言は、同じ型を宣言する。(6.7.2.3p4)
ある翻訳単位に、ある型のタグや他の宣言があるかどうかにかかわらず、コンテンツの定義のとじカッコ(})が現れるまでは不完全型であり、その直後に完全型となる。(6.7.2.3p4)

識別子は、オブジェクト、関数、構造体や共有体や列挙のメンバ名かタグ、typedef名、ラベル名、マクロ名、マクロ仮引数を、一つ示す(意味する、表す)ことができる。(6.2.1p1)
宣言は識別子の解釈と属性を明示する。(6.7p5)
オブジェクトのストレージの割当を引き起こす、オブジェクトの識別子の宣言は定義である。(6.7p5)
関数の本体を含む識別子の宣言は定義である。(6.7p5)

なお、無名の構造体(タグも識別子も持たない、構造体のコンテンツの定義のみを行う)は文法上構造体などの内部でしかできないと思う(ちゃんと確認はしていない)(6.7.2.1p1)

以上のことから、最初のコード片は、
「新しい構造体型の宣言と、構造体を示す識別子(=タグ)の宣言であり、さらにその識別子が示す構造体型のコンテンツを定義している」
と考える。

C++では少なくとも、宣言のうち定義でないものが明確であり(3.1p2)、一般的な意味での「クラスの定義」とは何であるかはわかりやすい。(9p2)
C++の方が、部分的にはわかりやすい気がします。

2018年7月28日土曜日

C言語疑問とメモ:多次元配列は何故メモリ上で並んでいると言えるか

以下の内容は個人的メモであり、誤りが含まれている可能性があります。ご注意ください。
CについてはN1570を、C++についてはN3337を参照しています。

多次元配列は何故メモリ上で並んでいると言えるか(int[3][5])(ここでは可変長は考えない)
基本的には「多次元配列の宣言は宣言構文上、配列の配列(Array of array)の宣言である」からだと思う。
(6.5.2.1)に値を参照する場合の説明がある。

その他の前提となりそうな知識
配列とはオブジェクトの連続した(=隙間ない?)並びである。(6.2.5p20)
さらに、(6.2.6.1)より、オブジェクトはバイトの連続した並びである。
E1[E2] は (*((E1)+(E2))) と等しい。(6.5.2.1p2)

先頭要素へのポインタに+1すれば次の要素になることはどこで保証されている?
(6.5.6p8)で保証されると思う。

負の要素がないことはどこで保証する?
配列の要素数は1以上(6.7.6.2p1)と(6.5.6p8)の順序付けによって保証できそう。

配列のサイズ=要素数で良いのか?
(6.7.6.2p1)によれば、配列の宣言で指定しているのは配列のサイズである。
規格書が完璧に書かれていると考えれば、
* sizeofはオペランドのサイズを(バイト単位で)返す。サイズはオペランドの型で決まる(6.5.3.4p2)
* sizeofを配列型を持つオペランドに適用した場合、配列内の総バイト数を返す。(6.5.3.4p4)
であるので、「サイズ!=要素数」と考えたくなる。
そして、配列がオブジェクトの隙間ない並びであると考えれば、要素数はサイズによって決定できると言えそう。
ただ、「要素数を決めることはサイズを決めることである」というのは自明なことであって、わざわざそこまで厳密に書いていないのかもしれない。

規格書を引く場所を間違っているのだろうか?
この点については、C++はしっかり書いてくれている。(8.3.4p1)
ただ、C++では「boundを指定する」、という表現であるので、Cで「サイズを指定する」、というのは正しい表現なのかもしれない。

その他
無理にC言語を使わずに、C的な書き方をする場合でもC++を使ったほうが楽かもしれない。 C++の(3.9p4)のようなわかりやすい表現がCにもあればよいのに。