C 言語ノート#
Frank に感謝します。彼は多くの学生に現代 C 言語への扉を開いてくれました。
[TOC]
6 月に書いたブログ記事はあまりにも内容が薄かった。このノートから、ブログ記事の内容は一段と高まります!
内容は講師 Micro_Frank の無料コースからのもので、ノートは具体的に講義内容に基づいています。著作権侵害があれば、ブログ主にご連絡ください!
第 1 章#
最初のプログラム:C 言語の実行プロセス#
#include <stdio.h>
int main()
{
printf("Hello World!\n");
return 0;
}
このプログラムは C 言語の基本的な例であり、1 行のテキストを出力する方法を示しています。#include <stdio.h>
文は標準入出力ライブラリを含み、int main()
はプログラムのエントリーポイントで、printf("Hello World!\n");
は文字列を印刷するために使用され、return 0;
はプログラムが成功裏に終了したことを示します。
C 言語の物語・1#
教科書の選択:C Primer Plus#
英語と中国語の対照読書をお勧めします。
C 言語の物語・2#
宣言 定義 代入#
#include <stdio.h>
int main()
{ //宣言 declaration
int number = 123;
//定義 definition
printf("%d\n", number);
//代入 assignment
return 0;
} //Frank_PPT講師はこのセクションでTABキーとタブの使用についても説明しました。
コードは主に宣言、定義、代入の概念を含む簡単な C プログラムです。以下は詳細な分析です: c #include // 標準入出力ライブラリをインクルード
- #include
: これはプリプロセッサディレクティブで、標準入出力ライブラリをインクルードし、プログラムがprintf
などの関数を使用して入出力操作を行えるようにします。 c int main() { // メイン関数、プログラムのエントリ
- int main()
: メイン関数を定義し、C プログラムの実行はここから始まります。戻り値の型はint
で、この関数が整数値を返すことを示します。 c int number = 123; // 整数型変数を定義し初期化
- int number = 123;
: ここでは、number
という名前の整数型変数を宣言し、初期化しています。C 言語では、変数の宣言と定義は通常 1 つの文で行われます。 c printf("%d\n", number); // 変数numberの値を印刷
- printf("%d\n", number);
: この行のコードの意図は、変数number
の値を出力することですが、誤りがあります。正しい使い方はprintf("%d\n", number);
です。この文では、%d
は整数型データを出力するためのフォーマット指定子で、\n
は改行文字です。誤ったバージョンでは、文字列"number"
が普通のテキストとして出力され、変数の値ではありません。 c return 0; // メイン関数を終了し、0を返してプログラムが成功したことを示す }
- return 0;
: この行のコードはメイン関数の終了を示し、0
をオペレーティングシステムに返します。通常、プログラムが成功裏に実行されたことを示します。 修正コード 上記の分析を考慮すると、正しいコードは次のようになります: c #include <stdio.h> int main() { int number = 123; // 変数を定義し初期化 printf("%d\n", number); // 変数の値を正しく印刷 return 0; // 0を返し、プログラムが成功したことを示す }
### まとめ このコードは、変数の宣言と定義、標準ライブラリの使用、出力フォーマットの使用、メインプログラムの戻り値を含む基本的な C 言語の構造を示しています。唯一注意が必要なのはprintf
のフォーマット文字列の正しい使用です。
識別子#
identifier:
nondigit
identifier
digit
nondigit:
_ a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
digit:
0 1 2 3 4 5 6 7 8 9
コンパイラは大文字と小文字の文字を異なる文字として扱います。
add
ADD
Add
aDD
Microsoft 専用
2 つのアンダースコアで始まるか、1 つのアンダースコアの後に大文字が続く名前。
Microsoft はアンダースコアと大文字をマクロ名の先頭に使用し、ダブルアンダースコアを Microsoft 特有のキーワード名の先頭に使用します。
有効な識別子
j
count
temp1
top_of_page
skip12
LastNum
キーワード#
第 2 章 データ型#
コメント#
//行
コメント
/*
ブロック
注
釈
*/
進数#
変数#
//int 変数
#include <stdio.h>
int main(){
//整数 代入
int sun_flower = 9673;
printf("陽光の現在の値:%d\n", sun_flower);
return 0;
}
整数型#
構文:定義された型 識別子 = 値
int 型のメモリ内の保存#
コンピュータのストレージ単位:
1G = 1024MB
1MB = 1024KiB
1KiB = 1024Btyes
1024Btye = 8Bit
00000000 | 00000000 | 00000000 | 00000000 | int |
---|
0は正負を表示するために使用され、合計で $ 2^{32} - 1 $ 個の値を表すことができます。
#include <stdio.h>
int main()
{
int number = 100;
//10進数で表示
printf("Decimal: %d\n", number);
printf("Octal: %o\n", number);
printf("Hexadeciaml (lowercase) : %x\n", number);
printf("Hexadeciaml (uppercase) : %X\n", number);
return 0;
}
浮動小数点数#
浮動小数点数には:2.75、3.16E7、2e-8 などがあります。
コンピュータは浮動小数点数をどのように保存するのか?#
浮動小数点数:符号(1)+ 小数(23)+ 指数(8) int 型 32 ビット
One Step : 3.14159=> $ 314159×10^{-5} $ まず int 型に変換します。
Two Step : 指数をどのように保存するのか?$ 2^{8} $ = 256 0 から 255 まで、127 を左に偏らせた数は負の指数で、偏差は指数値です。
コンピュータにおける浮動小数点数の保存は通常 IEEE 754 標準に従います。これは浮動小数点数の表現方法を定義する標準です。浮動小数点数は主に 3 つの部分から構成されます:符号ビット、指数部分、尾数(または有効数字)。以下は浮動小数点数の一般的な保存形式です: 1. 符号ビット:1 ビットを占め、数値の正負を示します。0 は正数、1 は負数を示します。 2. 指数部分:一定のビット数を占め(例えば、32 ビット浮動小数点数は 8 ビット、64 ビット浮動小数点数は 11 ビットを使用)、数値の大きさの範囲を示します。指数は一般にオフセット(すなわち、一定の数を加えること)を加えたもので、負の指数値を表すことができます。 3. 尾数:残りのビット数を占めます。標準の浮動小数点表現では、尾数は通常 1.XXXX の形式で保存され、前に 1 が暗黙的に存在します(これを暗黙の先頭ビットと呼びます)。 32 ビット浮動小数点数(単精度)と 64 ビット浮動小数点数(倍精度)の保存構造において、具体的なビットの割り当ては次の通りです: - 単精度(32 ビット): - 符号ビット:1 ビット - 指数:8 ビット - 尾数:23 ビット - 倍精度(64 ビット): - 符号ビット:1 ビット - 指数:11 ビット - 尾数:52 ビット この形式を使用することで、コンピュータは非常に大きな数値範囲と小数部分の精度を表現できますが、浮動小数点数の表現には丸め誤差や表現範囲の制限もあります。 コンピュータでは、浮動小数点数は通常二進数形式で保存され、これはメモリ内での保存と操作が二進数値に基づいて行われることを意味します。これは浮動小数点数の演算をより効率的にします。
float と double の型#
float 型の範囲は約 3.4E-38 から 3.4E+38 の間です。
小数は一般に > 1 であるため、正規化された浮動小数点数はしばしば 1 より大きく、尾数(残りのビット数を占めます)。標準の浮動小数点表現では、尾数は通常 1.XXXX の形式で保存され、前に 1 が暗黙的に存在します(これを暗黙の先頭ビットと呼びます)。
したがって float の尾数の暗黙的な長さは 24 ビット、double の尾数の暗黙的な長さは 53 ビットです。
単精度(32 ビット)- 倍精度(64 ビット)
保存 > 精度の場合は double を選択し、保存 < 精度の場合は float を選択します。
浮動小数点数の印刷出力#
#include <stdio.h>
int main() {
float temperture = 36.5f;
float humidity = 48.3f;
float speed_of_sound = 343.5e2f;
float lenght = 12.3f, width = 23.45f, height = 34.56f;
printf("Temperture: %f\n", temperture);
printf("Humidity: %f\n", humidity);
printf("Speed_of_Sound: %f\n", speed_of_sound);
printf("Lenght: %f x %f x %f\n", lenght, width, height);
//精度を失う
//double %lf , float %f
return 0;
}
C99 における浮動小数点数の規定#
#include <stdio.h>
int main () {
float num = 123.456;
printf("Using %%f: %f\n", num);
//%e %E 科学的記数法のフォーマット出力
printf("Using %%e: %e\n", num);
printf("Using %%E: %E\n", num);
//%a %A 十六進浮動小数点数のp記数法
printf("Using %%a: %a\n", num);
printf("Using %%A: %A\n", num);
return 0;
}
浮動小数点数のオーバーフロー#
#include <stdio.h>
#include <float.h>//開いて確認
int main() {
float max_float = FLT_MAX;
float overflow = max_float * 1000.0f;
//オーバーフロー
float min_float = FLT_MIN;
float underfloat = min_float / 1000.0f;
//アンダーフロー
printf("最大浮動小数点数: %e\n", max_float);
printf("オーバーフロー: %e\n", overflow);
printf("最小浮動小数点数: %e\n", min_float);
printf("アンダーフロー: %e\n", underfloat);
return 0;
}
Nan & Infinity#
#include <stdio.h>
#include <float.h>
#include <math.h>
int main() {
//正の無限大
float positive_infinty = INFINITY;
printf("正の無限大: %f\n", positive_infinty);
//負の無限大
float negative_infinty = -INFINITY;
printf("負の無限大: %f\n", negative_infinty);
//0で割ることによって生成される無限大
float num = 1.0f;
float infinity = num / 0.0f;
printf("0.0 / 0.0 = %f\n", nan);
//Nan 0/0
//float nan = 0.0f /0.0f;
//printf("0.0f / 0.0f =%f\n", nan)
//負数の平方根
float negative_sqrt = sqrt(-1.0f);
printf("sqrt(1.0f) = %f\n", negative_sqrt);
return 0;
}
最近偶数丸め基準(銀行家の丸め)#
#include <stdio.h>
int main() {
//四捨五入
//IEEE 754
//最近偶数丸め 近似値に丸める、偶数に結びつく
//銀行家の丸め
//3.14159
float number = 3.14159f;
printf("%.4f\n", number);
//3.15
//3.25
return 0;
}
double,long double 科学研究と企業の違い#
#include <stdio.h>
int main() {
//double
//float 精度を失う
//3Dレンダリング
//利息 NASA
//浮動小数点定数 3.14
//3.14はデフォルトでdouble型 特に注記がない限り3.14f
return 0;
}
コンピュータ科学とプログラミングにおいて、double
とlong double
は小数を持つ数字を表すための 2 つの浮動小数点数データ型です。これらは科学研究と企業での使用において、時には異なる焦点を持つことがあります。主に以下の点で異なります:
- 精度の要求:
- 科学研究:科学研究はしばしば高度に正確な計算を含み、物理学や化学のシミュレーションなどが含まれます。この場合、
long double
が優先的に使用されることがあります。 - 企業:ビジネスアプリケーションでは、通常、浮動小数点数の精度要求はそれほど厳しくなく、
double
がほとんどのニーズを満たすことができます。特に財務計算や統計分析などのシーンで。
- 性能の考慮:
- 科学研究:科学計算は計算の正確性にもっと焦点を当てるかもしれませんが、高性能計算の一部では、精度アルゴリズムと計算性能の間のトレードオフを考慮する必要があります。
- 企業:企業アプリケーションはしばしば性能、応答時間、リソース利用効率を追求するため、
double
を使用する傾向があります。
- プラットフォーム依存性:
- 科学研究:研究者は異なるハードウェアプラットフォーム間の違いを考慮し、浮動小数点数型を選択する際にクロスプラットフォームの互換性を考慮します。
- 企業:企業は通常、ソフトウェアの保守性と開発効率に焦点を当て、より一般的で標準化された
double
型を選択します。
- 言語とツールのサポート:
- 科学研究:研究者は特定の科学計算ライブラリ(NumPy、SciPy など)を使用することがあり、これらのライブラリはさまざまな浮動小数点数型のサポートを考慮しています。
- 企業:企業環境では、開発チームは一般的なデータ型を使用する傾向があり、標準ライブラリとストレージプロセスに依存するため、
double
型がより一般的です。
以上のように、double
とlong double
は浮動小数点数型であるものの、科学研究と企業での具体的な使用状況は、要求、性能、プラットフォームの違いによって異なる場合があります。
float と double の有効精度の比較 原理と計算#
#include <stdio.h>
int main() {
float float_num = 1.0 / 3.0;
double double_num = 1.0 / 3.0;
printf("Float precision: %20f\n", float_num);
printf("Double precision: %.20lf\n", double_num);
//floatとdoubleの有効精度の比較
printf("Defined max precision for double: %d\n", FLT_DIG);
printf("Defined max precision for float: %d\n", DBL_DIG);
//53をlog₂10で割る 24をlog₂10で割る それが精度です。
}
float(単精度浮動小数点数):通常 4 バイト(32 ビット)を占め、1 ビットが符号ビット、8 ビットが指数ビット、23 ビットが尾数(有効数字)です。通常、有効精度は約 7 桁の 10 進数です。
double(倍精度浮動小数点数):通常 8 バイト(64 ビット)を占め、1 ビットが符号ビット、11 ビットが指数ビット、52 ビットが尾数です。通常、有効精度は約 15 から 16 桁の 10 進数です。
浮動小数点数の表現は IEEE 754 標準に従います。その値は以下の公式で表すことができます: [\text = (-1)^{sign} \times (1 + fraction) \times 2^{exponent} ]
sign: 符号ビット、正負を決定します。 fraction: 尾数、数値の精度を決定します。 exponent: 指数、数値の大きさを決定します。 bias: オフセット、float
の場合は 127、double
の場合は 1023 です。
銀行では ** 固定小数点数(データベース MySQL)** を使用します。
char & ASCII#
#include <stdio.h>
int main() {
char mych = 'a'; //実際にはint型で保存され、Aをint型に変換 int(97)
printf("mych: %c\n");
//ASCIIアメリカ情報標準コード 一般に7bitを使用し、128文字を含む
}
エスケープシーケンス バックスラッシュ \#
エスケープシーケンス | 表示 |
---|---|
\a | 鳴音(アラーム) |
\b | バックスペース |
\f | ページ送り |
\n | 改行 |
\r | キャリッジリターン |
\t | 水平タブ |
\v | 垂直タブ |
\' | シングルクォート |
\" | ダブルクォート |
\\ | バックスラッシュ |
\? | テキストの疑問符 |
\ ooo | 8 進数表記の ASCII 文字 |
\x hh | 16 進数表記の ASCII 文字 |
\x hhhh | 16 進数表記の Unicode 文字(このエスケープシーケンスがワイドキャラクタ定数または Unicode 文字列テキストに使用される場合)。例えば、WCHAR f = L'\x4e00' またはWCHAR b[] = L"The Chinese character for one is \x4e00" 。 |
画面をクリアする print("\033[2J");
カーソルを移動する print("\033[%d;%dH", 3, 3);
ブール型 bool 型#
#include <stdio.h>
#include <stdbool.h>
int main() {
//trueまたはfalse
//1または0に変換
bool is_game_win = true;
bool is_game_over = false;
return 0;
}
定数 const と #define マクロ#
#include <stdio.h>
#define PI 3.14
int main() {
//定数
const double MAX_USER = 100;
printf("PI: %lf\n", PI);
return 0;
}
第 2 章 終了の言葉#
第 3 章 演算子#
演算子の紹介#
1、算術演算子
- 単項
++
(インクリメント)、--
(デクリメント)、+
(加算)と-
(減算)演算子 - 二項
*
(乗算)、/
(除算)、%
(余剰)、+
(加法)と-
(減法)
2、関係演算子
演算子 | テストする関係 |
---|---|
< | 第 1 オペランドが第 2 オペランドより小さい |
> | 第 1 オペランドが第 2 オペランドより大きい |
<= | 第 1 オペランドが第 2 オペランド以下 |
>= | 第 1 オペランドが第 2 オペランド以上 |
== | 第 1 オペランドが第 2 オペランドと等しい |
!= | 第 1 オペランドが第 2 オペランドと等しくない |
データオブジェクト 左辺値と右辺値#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main() {
//データオブジェクト
//左辺値 Lvalue
//右辺値 Rvalue
//演算子
uint32_t apple_box = 5;
uint32_t orange_box = 5;
printf("リンゴの箱の中には %" PRIu32 "個のリンゴがあります\n", apple_box);
printf("オレンジの箱の中には %" PRIu32 "個のオレンジがあります\n", orange_box);
uint32_t total_fruit = apple_box + orange_box;
printf("箱の中には %" PRIu32 "個の果物があります\n", total_fruit);
return 0;
}
多重代入#
前置後置インクリメントとデクリメント#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main() {
int32_t value = 5;
int32_t result_1;
int32_t result_2;
//後置インクリメント、先に代入し、その後++
result_1 = value++;
//前置デクリメント、先に--、その後代入
result_2 = --value;
printf("後置インクリメント後、result_1 = %" PRIu32 ", result_2 = %" PRIu32 ", value = %" PRIu32 "\n", result_1, result_2, value);
return 0;
}
ビットシフト演算子#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
uint8_t num = 22;
num >> 2;//高位を0で埋め、低位を排除
return 0;
}
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
uint8_t num = 22;
//num >> 2; 高位を0で埋め、低位を排除
uint8_t num = 22;
printf("元の数: %" PRIu8 " (バイナリ: 00010110)\n", num);
uint8_t left_shifted = num << 2;
printf("2ビット左シフト: %" PRIu8 " (バイナリ: 01011000)\n", left_shifted);
uint8_t right_shifted = num >> 2;
printf("2ビット右シフト: %" PRIu8 " (バイナリ: 00000101)\n", right_shifted);
return 0;
}
乗算演算:ビットシフト演算子は直接の * 処理よりも速いです。
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
uint8_t num = 25;
//uint32_t result = num * 1024;
printf("結果: %" PRIu32 "\n", result);
ALUは基本的な演算を担当します。
return 0;
}
ビットシフト演算子が直接の乗算演算子(*)よりも速く処理される理由は、主に底層の実装の違いによるものです。
具体的な理由は以下の通りです:
底層操作:乗算演算はコンピュータの底層では通常、加算やビットシフト演算よりも複雑です。乗算操作は、複数回の加算と中間結果の保存を伴うため、より多くのハードウェアリソースと時間を必要とします。一方、ビットシフト演算は単に二進数のビット位置を調整するだけで済むため、非常に迅速な操作です。
CPU 命令セットの最適化:多くの現代の CPU はビットシフト操作を実行するための特別な命令を提供しており、これらの操作はハードウェアレベルで非常に効率的に最適化されています。相対的に、乗算命令も最適化されていますが、通常その複雑さから実行時の遅延が高くなります。
特定のシーン:2 の累乗の乗算、例えば 2、4、8 などはビットシフト操作で置き換えることができます。例えば、2 で乗算するには 1 ビット左にシフトし、4 で乗算するには 2 ビット左にシフトします。この置き換え方式は特定の状況で性能を大幅に向上させることができます。
コンパイラの最適化:多くのプログラミング言語のコンパイラは最適化段階で、特定の乗算演算(特に 2 の累乗での乗算)を対応するビットシフト演算に置き換えることがあります。これにより、コードの実行効率が向上します。
以上のように、高レベルでは通常これらの詳細を気にする必要はありませんが、性能に敏感なアプリケーションでは、これらの演算の効率の違いを理解することが役立ちます。
int 型は左シフトがオーバーフローを引き起こす可能性があることに注意が必要です。
論理の真と偽、C の関係演算子#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
int a = 10;
int b = 20;
//xxx ? xxx : xxx 判断trueまたはfalse
bool greater = a > b;
printf("a > b: %d\n", greater);
printf("a > b: %s\n", greater ? "true" : "false");
bool less = a < b;
printf("a < b: %d\n", less);
printf("a < b: %s\n", less ? "true" : "false");
bool not_equal = a != b;
printf("a != b: %d\n", not_equal);
printf("a != b: %s\n", not_equal ? "true" : "false");
bool equal = a == b;
printf("a == b: %d\n", equal);
printf("a == b: %s\n", equal ? "true" : "false");
bool greater_or_equal = a >= b;
printf("a >= b: %d\n", greater_or_equal);
printf("a >= b: %s\n", greater_or_equal ? "true" : "false");
bool less_or_equal = a <= b;
printf("a <= b: %d\n", less_or_equal );
printf("a <= b: %s\n", less_or_equal ? "true" : "false");
return 0;
}
条件式演算子#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
int score = 89;
printf("あなたの成績のレベル:%s\n", score >= 60 ? "合格" : "不合格");
return 0;
}
#include <stdio.h>
int main() {
int score = 85;
// 必要に応じてこのスコアを変更できます
const char* result = (score >= 90) ? "優秀" : (score >= 85) ? "一般" : (score >= 60) ? "合格" : "不合格";
printf("%s\n", result);
return 0;
}
ビット演算子 & ^ |#
演算子 | 説明 |
---|---|
& | ビット単位の「AND」演算子は、最初のオペランドの各ビットを、対応する 2 番目のオペランドのビットと比較します。両方のビットが 1 の場合、対応する結果ビットは 1 に設定されます。そうでない場合、対応する結果ビットは 0 に設定されます。 |
^ | ビット単位の「XOR」演算子は、最初のオペランドの各ビットを、対応する 2 番目のオペランドのビットと比較します。1 つのビットが 0 で、もう 1 つのビットが 1 の場合、対応する結果ビットは 1 に設定されます。そうでない場合、対応する結果ビットは 0 に設定されます。 |
**` | `** |
& ビット AND
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
int a = 12;
int b = 25;
// & 二進数のビットを比較し、両方が1の場合に1を出力します。例えば
printf("%d\n", 12 & 25);
//特定のビットをクリアする、あるビットが1かどうかを確認する
return 0;
}
| ビット OR
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
int a = 2;
int b = 5;
// | 二進数のビットを比較し、1があれば1を出力します。例えば0111
printf("%d\n", a | b);
//特定のビットを設定する、フラグを組み合わせる
return 0;
}
^ ビット XOR
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
int a = 2;
int b = 10;
// ^ 二進数のビットを比較し、1が0であれば1を出力します。例えば1000
printf("%d\n", a ^ b);
//論理XOR操作
//特定のビットを反転する、2つの変数の値を交換する、異なるかどうかを確認する
return 0;
}
ビット反転~#
マスクと回路リモコン LED ライト演習#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
void print_binary(uint8_t num);
int main() {
uint8_t status = 0b00001100; //初期状態
printf("初期状態: 0b");
print_binary(status);
printf("\t(バイナリ)\n");
status = status & 0b11111011; //maskマスク XOR制御
printf("最終状態: 0b");
printf("\t(バイナリ)\n");
return 0;
}
void print_binary(uint8_t num) {
for (int index = 7; index >= 0; index--) {
printf("%d", (num >> index) & 1);
}
}
論理演算子 && ||#
演算子 | 説明 |
---|---|
&& | 両方のオペランドが非ゼロ値を持つ場合、論理「AND」演算子は 1 を生成します。いずれかのオペランドが 0 の場合、結果は 0 になります。論理「AND」演算の最初のオペランドが 0 の場合、2 番目のオペランドは計算されません。 |
|| | 論理「OR」演算子は、そのオペランドに「AND」演算を実行します。両方のオペランドの値が 0 の場合、結果は 0 になります。いずれかのオペランドが非ゼロ値を持つ場合、結果は 1 になります。論理「OR」演算の最初のオペランドが非ゼロ値を持つ場合、2 番目のオペランドは計算されません。 |
複合代入演算子#
複合代入演算子のオペランドは整数型と浮動小数点型のみです。
#include <stdio.h>
/*
struct BigStruct {
//..
//..
};
void update(BigStruct& bs) {
BigStruct temp = someExp();
bs = bs + temp;
bs += temp;
}
*/
int main() {
int base_number = 8;
int add_number = 2;
int sub_number = 3;
int mul_number = 2;
int div_number = 5;
int mod_number = 4;
base_number += add_number;
//原地修正
base_number -=
base_number *=
base_number /=
base_number %=
base_number <<=
base_number >>=
base_number &=
base_number |=
base_number ^=
return 0;
}
コンマ演算子#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
uint32_t a = 1, b = 2, c = 4;
uint32_t result = (a += 1, b -= 1, c += 3);
printf("a = %d, b = %d, c = %d, result = %d", a, b, c, result);
return 0;
}
Microsoft Learn
// cpp_comma_operator.cpp
#include <stdio.h>
int main () {
int i = 10, b = 20, c= 30;
i = b, c;
printf("%i\n", i);
i = (b, c);
printf("%i\n", i);
}
出力:
20
30
[collapse status="false" title="Microsoft Learn の説明"]
コンマ演算子は左から右への結合性を持ちます。コンマで区切られた 2 つの式は左から右に計算されます。左オペランドは常に計算され、右オペランドが計算される前にすべての副作用が完了します。
特定のコンテキスト(関数の引数リストなど)では、コンマは区切り記号として使用されます。このコンマを区切り記号として使用する場合と、演算子として使用する場合を混同しないでください。これらの 2 つの使用法は完全に異なります。
式e1, e2
を考慮してください。この式の型と値は e2 の型と値です。e1 の計算結果は破棄されます。右オペランドが左値である場合、結果は左値になります。
通常、コンマが区切り記号として使用されるシナリオ(関数や集約初期化子の引数など)では、コンマ演算子とそのオペランドは括弧内に含める必要があります。例えば:
**C++** コピー
func_one( x, y + 2, z );
func_two( (x--, y + 2), z );
上記のfunc_one
への関数呼び出しでは、コンマで区切られた 3 つの引数が渡されます:x
、y + 2
、z
。func_two
への関数呼び出しでは、括弧がコンパイラに最初のコンマを順序計算演算子として解釈させます。この関数呼び出しでは、順序計算演算(x--, y + 2)
の結果が最初の引数として渡されます。第二の引数はz
です。
[/collapse]
計算の優先順位と順序#
シンボル ^1^ | 操作タイプ | 結合性 |
---|---|---|
[ ] ( ) . -> ++ -- (後置) | 式 | 左から右 |
sizeof & * + - ~ ! ++ -- (前置) | 単項 | 右から左 |
typecasts | 単項 | 右から左 |
* / % | 乗算 | 左から右 |
+ - | 加算 | 左から右 |
<< >> | ビット移動 | 左から右 |
< > <= >= | 関係 | 左から右 |
== != | 等しい | 左から右 |
& | ビット「AND」 | 左から右 |
^ | ビット「XOR」 | 左から右 |
| | ビット「OR」 | 左から右 |
&& | 論理「AND」 | 左から右 |
|| | 論理「OR」 | 左から右 |
? : | 条件式 | 右から左 |
= *= /= %= += -= <<= >>= &= ^= |= | 単純および複合代入 ^2^ | 右から左 |
, | 順序計算 | 左から右 |
^1^ 演算子は優先順位の降順でリストされています。同じ行またはグループに複数の演算子が表示されている場合、それらは同じ優先順位を持ちます。
^2^ すべての単純および複合代入演算子は同じ優先順位を持ちます。
式には同じ優先順位の複数の演算子を含めることができます。これらの演算子が同じレベルで複数回出現する場合、計算はその演算子の結合性に従って右から左または左から右の順序で実行されます。計算の方向は、同じレベルで複数の乗算(*
)、加算(+
)またはビット単位(&
、|
または^
)演算子を含む式の結果に影響を与えません。言語は演算の順序を定義しません。コンパイラが一貫した結果を保証できる場合、コンパイラは任意の順序でこのような式を計算できます。
順序計算(,
)、論理「AND」(&&
)、論理「OR」(||
)、条件式演算子 (? :
) および関数呼び出し演算子は、したがって、オペランドの計算に特定の順序を保証します。関数呼び出し演算子は、関数識別子の後に続く括弧のセットです。順序計算演算子(,
)は、そのオペランドを左から右の順序で計算することを保証します。(関数呼び出しのコンマ演算子は順序計算演算子とは異なり、そのような保証を提供しません。)詳細については、シーケンスポイントを参照してください。
論理演算子は、オペランドを左から右の順序で計算することを保証します。ただし、結果を決定するために必要な最小数のオペランドのみを計算します。これを「ショートサーキット」計算と呼びます。したがって、式の一部のオペランドを計算できない場合があります。例えば、以下の式では
x && y++
y++
が true(非ゼロ)である場合にのみ、2 番目のオペランド(x
)が計算されます。したがって、y
が false(0)の場合、x
は増加しません。
例
以下のリストは、コンパイラが複数の例の式を自動的にバインドする方法を示しています:
展開表
式 | 自動バインド |
---|---|
a & b || c | (a & b) || c |
a = b || c | a = (b || c) |
q && r || s-- | (q && r) || s-- |
最初の式では、ビット単位の「AND」演算子(&
)の優先順位が論理「OR」演算子(||
)の優先順位よりも高いため、a & b
は論理「OR」演算の最初のオペランドを構成します。
2 番目の式では、論理「OR」演算子(||
)の優先順位が単純代入演算子(=
)の優先順位よりも高いため、b || c
は代入の右オペランドとしてグループ化されます。注意すべきは、a
に代入される値は 0 または 1 です。
3 番目の式は、予期しない結果を生成する可能性のある正しい形式の式を示しています。論理「AND」演算子(&&
)の優先順位は論理「OR」演算子(||
)の優先順位よりも高いため、q && r
はオペランドとしてグループ化されます。論理演算子は、左から右の順序でオペランドを計算することを保証するため、q && r
はs--
よりも先に計算されます。ただし、q && r
の計算結果が非ゼロ値である場合、s--
は計算されず、s
は減少しません。s
が減少しないことが問題を引き起こす場合、s--
は式の最初のオペランドとして表示されるか、別の演算で減少させる必要があります。
以下の式は不正であり、コンパイル時に診断メッセージが生成されます:
不正な式 | デフォルトのグループ |
---|---|
p == 0 ? p += 1: p += 2 | ( p == 0 ? p += 1 : p ) += 2 |
この式では、等号演算子(==
)の優先順位が最も高いため、p == 0
はオペランドとしてグループ化されます。条件式演算子(? :
)は次に高い優先順位を持ちます。その最初のオペランドはp == 0
であり、2 番目のオペランドはp += 1
です。しかし、条件式演算子の最後のオペランドはp
と見なされ、p += 2
ではありません。複合代入演算子に比べてp
のマッチが条件式演算子により密接にバインドされるため、構文エラーが発生します。このようなエラーを防ぎ、可読性の高いコードを生成するために、括弧を使用する必要があります。例えば、次のように括弧を使用して前の例を修正し、明確にすることができます:
( p == 0 ) ? ( p += 1 ) : ( p += 2 )
参照
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
int32_t result;
result = a * b + c << d > e ? b : c * sizeof(++e) / sizeof(int32_t);
printf("結果: %" PRId32 ", result");
return 0;
}
単項演算子の中で、& - + などの前置 * は解参照演算子です。
&ビットAND
と前置&
は異なり、後者の優先順位が高いです!
第 3 章 分岐と制御#
決定制御#
天気が晴れたら —— 遊びに行く;
論理 && || ;
人は決定能力を持っており、論理的な判断がプログラムに選択させます!
前提は:プログラムに「天気の状態」が true か false かを判断させることです。
例えば、天気アプリケーション:
温度 <30° 温度> 30°
プログラミング言語:
- 実際の環境に適用する必要があります。
- 柔軟性、ランダム性
- 完全ではない、完璧があればそれは嘘です。
if 文と if else#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
uint32_t number = 100;
//if文
if (number > 10) {
printf("この数は10より大きいです!\n");
printf("この数は10より大きいです!\n");
}
if (number >= 100) {
printf("この数は100以上です!\n");
}
else {
printf("この数は100未満です!\n");
}
return 0;
}
論理 AND と OR のショートサーキット動作#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
bool is_weather_sunny = false;
bool is_venue_available = true;
if (is_weather_sunny && is_venue_available) {
printf("イベントは予定通り開催されます!\n");
}
else {
printf("イベントは予定通り開催されません!\n");
if (!is_weather_sunny) {
printf("理由:天気が晴れていません!\n");
}
if (!is_venue_available) {
printf("理由:会場がありません!\n");
}
}
return 0;
}
//自動販売機
//コインのみをサポート
#include <stdio.h>
#include <stdbool.h>
#include <inttypes.h>
int main(void)
{
const uint32_t PRICE = 3; //飲み物の価格
uint32_t balance = 0; //現在の合計コイン
uint32_t coin; //それぞれのコイン
puts("飲み物の価格は$5です!");
while (balance < PRICE) //必要な金額が投じられるまで
{
puts("投げないで!"); //取引を拒否
scanf_s("%" PRIu32, &coin);
if (coin == 1 || coin == 2 || coin == 5) //金額の種類を確認
{
balance += coin; //代入 加算
printf("あなたはすでに $%" PRIu32 "を投入しました\n", balance);
}
else {
printf("申し訳ありませんが、$%" PRIu32 "のコインは受け付けていません\n", coin);
}
}
if (balance > PRICE) {
printf("お釣り:%" PRIu32 "\n", balance - PRICE);
}
return 0;
}
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <inttypes.h>
int main(void)
{
//プレイヤーが異なる条件の部屋に入る
uint32_t coins = 15;
bool is_vip = true;
bool have_special_tool = false;
if (is_vip) {
puts("VIP入場!\n");
}
else {
puts("非VIP退出!\n");
}
if (coins >= 10 || have_special_tool) {
puts("入場!\n");
}
else {
puts("退出!\n");
}
return 0;
}
#include <stdio.h>
#include <stdbool.h>
#include <inttypes.h>
int main(void)
{
const uint32_t all_laps = 100;
uint32_t current_laps = 0;
puts("開始!");
while (current_laps < all_laps)
{
current_laps++;
printf("第 %" PRIu32 "周を完了しました。\n", current_laps);
}
return 0;
}
//自動販売機
//コインのみをサポート
#include <stdio.h>
#include <stdbool.h>
#include <inttypes.h>
int main(void)
{
const uint32_t price = 10; //飲み物の価格
uint32_t balance = 0; //現在の合計コイン
uint32_t coin; //それぞれのコイン
puts("飲み物の価格は$5です!");
while (balance < price) //必要な金額が投じられるまで だけでなく、十分な金額が投じられたときにループを開始します
{
puts("投げないで!"); //取引を拒否
//コインを投げるシミュレーション
scanf_s("%" PRIu32, &coin);
if (coin == 1 || coin == 2 || coin == 5) //金額の種類を確認
{
balance += coin; //代入 加算 カウント
printf("あなたはすでに $%" PRIu32 "を投入しました\n", balance);
}
else {
printf("申し訳ありませんが、$%" PRIu32 "のコインは受け付けていません\n", coin);
}
}
if (balance > price) {
printf("お釣り:%" PRIu32 "\n", balance - price);
}
return 0;
}
//必ずprintfを書くことで、効果を確認できます。
//ループの問題に直面した場合
//ループ内で何度も使用しないようにして、不要なエラーを避けてください。
//計算機の合計を求める
#include <stdio.h>
#include <inttypes.h>
int main(void)
{
uint32_t sum = 0; //初期の合計を0に設定
uint32_t number = 1; //最初のnumberの値
while (number != 0) //0をループを終了する方法として設定
{
scanf_s("%" PRIu32, &number); //scanfでnumberを再代入し、最初のnumberの値は実際には使用されません。
sum += number;
}
printf("求められた合計sumは %" PRIu32 "\n", sum);
return 0;
}
#include <stdio.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
int main()
{
uint32_t sum = 0; //初期の合計を0に設定
char input[50]; //ユーザーの入力を文字から数字に変換
char* end;
puts("一連の数字を入力してください。改行で区切り、qで終了します。");
while (true) //0をループを終了する方法として設定
{
errno = 0;
puts("数字を入力してください:");
scanf_s(" %49s", &input, 50);
if (input[0] == 'q' && input[1] == '\0') {
break;
}
long number = strtol(input, &end, 10);
//入力された文字を数字に変換し、合計sumに加えます。
//0のASCIIは48
if (end == input || *end != '\0') {
printf("無効な入力です。数字または文字qを入力してください。\n");
}
else if (errno == ERANGE || number < 0 || number > UINT32_MAX){
printf("数字が範囲を超えています!%u以下の正の整数を入力してください。\n", UINT32_MAX);
}
else {
sum += (uint32_t)number;
}
}
printf("求められた合計sumは %" PRIu32 "\n", sum);
return 0;
}
#include <stdio.h>
#include <inttypes.h>
#include <stdbool.h>
int main()
{
uint32_t sum = 0; //初期の合計を0に設定
uint32_t number; //最初のnumberの値
puts("一連の数字を入力してください。改行で区切り、0で終了します。");
while (true) //0をループを終了する方法として設定
{
scanf_s(" %" PRIu32, &number);
if (number == 0) {
break;
}
sum += number;
}
printf("求められた合計sumは %" PRIu32 "\n", sum);
return 0;
}
//衛語句の使用:レンタカーの例
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <inttypes.h>
void check_car_rent(uint8_t age, uint8_t driving_exp_years);
int main(void)
{
check_car_rent(22, 3);
return 0;
}
void check_car_rent(uint8_t age, uint8_t driving_exp_years) {
//衛語句
if (age < 21) {
puts("条件を満たしていません。年齢不足です!");
return 0;
}
if (driving_exp_years < 1) {
puts("条件を満たしていません。運転歴不足です!");
return 0;
}
}
//論理式の簡略化
#include <stdbool.h>
#include <inttypes.h>
int main(void)
{
//論理式は簡略化すべきです!
bool is_weekend = true;
bool has_enter = true;
if (!has_enter) {
return 0;
}
return 0;
}
状態機械:複雑な状態の遷移を管理#
#include <stdbool.h>
#include <inttypes.h>
int main(void)
{
//状態機械:複雑な状態の遷移を管理し、switch caseを使用
uint8_t traffic_state = 0;
switch (traffic_state)
{
case 0:
puts("赤");
traffic_state = 1;
break;
case 1:
puts("黄");
traffic_state = 2;
break;
case 2:
puts("緑");
traffic_state = 0;
break;
default:
puts("???");
break;
}
return 0;
}
switch-case と if-else の違い#
//switch-caseとif-elseの違い
#include <stdio.h>
#include <stdbool.h>
#include <inttypes.h>
int main(void)
{
return 0;
}
ループの生活における役割#
//ループの生活における役割
#include <stdio.h>
#include <stdbool.h>
#include <inttypes.h>
int main(void)
{
int32_t number;
scanf_s("入力してください: %d %d %d", &number, &number, &number);//実際の開発ではscanfを使用しません
//それに\nを書く必要はありません
printf("あなたが入力した数字は: %d %d %dです\n", number, number, number);
return 0;
}
do-while と while の違い#
//do-whileとwhileの違い
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main(void)
{
uint32_t total_laps = 10;
uint32_t current_laps = 0;
puts("罰走開始!");
do {
current_laps++;
printf("ランナーは第%" PRIu32 "周を完了しました\n", current_laps);
} while (current_laps < total_laps);
return 0;
}
do-while の実際の役割#
//do-whileの実際の役割
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main(void)
{
uint32_t choice;
do
{
puts("****メインメニュー****");
puts("1. 新しいゲーム");
puts("2. ゲームをロード");
puts("3. 終了");
scanf_s("%" PRIu32, &choice);
switch (choice)
{
case 1:
puts("****新しいゲームを作成!");
break;
case 2:
puts("****保存をロード!");
break;
case 3:
puts("****ゲームを終了!");
break;
}
} while (choice != 3);
return 0;
}
ランダム数当てゲームの例#
//ランダム数当てゲームの例
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <time.h>
#include <stdlib.h>
int main(void)
{
uint32_t secret_num, guess, status;
char buffer[50];
srand(time(NULL));
//1-100のランダム数を生成
secret_num = rand() % 100 + 1;
puts("当ててみて!");
do {
puts("あなたが推測した数を入力してください:");
fgets(buffer, sizeof(buffer), stdin);
status = sscanf_s(buffer, "%d", &guess);
//衛語句
if (status != 1) {
puts("無効な入力です!");
continue;
}
if (guess < secret_num) {
puts("小さすぎます!");
}
else if (guess > secret_num) {
puts("大きすぎます!");
}
} while (guess != secret_num);
printf("おめでとうございます、正解です!");
return 0;
}
continue 文#
//continue文
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main(void) {
uint32_t sum = 0;
int32_t number;
puts("一連の正の整数を入力してください。負の数を入力し、0で終了します。");
while (1) {
puts("数字を入力してください");
scanf_s("%d", &number);
if (number == 0) {
break;
}
if (number < 0) {
continue; //continueはこのループの残りの部分をスキップします
}
sum += number;//カウント
}
printf("計算結果は:%" PRIu32 "\n", sum);
return 0;
}
continue と break の連携条件判断#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main() {
uint32_t number;
puts("数字を入力してください(0-100)、-1を入力するとプログラムが終了します");
while (1) {
puts("数字を入力してください:");
scanf_s("%d", &number);
//衛語句!条件をチェックして終了
if (number == -1) {
break;
}
if (number < 0 || number > 100) {
continue;//このループの残りの部分をスキップします。
}
sum += number;
}
return 0;
}
初探 for ループ#
//初探forループ
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main(void)
{
//forループ
const uint32_t total_laps = 10; //目標周数
//uint32_t current_lap = 0; 現在の周数を初期化
puts("ランナーが走り始めます");
for (uint32_t current_lap = 0; current_lap <= total_laps; current_lap++) {
printf("ランナーは第 %" PRIu32 "周を完了しました\n", current_lap);
}
return 0;
}
整数平方和#
//整数平方和
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main(void)
{
/*
目的は、プレイヤーが入力したn(nはプレイヤーが入力する)までのすべての整数の平方和を計算することです。
*/
uint32_t number;
uint32_t sum_of_squares = 0;
puts("整数nを入力してください");
scanf_s("%u", &number);
for (uint32_t index = 1; index <= number; index++) {
sum_of_squares += index * index;
}
printf("%" PRIu32 "\n", sum_of_squares);
return 0;
}
カウントダウン#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main(void) {
/*
星光大道
カウントダウン
*/
uint32_t start_number;
puts("正の整数を入力してください");
scanf_s("%" SCNu32, &start_number);
puts("カウントダウンが始まります");
for (uint32_t index_number = start_number; index_number > 0; index_number--)
{
printf("%" SCNu32 "\n", index_number);
}
puts("カウントダウンが停止します");
return 0;
}
//拡張sleep
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <windows.h>
int main(void)
{
/*
星光大道
カウントダウン
*/
uint32_t start_number;
puts("正の整数を入力してください");
scanf_s("%" SCNu32, &start_number);
puts("カウントダウンが始まります");
for (uint32_t index_number = start_number; index_number > 0; index_number--)
{
printf("%" SCNu32 "\n", index_number);
Sleep(1000);
}
puts("カウントダウンが停止します");
return 0;
}
階乗#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <windows.h>
//階乗
int main(void) {
uint32_t number;
uint32_t factorial = 1;
puts("正の整数を入力してください:");
scanf_s("%" SCNu32, &number);
for (uint32_t index = 1; index <= number; index++) {
factorial *= index;
}
printf("%" PRIu32 "! = %" PRIu32 "\n", number, factorial);
return 0;
}
平方根#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <math.h>
#include <stdbool.h>
int main(void)
{
double number = 4.00;
printf("%lf\n", sqrt(121));
return 0;
}
素数#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <math.h>
#include <stdbool.h>
int main(void)
{
uint32_t num;
bool is_prime = true;
puts("正の整数を入力してください。1以外の数で、素数かどうかを確認します:");
scanf_s("%" SCNu32, &num);
if (num <= 1) {
is_prime = false;
}
else {
//for , 1とそれ自身以外の因数をチェック
for (uint32_t i = 2; i * i <= num; i++) {
if (num % i == 0) {
is_prime = false;
break;
}
}
}
return 0;
}
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <math.h>
#include <stdbool.h>
int main(void)
{
uint32_t size;
puts("パターンのサイズを入力してください: ");
scanf_s("%" SCNu32, &size);
//特徴:長さと幅が同じ
puts("正方形のパターンを印刷します");
for (uint32_t i = 0; i < size; i++) {
for (uint32_t j = 0; j < size; j++)
{
printf("* ");
}
printf("\n");
}
return 0;
}
この文は [Mix