コース内容#
構造体#
- キーワード:struct
- 構造体変数を宣言する際には、struct を付ける必要があり、構造体名だけではいけない
- struct person Yu;
- 構造体変数を宣言する際には、struct を付ける必要があり、構造体名だけではいけない
- 代入、アクセス:
- '.' 直接参照 —— '.' の前は構造体変数
- '->' 間接参照 ——'->' の前は構造体変数を指すポインタ
- 例:a. 自分の家に帰る、b. 友達に自分の家の住所を教える、私の家に来て
- 構造体変数を宣言する際の占有スペース
- 構造体の定義はスペースを占有しない
- 各変数の合計
- しかし、アライメントの原則を考慮する必要がある
- ① メンバーが占有するバイト数の最大値の整数倍
- ② メンバーが構造体の開始アドレスに対するオフセットがその自身のサイズで割り切れること
- 割り切れない場合は、前のメンバーの後ろにバイトを補充する
- 以下の例を参照:
-
-
- 変数 a、b、c は一緒に配置できず、b のオフセットは 4 の倍数である必要があるため、b のオフセットは 4、c のオフセットは 8 になる
- 参考構造体のアライメントの詳細——cnblogs
- スペースを節約する方法:同じタイプの変数をまとめて定義する
- 事前定義されたマクロを使用してアライメントのバイト数を強制的に変更することができる
- 参考#pragma pack () の使い方- 簡書
- 匿名構造体:struct {...;} var;
- 初期化時に一度だけ使用
- var は宣言された構造体変数である。構造体を定義しながら変数を宣言する
共用体(ユニオン)#
-
キーワード:union
- 一つのスペースを共有する
- スペースの大きさは共用体内の最大メモリ使用量のフィールドによって決まる
-
unsigned char の意義
-
-
まず、通常の意味で理解すると、バイトには符号ビットがないと考えられ、より重要なのは、バイトの値を int、long など(ではない)データタイプに代入すると、システムは追加の作業を行う
-
charの場合、システムは最上位ビットを符号ビットと見なすが、int は 16 または 32 ビットであり、最上位ビットを拡張する
-
そして unsigned char の場合、拡張されない
-
-
メモリ占有構造図
-
⭐ポインタとアドレス#
-
データの本源を操作することを学ぶ
-
アドレス取得演算子:&
- 取得するのは先頭アドレス:第 0 バイトのアドレス
-
各バイトはアドレス番号に対応する
-
64 ビット / 32 ビット:アドレスのアドレス範囲
- 32 ビットは最大 4GB のメモリをマッチさせる
- 2^30 Byte = 1 GB 👉 32 ビットは最大 4 GB をサポート
- 32 ビットは最大 4GB のメモリをマッチさせる
-
ポインタ変数
- アドレスを保存するために使用され、アドレスは一般に 16 進数で表される
- ポインタのタイプ(int、char)に関係なく、それらの地位は同じで、アドレスを保存する
- 占有スペースの大きさ:64 ビット - 8 バイト;32 ビット - 4 バイト(64 ビットはアドレスのビット数が 64 であることを示す)
- int *p は char *q に渡すことができますか?完全に可能で、どちらも 8 バイトを占有する(64 ビット)
- タイプの区別の意義は以下の通り:等価形式変換
- int *p;
- 定義時に * は p がポインタ変数であることを示す
- 定義後、p はポインタを表し、*p の * は値取得操作を示す
- ポインタ変数も変数である!
- ポインタ Q を使用してポインタ p のアドレスを保存することができる
-
scanf 関数の説明
- 変数の値を変更する必要があるため、アドレスで値を渡す必要がある
- アドレスを渡すパラメータは出力パラメータと呼ばれることができ、操作は関数外にも反映される
- しかし、関数の値渡しは、その操作はスコープ内でのみ有効である
- 変数の値を変更する必要があるため、アドレスで値を渡す必要がある
-
等価形式変換
-
- 値取得操作
- +1 の 1 のオフセットは **p が示すタイプのバイト数(タイプの区別の意義)** に依存する
- 前提:ポインタ p は構造体変数 a を指す
関数ポインタ#
-
変数:曖昧さを避けるために、関数変数の宣言は次のようにするべきである
-
- *add(add は関数名)を括弧で囲む
-
-
タイプ:typedef を使用して変数をタイプに昇格させることができる
-
- タイプを使用して変数を定義することができる
-
-
typedef の二つの使い方
- 組み込みタイプ、構造体タイプのリネーム
- typedef long long lint;
- typedef char * pcahr; // char * → pchar
- これは #define に似ているが、微妙な違いがある。詳細はコードデモを参照
- typedef struct __node { int x,y;} Node, *PNode;
- 二つのエイリアスを同時に定義
- PNode p; // Node 構造体タイプのポインタ変数を定義できる
- タイプに昇格
- typedef int (*func)(int);
- 無数の関数ポインタ変数を定義できる
- typedef int (*func)(int);
- 組み込みタイプ、構造体タイプのリネーム
-
main 関数のパラメータ
-
-
パラメータがある場合、パラメータを渡すのは誰か?オペレーティングシステム
-
return 0; // システムに返す
-
"echo $?" はシステムの前回のコマンド実行が成功したかどうかを判断するために使用できる
- 0:成功
- 非 0:不成功
-
パラメータの解釈
- argc:パラメータの数
- argv:対応する二次元配列、argc 個の文字列を受け取る、一次元配列は一行の文字列を受け取る
- *arr [] は arr [][] に対応する
- env:環境変数
- env は char * を指すポインタである
- env はアドレスであり、文字配列のアドレスを指す
- env このアドレスはさらに進むことができるため、実際には二次元文字配列に対応する
- ❓**env と * env [] の定義方法には違いがあるか?ここでは違いはない!
- env は char * を指すポインタである
- ❓❗int ** を使って二次元配列を表現できない
- malloc で定義する必要がある?
- 静的配列の場合、連続性を保証できないようだ
- ここではネットでじっくり考える必要がある!
- 参考なぜ二次元配列を二次ポインタで直接指すことができないのか-CSDN
-
質問:ゼ哥、char str [10][10] の二次元配列を定義した場合、char** で受け取れません。main 関数の **env は文字列配列を受け取ることができますが、どう理解すればいいですか、文字列配列は二次元配列ではないのですか?
回答:後者は malloc で作成された動的配列です
授業中の練習#
一:構造体のバイトアライメント#
-
-
定義の順序が異なる
-
左:8 バイト;右:12 バイト
二:共用体のメモリ図#
👇
三:ip を整数に変換#
-
-
192.168.1.2
- 各セグメントは最大 255 であり、1 バイトで表現でき、unsigned char に対応する
- 合計で 4 バイト、int タイプに対応する
-
コード
-
* union内のstructは変数を宣言する必要がある * sscanfを使わなくてもいい?使わない場合、xxx.xxx.xxx.xxxの各セグメントの値を読み取るのが不便になる * ビッグエンディアン、小エンディアン * 小→数字の低位→低アドレス * 大→数字の低位→高アドレス
ip(192.168.0.1)を小エンディアンで各セグメント順に保存 | ||||
---|---|---|---|---|
int 数字バイトアドレス番号 | 0 | 1 | 2 | 3 |
ip の保存 | 1 | 0 | 168 | 192 |
-
-
- 一般的なコンピュータは小エンディアンである
- 小エンディアンかどうかを判断するには、int タイプの num = 1 を定義し、最上位ビットの値を判断する
- int タイプのアドレスを char タイプのアドレスにキャストすると、第 0 バイトのアドレス上の値を取得できる
- 数字を直接読むと、本機のバイト順序に従って高位 / 低位から読み始めるのか?はい
- 本端、対端のバイト順序が一致しない場合はどうするか
- プロセス:本機のバイト順序→ネットワークのバイト順序(統一基準)→対端のバイト順序
- 読み取るときだけバイト順序を区別する必要があり、他の状況では考慮する必要はない
- バイトまで細分化されていない場合、バイト順序を体感することはできないはずである
- 参考バイト順序の理解- 阮一峰
- 出力
-
-
追加:a [1].x をできるだけ多くの形式で表現する#
【少なくとも 30 種類、前述の等価形式、ネストを利用!】
-
-
コード
-
ハイライトノート#
コードデモ#
コード#
構造体のオフセット計算、マクロ定義のタイプと typedef の違い、主関数のパラメータの詳細#
-
-
-
文字列は '\0' で終わる必要があり、読み取りの判断を終了するのが便利である(num の低位が 0 であればよい)
-
アドレスは long int タイプに対応する(64 ビット - 8 バイト、32 ビット - 4 バイト)
-
コメントアウトされた offset () マクロ定義にはどのような欠点があるか?
- 変数を定義する必要があり、スペースを占有する
- したがって、空のアドレス(0 または NULL)を強制的にキャストすることができる
-
タイプエイリアスを定義する際、マクロ定義は単純な置換に過ぎず、typedef の方が便利である
-
main 関数の入力パラメータ
- スペースで区切られ、二重引用符で複数の文字列を統合できる
-
プログラム出力
-
追加知識#
- C++ のオブジェクト指向において、構造体はクラスである
- 参照の効率はポインタより高く、ポインタは一つ多く渡す
- 既知のメモリアドレスから値を読み取る方法
- アドレスをポインタに渡し、値取得演算子 * を使用する
- シンタックスシュガー
- コンピュータ言語に追加されたある種の構文で、言語の機能には影響しないが、プログラマにとっては使いやすくなる
- プログラムをより簡潔にし、可読性を高める
思考点#
ヒント#
-
ポインタとアドレスの応用関係に重点を置く
-
ノートを取ることは非常に重要である
- typora で記録
-
履歴書
- ShareLaTeXを使用して作成できる
- コンピュータ関連の履歴書:白黒、インデントに注意し、しっかりとした印象を与える
-
参考書の第 9、10 章