コース内容#
if 文、switch 文#
- (C 言語の関係演算子)
- 0 → false;1 → true
- false 👉 0、NULL(null)、'\0'
- <= → =<
- ! 否定 (関係演算子)
- 注意すべき点:ビット反転演算子~(ビット演算子)
- ⭐!! 非非操作:論理的な正規化
- すべての真値を 1 に統一し、偽値は 0 のまま
- 順序構造
- 略
- 分岐構造
IF 文#
-
-
(式)
- 任意の式には論理的な戻り値がある
- 代入式 a = 123; の場合:
- 戻り値は a 変数の値 123
-
{コードブロック;}
- 1 つの文とは何かを理解する?
- 単文:“;” で終わる文
- int a = 1, b = 234; // これは 1 つの文であり、“,” の前後は 1 つの式に分かれる
- 空文:ただの “;” で、効果がない
- 複合文
- 中括弧を使用
- つまりコードブロック👆
- 単文:“;” で終わる文
- 1 つの文とは何かを理解する?
-
if else 文
- 三項演算子を使って置き換えることができる
- ...? ...: ...;
- 三項演算子を使って置き換えることができる
SWITCH 文#
-
-
変数 a は明確な唯一の整数値を持つ必要がある!
- 文字もこのようなマッピングを持つ:ASCII コード
-
case に入ると、順次後続のコードを実行
- break、continue または構造の末尾に出会うまで
- その後の case が満たされているかどうかを再度判断する必要はない
-
default 分岐
- すべての case が満たされない場合に入る、if else 文の else に相当
CPU の分岐予測#
力扣の問題 -回文整数から導入
船長の超高効率コードは以下の通り:
-
-
赤枠で実現されているのは x<0
- 0 未満の整数をフィルタリングするため、負数は確実に回文整数ではない
-
この文はオペレーティングシステムのカーネル内に現れる:
-
-
!!(x) 論理的正規化、結果は 1 または 0 のみ
- 成立するかどうかの状況は、処理中のプログラムの CPU に通知される
⭐CPU のプログラム実行プロセス解析
- C 言語プログラムの実行プロセス:コンパイル後に実行可能ファイルを生成→メモリにロード→CPU が実行
- キャッシュ
- 一次キャッシュ:最も速く、容量は最小
- CPU 命令実行方式の比較
- 初期の直列(アドレス取得、命令の事前解析、データの書き込み、実行、メモリへの書き戻し)
- 現在の並列
- 示意図は以下の通り:
-
-
5 つの命令に対して
- 直列方式では 5 * 5 = 25 クロックサイクルが必要
- 並列方式では 5 + 4 = 9 クロックサイクルのみ
-
命令が非常に多い場合、効率が約 5 倍向上する
-
- したがって!CPU は順序構造が好き
- 分岐構造は嫌い(if else 文は少なく使用すること)
- 分岐予測の問題
- 並列方式では、CPU は分岐構造の次のステップが誰であるかを知らず、どのステップを事前にロードすべきか?
- CPU は実際には次のステップをランダムにロードする
- したがって、予測が外れた場合、後続の処理は無駄になり、その時に戻って再処理する必要がある
- CPU は実際には次のステップをランダムにロードする
- そして__builtin_expect () が行うのは、CPU にどの分岐状況がより発生しやすいかを伝えることです!!!
ループ構造#
- WHILE 文
- while () {コードブロック;}
- 先に判断し、次に実行
- do {コードブロック;} while (式); ←セミコロンに注意!
- 少なくとも 1 回は実行される
- while () {コードブロック;}
- FOR 文
- for (初期化;ループ条件;実行後の操作) {コードブロック;}
- ループを非常に美しくする、3 つの部分から構成される
- これら 3 つの部分はすべて省略可能
- 例えば:for (; ;);、これは while (1); に相当する
- 上記は無限ループで、何もない
授業中の練習#
-
-
条件判断の冗長チェックを忘れずに
-
不正な入力時(例えば "q")に無限ループに陥る問題
- 解決方法と考察プロセスは以下に詳述:考察点 1
-
コード
-
冗長版
-
-
❗ここで scanf の \n は危険です!\n を取り除く必要があります、さもなければもう 1 つの数を読み取る必要があります
-
次の数を入力する際に \n を読み取るため
-
⭐参考ブログ:用 "% d\n" 要多读一个数? —— scanf () 函数的那些坑
-
-
-
簡潔版
-
-
-
①
-
②
- switch 文の終了特性に慣れる
- コード
-
-
-
コード
-
while 方式の場合、最初の判断が成立すれば、do...while 方式と違いはありません
-
-
コード
-
ハイライトノート#
- ⭐条件判断文の冗長チェック(カット)を行うことで、通常はより簡潔なコードが得られます
- すべての判断式が 0 であるかどうかを直接使用することができ、if (! 式) を使用できます
- if (式) 単文の場合、論理積を利用できます
- (式) && 単文
- 例えば i && printf (" "); は、最初のループ以外で空白を出力するために使用されます
- 単文は {コードブロック;} であってはなりません
- if (式) 単文の場合、論理積を利用できます
- if...else を三項演算子で置き換えます
- すべての判断式が 0 であるかどうかを直接使用することができ、if (! 式) を使用できます
- 一般的にループ変数 i は初期化部分で定義します:int i = 0
- 事前に前に置く必要はありません
- より規範的で簡潔:変数を使用する前に定義し、遠くに離れないようにします
- スコープの問題
- その変数はループ内でのみ使用されます
- 事前に前に置く必要はありません
- ++i は i++ より速い?
- ++i を使えるなら使います
- 関数スタックの観点から:👇
- ++i は直接スタックに i+1 の値を入れます
- i++ の場合、まず i をスタックに入れ、その後 i+1 をスタックに入れます
- ++i を使えるなら使います
コードデモ#
分岐構造(6.struct_program.cpp)#
-
-
a - b は a == b の判等を代替できます
-
if else は三項演算子で置き換えることができます
-
-
出力 1:false
- a++ と && の演算優先度は?
- ++ の優先度は && より高い
- a++ の外側の () はなぜ ++ の演算優先度を && より高くしないのか?
- 優先されます
- ここで false が出力されることは関係ありません~
- if 判断にとって、まず a を判断し、その後に ++
- a++ 式の値は ++ されていない a です
- a++ と && の演算優先度は?
-
出力 2:a = 1, b = 0
- ⭐論理積 -- 短絡規則
- 前に偽値があれば、後には進まない【賢い人のやり方】
- ⭐論理積 -- 短絡規則
-
-
(続き a = 1, b = 0)
-
出力 1:true
-
出力 2:a = 2, b = 0
- ⭐論理和 -- 長路規則
- 前に真値があれば、後には進まない
- 後の式を実行するには、前の式がすべて偽値でなければならない
- ⭐論理和 -- 長路規則
-
-
空白で区切られた(末尾に空白なし)の出力方法の参考
- 方法 1:最初のループでない場合、前に空白を出力
- 最初は得られる:i == 0
- 43 行目を最適化可能:i && printf(" ");
- 方法 2:最後のループでない場合、後に空白を出力
- 方法 1:最初のループでない場合、前に空白を出力
-
rand()
-
<stdlib.h> をインクルードする必要があります
-
出力されるのはランダムな符号なし数です
- rand () % 100 は 0-99 の数をランダムに出力できます
-
⭐実際には固定されたランダム
-
-
**srand ()** を通じてランダムシードを設定します
-
srand(time(0))
- time(0)
- 現在の時間を取得し、<time.h> をインクルードする必要があります
- 秒単位で、1970.1.1 から現在までの秒数
- time(0)
-
値が変化していることがわかりますが、コンピュータには真の意味でのランダムはありません
-
-
-
-
-
最適化版
-
上記のコードには 3 つの簡略化できる部分があります!2 つの if を削除できます
-
-
⭐~
- ビット演算と剰余の変換:% 2 は & 1 に等しい
- % (2 ^ n) は & (2 ^ n - 1) に等しい
- +1 と真値 1 の等価
- 論理積の短絡規則
- ビット演算と剰余の変換:% 2 は & 1 に等しい
-
ループ構造(7.cpp)#
回文整数の判断(十進法)
-
-
負数に対して特別な判断を忘れないでください!
-
もし二進法で回文整数を判断したい場合は?
-
円の部分をすべて 2 に変更すればよい
- コンピュータの底層では実際には二進法でデータが保存されています
- どの進数でも回文を判断できます
-
base 進数での回文整数の判断
-
-
整数の桁数を計算する(十進法)
- while ループと do...while ループの微妙な違いの比較
- 重要なのは最初のループの条件が成立するかどうかです~
-
- 非 0 の数字を入力した場合、digit と digit2 に違いはありません
-
- 数字 0 を入力した場合、違いがあります
-
-
したがって 0 の状況で桁数を判断する
* do...while を使用する
* または特別な判断を行う
-
- 重要なのは最初のループの条件が成立するかどうかです~
追加の知識点#
-
switch 文
- case の後に変数を宣言することは許可されていません
- default の末尾にも break を加えるべきですか?
- 追加しても追加しなくてもよい、break を加えるのはスタイルを統一するためです
- Python は switch 文をサポートしていません
-
C 言語では、main 関数の末尾に return 文がない場合、どのような影響がありますか?
- C99 からは、基本的に影響はなく、標準は自動的にreturn 0; を補完することを要求します。
- C99 以前は、制御が main 関数の末尾に到達しても return がない場合、未定義の動作となります。
-
浮動小数点数の判定
- == を直接使用すると不正確で、差があるかどうかを極小値で判定します
-
__builtin_expect () には 6 つ以上の兄弟がいます
-
-
ビットの重みが左移動と右移動の倍数を決定します
- 十進法では、左に 1 ビット移動すると * 10
- 二進法では、左に 1 ビット移動すると * 2
考察点#
-
💡(解決)5.if.cpp、つまり授業中の練習 1。不正な値(例えば文字‘q’)を入力すると無限ループに陥ります
-
‘q’の ASCII コードに関係がありますか?
- 関係ありません~
- % d 形式で‘q’を出力すると、アドレスのように見え、単一の実行では固定されているはずで、n を初期化する際にランダムに割り当てられた値です
- 正常な値を入力した後に誤った値を入力すると、この時点でその値は以前の n 値になります
- 実際には、値を読み取って n を変更することはありません
-
‘q’の値を読み取れません
- scanf の戻り値は 0 です
- しかし、次の入力に移動することはなく、最初の講義の %[^\n] に遭遇した際の無限ループのような状況です
-
⭐もちろん! 同様に getchar () を使用してこの不正な値を飲み込むことができます
- 入力 qw は不正ですか?
- はい、getchar () を 2 回飲み込む必要があります
- 入力 qw は不正ですか?
-
修正後のコード
-
-
getchar () は if (!ret) の中に置く方が良い、無効な文字を読み込むときだけ文字を飲み込む
-
上のコード:getcharは空白と不正な文字を飲み込みます
-
下のコード:不正な文字だけを飲み込みます
-
- 飲み込まれた文字が空白ではなく不正な文字だけであることを示すために、ここで printf 関数を追加しました
-
-
-
-
-
-
💡同様に、5.if.cpp(ループがあり、手動で停止する必要がある)を使用して > output で標準出力をリダイレクトすると、出力がリダイレクトされたファイルに書き込まれない問題が発生します
- 具体的な状況は海賊 QA で質問し、議論を期待しています:出力リダイレクトはどの条件下で出力をファイルに書き込むのか?(stdout、./a.out > output、Ctrl+C/Ctrl+D)
- Ctrl+C は書き込まれず、Ctrl+D は書き込まれます
Ctrl+C:Linux でのデフォルトの中断キーで、このキーを入力すると、システムは実行中のプログラムとシェルに中断信号を送信します。
Ctrl+D:Linux での標準入力出力のEOF。標準入力出力デバイスでこのシンボルに遭遇すると、プログラムはファイルの末尾を読み取ったと見なし、入力または出力を終了します。
- 不正な入力が大量の出力を引き起こす場合、Ctrl+C も書き込むことができます
- 疑問:これはキャッシュが不足しているため、出力を output ファイルに強制的に書き込むことになったのですか?
- そのように理解できます
- 疑問:では、この部分の output ファイルへの書き込みは、キャッシュオーバーフローの出力だけですか、それとも Ctrl+C 前のすべての出力ですか?
- オーバーフローの出力です
- 疑問:これはキャッシュが不足しているため、出力を output ファイルに強制的に書き込むことになったのですか?
- コード 5.switch.cpp:なぜ文字などを入力すると無限ループになるのですか?
- 5.if.cpp と同様、考察点 1 を参照してください
ヒント#
- OJ の問題解決方法
- vim でコードを書く
- cat でコードを表示してコピーする
- 提出する
- クラシックな問題:年月日が合理的かどうかを判断する
- そんなに多くの if else を使う必要がありますか?
- 空間を時間で交換できます:各月の日数を配列に保存します
- 参考書第 6 章 6.4 節