コース内容#
構造体#
- キーワード: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 int でも同様である
-
しかし、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 章