<>読書ノート

5417 ワード

最近、コード読書という本を読んでいますが、他人のコードを読むのも自分の能力を高める良い方法です.記事参照
私はどうしてコードを読むのが好きですか?あなたはどうして爱すべきですか?およびRead Great Programs
第三章:高級Cデータ型
1、c/c++のポインタタイプ(メモリアドレス)のパラメータは、関数呼び出し中に大規模なデータ要素のレプリケーションオーバーヘッドを回避するためによく使用されます.よくconstで
宣言子は、効率的にパラメータを参照して渡すことを示します.
2,c/c++では,配列がパラメータとして関数に伝達されるか,結果として返されるとポインタに暗黙的に使用される.
3、文字ポインタの文字配列の違いに注意してください:文字ポインタはポインタ変数で、彼は空間を占有します.この空間の大きさは機械が何ビットなのかによって決まります.
文字配列は文字ポインタを使っているように使用されていますが、実際にはコンパイラがメモリで作ったタグにすぎません.文字配列
それ自体はスペースを占有しません.
         
struct test{
    int a;
    char* buff;
    char buf[];
}; 

上のstructの32ビットマシンのサイズは8バイトです.int,char*はそれぞれ4バイトを占有する.char buf[]; これは1つの空間も占有しないで、彼はまるで隠れた文字のポインタのようです.
だからポインタは単独の変数で、自分のアドレス空間があって、その値は文字列の定数のアドレスで、配列名はこの配列を代表して、単独のアドレス空間がありません;
その他の例:
char *s = "haha";
char line[] = "haha";

私のマシンでは変数sizeof(s)サイズが4バイトで、他の文字列を指すことができますが、指す文字列の内容を変更することはできません.これは予知できない動作を生むからです.
sizeof(line)のサイズは5.lineが指すメモリアドレスは変更できませんが、含まれる要素は自由に変更できます.
char ch[]="abc";
  ch                 '/0'     ,          ,  char          。
char *pch = "abc";
  pch      ,            ,           ,             ,      。
     ______           ______      ______
ch: |abc\0 |    pch: | ◎----->  |abc\0 |
     ______           ______      ______

4,cではstructを用いてオブジェクト向けプログラミングを実現する.関連するデータと関数ポインタを構造体でカプセル化することで、クラスのフィールドとメソッドをシミュレートし、作成する場合があります.
類似オブジェクトのエンティティ.次のようになります.
 
struct	domain {
	int	dom_family;		/* AF_xxx */
	char	*dom_name;
	void	(*dom_init)		/* initialize domain data structures */
			__P((void));
	int	(*dom_externalize)	/* externalize access rights */
			__P((struct mbuf *));
	void	(*dom_dispose)		/* dispose of internalized rights */
			__P((struct mbuf *));
	struct	protosw *dom_protosw, *dom_protoswNPROTOSW;
	struct	domain *dom_next;
	int	(*dom_rtattach)		/* initialize routing table */
			__P((void **, int));
	int	dom_rtoffset;		/* an arg to rtattach, in bits */
	int	dom_maxrtkey;		/* for routing layer */
};

このような宣言によれば,この構造体タイプの変数やポインタは初期化が完了すると,C++オブジェクトのように使用できる.
 
	for (dom = domains; dom; dom = dom->dom_next)
			if (dom->dom_family == i && dom->dom_rtattach) {
				dom->dom_rtattach((void **)&nep->ne_rtable[i],
					dom->dom_rtoffset);
				break;
			}

オブジェクトは異なるメソッド(関数ポインタ)を使用して初期化でき、同じインタフェース呼び出し(同じ構造体メンバー名を使用することによって)を使用することができます.これは、オブジェクト言語で虚関数とマルチステートを実現するためのものです.
やり方.実際には、同じタイプのオブジェクトには同じメソッド(同じ関数アドレスを指す)が格納されているためです.
5.ユニオン(union、共通体とも呼ばれる)内のすべての要素が同じ記憶領域を占有するため、いずれかの時点で1つの要素しか記憶またはアクセスできない.C中の連合体は以下の機能を実現する.
メモリスペースを有効に利用し、マルチステートを実現し、内部キャラクタリゼーション方式を使用してデータにアクセスします.
これまで、連合体の最も一般的な用法は多態を実現することであり、多態はここでは同じオブジェクト(通常c構造体で表される)で複数のタイプを表すことを指す.異なるデータ型をコンビネーションに格納
異なるメンバー変数では、メモリを節約できます.しかし、メモリの節約は主な目的ではありません.本当の目的は、異なるタイプのデータを格納することです.マルチステート用途を実現する連合体は、通常、構造体に含まれる.
で、構造体には、コンビネーションに格納されているデータ型を示す列挙タイプのフィールドもあり、typeなどのフィールド名も非常に簡単です.次に、Sun RPCライブラリの例を示します.
RPCメッセージの構造体を含むコード.この構造体は、呼び出し型メッセージと応答型メッセージの2つのタイプのメッセージを表すことができる.列挙タイプの変数msg_typeは示すために使用される
メッセージの特定のタイプであり、連合体には各タイプのデータ要素の構造体が含まれています.
enum msg_type {
	CALL=0,
	REPLY=1
};
[...]
/*
 * The rpc message
 */
struct rpc_msg {
	u_int32_t		rm_xid;
	enum msg_type		rm_direction;
	union {
		struct call_body RM_cmb;
		struct reply_body RM_rmb;
	} ru;
6、volatile修飾子はいつ使うべきですか?
volatile修飾子は、コンパイラに変数が関与する操作をいくつか最適化しないように伝えます.2つの特殊な場合、volatile修飾子を使用する必要があります.1つ目は、グラフィックアダプタのようなメモリマッピングハードウェア(memory-mapped hardware)に関連し、2つ目は、共有メモリ(shared memory、すなわち、2つ以上同時に実行されるプログラムで使用されるメモリ)に関連します.
ほとんどのコンピュータは、コンピュータのホストメモリよりもアクセス速度が速い一連のレジスタを持っています.良いコンパイラは「冗長ロードと記憶の削除」(redundant load and store removal)と呼ばれる最適化を行うことができる.すなわち、コンパイラは、対応するデータがレジスタに格納されているため、メモリからデータをロードする命令を削除することができる.もう1つは、対応するデータが再び変更される前にレジスタに保持されるため、メモリにデータを格納する命令を削除することができる.
ポインタ変数が通常のメモリ以外の場所、例えば周辺デバイスのメモリマッピングポートを指す場合、冗長マウントとストレージの最適化は有害である可能性があります.たとえば、ある操作の時間を調整するために、次の関数が使用される場合があります.
time_t time_addition(volatile const struct timer * t, int a),
{
         int    n
         int    x
         time_t  then
         x=0;
         then= t->value
         for (n=0; n<1000; n++)
         {
               x=x+a ;
         }
      return t->value - then;
}

上記の関数では、変数t->valueは実際にはハードウェアカウンタであり、その値は時間とともに増加する.この関数は、a値をxに1000回加算する操作を実行し、この1000回の加算の実行中にt−>valueが増加した値を返す.
volatile修飾子を使用しない場合、賢いコンパイラは、t->valueが関数の実行中に変更されないと考えている可能性があります.この関数内にt->valueの文が明確に変更されていないためです.これにより、コンパイラは、答えが永遠に0であるため、メモリからt->valueを再び読み込んでthenを減算する必要はないと考えます.したがって、コンパイラは関数を「最適化」し、その結果、関数の戻り値が常に0になる可能性があります.
ポインタ変数が共有メモリ内のデータを指す場合、冗長読み込みとストレージの最適化も有害である可能性があります.共有メモリは、通常、2つのプログラム間の相互通信を実現するために使用されます.すなわち、1つのプログラムが共有メモリにデータを格納し、別のプログラムがこのメモリからデータを読み出すようにします.共有メモリからデータを読み込んだり、共有メモリにデータを格納したりするコードがコンパイラによって最適化されると、プログラム間の通信に影響を与えます.   
7,1つの変数をconstとvolatileと同時に説明できますか?
いいですよ.const修飾子の意味は、変数の値がconst修飾子を使用したコードで修正できないことですが、このコード以外の手段で修正できないという意味ではありません.例えば、6の例では、1つのvolatile constポインタtによってtimer構造にアクセスする.関数time_addition()自体はt->valueの値を変更しないため、t->valueはconstとして説明される.ただし、コンピュータのハードウェアはこの値を変更するので、t->valueはvolatileとして説明されます.変数をconstとvolatileで同時に説明すると、この2つの修飾子はどちらでも先にできます.