HansJack

HansJack

C語言筆記一文

C 語言筆記#

感謝 Frank 為廣大學生打開了進入現代化 C 語言的大門

C.png 

目錄

[TOC]


6 月份寫的博文太水了,從這個筆記開始,博客文章內容均會提高到一個高度!

內容來自講師 Micro_Frank 的免費課程,筆記具體來自所講內容,如有侵權,聯繫博主!

第一章#

第一個程序:C 語言執行過程#

#include <stdio.h>
int main()
{
	printf("Hello World!\n");

	return 0;
}

本程序是 C 語言的基礎示例,演示了如何輸出一行文本。#include <stdio.h> 語句包含了標準輸入輸出庫,int main() 是程序的入口點,printf("Hello World!\n"); 用於打印字符串,return 0; 表示程序成功結束。

C 語言故事・1#

選用教材:C Primer Plus#

建議英文中文對照閱讀

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 的整型變量,並且初始化它的值為 123。在 C 語言中,變量的聲明和定義通常在一條語句中進行。 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 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 專用

以兩條下劃線開頭或者以一條下劃線後跟一個大寫字母開頭的名稱。

Microsoft 使用下劃線和大寫字母作為宏名稱的開頭,並使用雙下劃線作為 Microsoft 特定關鍵字名稱的開頭。

有效標識符

j
count
temp1
top_of_page
skip12
LastNum

關鍵字#

image-20240810175108706

image-20240810175028158

第二章 數據類型#

注釋#

//行
    注釋

/*



*/

進制#

變量#

//int 變量
#include <stdio.h>

int main(){
	//integer 賦值
	int sun_flower = 9673;

	printf("陽光目前的數值:%d\n", sun_flower);

	return 0;
}

整型#

語法:定義的類型 標識符 = 值

int 類型在內存中存儲#

計算機的存儲單位:

1G = 1024MB

1MB = 1024KiB

1KiB = 1024Btyes

1024Btye = 8Bit

00000000000000000000000000000000int

0用於顯示正負,一共可以表示 $ 2^{32} - 1 $ 個值

#include <stdio.h>
int main()
{
	int number = 100;
	//以十進制表示
	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 標準,這是一種定義浮點數表示方法的標準。浮點數由三個主要部分組成:符號位、指數部分和尾數(或稱為有效數字)。以下是浮點數的一般存儲格式: 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.4+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 科學計數法格式化輸出
ptintf("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;
	//OverFlow 上溢

	float min_float = FLT_MIN;

	float underfloat = min_float / 1000.0f;
	//UnderFlow 下溢

	printf("Maximum Float: %e\n", max_float);
	printf("OverFloat: %e\n", overflow);
	printf("Minimum Float: %e\n", min_float);
	printf("UnderFloat: %e\n", underfloat);

	return 0;
}

Nan & Infinity#

#include <stdio.h>
#include <float.h>
#include <math.h>

int main() {

	//正無窮大
	float positive_infinty = INIFITY;
	printf("Positive Infinity: %f\n", positive_infinty);

	//負無窮大
	float negative_infinty = -INIFITY;
	printf("Negative Infinity: %f\n", negative_infinty);

	//除以0產生的無窮大
	float num = 1.0f;
	float inifity = num / 0.0f;
	printf("0.0 / 0.0 = %f\n", nan);

	//Nano 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", nefative_sqrt);

	return 0;
}

最近偶數舍入標準(銀行家舍入)#

#include <stdio.h>

int main() {

	//四舍五入
	//IEEE 754
	//最近偶數舍入  round to nearest, ties to even
	//銀行家舍入

	//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

}

在計算機科學和編程中,doublelong double 是兩種浮點數數據類型,用於表示帶小數的數字。它們在科研和企業中的使用有時會有不同的側重點,主要體現在以下幾個方面:

  1. 精度需求
  • 科研:科學研究往往涉及高度精確的計算,如物理、化學模擬等。這種情況下,long double 可能會被優先使用,以提高計算的精度,避免浮點數運算中的誤差。
  • 企業:在商業應用中,通常對浮點數的精度要求沒有那麼嚴格,double 已經可以滿足大部分的需求,特別是在財務計算和統計分析等場景中。
  1. 性能考慮
  • 科研:科研計算可能更加關注計算的準確性,而不是速度,但在一些高性能計算中,仍需權衡精度算法與計算性能之間的關係。
  • 企業:企業應用常常在追求性能、響應時間和資源利用效率,因此可能偏向於使用 double,以便在保證一定精度的基礎上提高計算速度。
  1. 平台依賴性
  • 科研:在科研領域,研究者會考慮到不同硬件平台之間的差異,選擇浮點數類型時會考慮跨平台的兼容性。 - 企業:企業可能更關注軟件的可維護性和開發效率,通常選擇更為常用和標準化的 double 類型,以獲得更好的開發支持和社區資源。
  1. 語言和工具支持
  • 科研:科研工作者可能會使用專門的科學計算庫(如 NumPy、SciPy 等),這些庫在設計時考慮了多種浮點數類型的支持。
  • 企業:在企業環境中,開發團隊可能更傾向於使用通用數據類型,依賴於標準庫和存儲流程,所以 double 類型更為普遍。 綜上所述,雖然 doublelong 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 位十進制數字。

double(雙精度浮點數): 通常占用 8 字節(64 位)。 1 位為符號位,11 位為指數位,52 位為尾數。通常有效精度約為 15 到 16 位十進制數字。

浮點數的表示遵循 IEEE 754 標準。它的值可以通過以下公式表示: [\text = (-1)^} \times (1 + \text) \times 2^ - \text)} ]

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响铃(警报)
\bBackspace
\f换页
\n换行
\r回车
\t水平制表符
\v垂直制表符
\'单引号
\"双引号
\\反斜杠
\?文本问号
\ ooo八进制表示法的 ASCII 字符
\x hh十六进制表示法的 ASCII 字符
\x hhhh十六进制表示法的 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 or false
	//轉換成 1 or 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;

}

第二章結束語#

第三章 運算符#

運算符的介紹#

1、算術運算符

2、關係運算符

運算符測試的關係
<第一个操作数小于第二个操作数
>第一个操作数大于第二个操作数
<=第一个操作数小于或等于第二个操作数
>=第一个操作数大于或等于第二个操作数
==第一个操作数等于第二个操作数
!=第一个操作数不等于第二个操作数

數據對象 左值和右值#

#include <stdio.h>
#include <stdint.h>
#include <inttype.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_box);
 
	return 0;

}

多重賦值#

前綴後綴遞增與遞減#

#include <stdio.h>
#include <stdint.h>
#include <inttype.h>
int main() {

	int32_t value = 5;
	int32_t result_1;
	int32_t result_2;

	//後綴遞增,先賦值,再++
	result_1 = value++;
	//前綴遞減,先--,後賦值
	result_2 = --value;
	printf("After postfix increment, 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;//高位補零,低位排擠

	return 0;
}
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>

int main() {

	uint8_t num = 22;
	//num >> 2; 高位補零,低位排擠

	uint8_t num = 22;
	printf("Original number: %" PRIu8 " (binary: 00010110)\n", num);

	uint8_t left_shifted = num << 2;
	printf("Left shifted by 2: %" PRIu8 " (binary: 01011000)\n", num);

	uint8_t right_shifted = num >> 2;
	printf("Right shifted by 2: %" PRIu8 " (binary: 00000101)\n", num);

	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("Result: %" 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 or 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 
int main() { 
	int score = 85; 
	// 你可以根據需要修改這個分數 
	const char* result = (score >= 90) ? "優秀" : (score >= 85) ? "一般" : (score >= 60) ? "合格" : "不及格"; printf("%s\n", result); 

	return 0; 
}

按位運算符 & ^ |#

運算符描述
&按位 “與” 運算符將其第一操作數的每一位與其第二操作數的相應位進行比較。 如果兩個位均為 1,則對應的結果位將設置為 1。 否則,將對應的結果位設置為 0。
^按位 “異或” 運算符將其第一操作數的每一位與其第二操作數的相應位進行比較。 如果一個位是 0,另一個位是 1,則相應的結果位將設置為 1。 否則,將對應的結果位設置為 0。
**``**

& 按位與

#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;
}

| 按位與或

#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;
}

^ 按位異或

#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操作
	//翻轉特定位、交換兩個變量值、檢查不同

	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("Initial status: 0b");
	print_binary(status);
	printf("\t(Binary)\n");

	status = status & 0b11111011;	//mask掩碼 異或 控制
	printf(("Fanal status: 0b");
	printf("\t(Binary)\n");
	return 0;
}

void print_binary(uint8_t num);
	for (int index = 7; index >= 0; index--) {
	    printf("%d", (num >> index) & 1);
	}
}

邏輯運算符 && ||#

運算符描述
&&如果兩個操作數具有非零值,則邏輯 “與” 運算符產生值 1。 如果其中一個操作數等於 0,則結果為 0。 如果邏輯 “與” 運算的第一操作數等於 0,則不會計算第二個操作數。
||邏輯 “或” 運算符對其操作數執行 “與或” 運算。 如果兩個操作數的值均為 0,則結果為 0。 如果其中一個操作數具有非零值,則結果為 1。 如果邏輯 “或” 運算的第一操作數具有非零值,則不會計算第二個操作數。

複合賦值運算符#

複合賦值運算符的操作數只能是整型和浮點型

#include <stdio.h>
/*
struct BigStruct {
	//..
	//..
};

void uodate(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;
	//In-place Modification 原地修改

	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);
}

Output:

20
30

[collapse status="false" title="Microsoft Learn 解釋"]

逗號運算符具有從左向右的關聯性。 由逗號分隔的兩個表達式將從左向右進行計算。 始終計算左操作數,並且在計算右操作數之前將完成所有副作用。

在某些上下文(如函數自變量列表)中,逗號可用作分隔符。 不要將該逗號用作分隔符與將其用作運算符的情況混淆;這兩種用法完全不同。

請考慮表達式 e1, e2。 該表達式的類型和值是 e2 的類型和值;e1 的計算結果將被丟棄。 如果右操作數是左值,則結果為左值。

在通常將逗號用作分隔符的方案中(例如,在函數或聚合初始值設置項的自變量中),逗號運算符及其操作數必須包含在括號中。 例如:

**C++** 複製

func_one( x, y + 2, z );
func_two( (x--, y + 2), z );

在上面的對 func_one 的函數調用中,會傳遞以逗號分隔的三個參數:xy + 2z。 在對 func_two 的函數調用中,圓括號強制編譯器將第一個逗號解釋為順序計算運算符。 此函數調用將兩個參數傳遞給 func_two。 第一個參數是順序計算運算 (x--, y + 2) 的結果,具有表達式 y + 2 的值和類型;第二個參數為 z

[/collapse]

計算的優先級和順序#

符號 ^1^操作類型结合性
[ ] ( ) . ->
++ --(後綴)
表達式從左到右
sizeof & * + - ~ !
++ --(前綴)
一元從右到左
typecasts一元從右到左
* / %乘法從左到右
+ -加法從左到右
<< >>按位移動從左到右
< > <= >=關係從左到右
== !=相等從左到右
&按位 “與”從左到右
^按位 “異或”從左到右
|按位 “與或”從左到右
&&邏輯 “與”從左到右
||邏輯 “或”從左到右
? :條件表達式從右到左
= *= /= %=
+= -= <<= >>= &=
^= |=
簡單和複合賦值 ^2^從右到左
,順序計算從左到右

^1^ 運算符按優先級的降序順序列出。 如果多個運算符出現在同一行或一個組中,則它們具有相同的優先級。

^2^ 所有簡單的和複合的賦值運算符都有相同的優先級。

表達式可以包含優先級相同的多個運算符。 當多個具有相同級別的這類運算符出現在表達式中時,計算將根據該運算符的結合性按從右到左或從左至右的順序來執行。 計算的方向不影響在相同級別包括多個乘法 (*)、加法 (+) 或二進制按位(&|^)運算符的表達式的結果。 語言未定義運算的順序。 如果編譯器可以保證一致的結果,則編譯器可以按任意順序隨意計算此類表達式。

只有順序計算 (,)、邏輯 “與”(&&)、邏輯 “或” (||)、條件表達式 (? :) 和函數調用運算符構成序列點,因此,確保對其操作數的計算采用特定順序。 函數調用運算符是一組緊跟函數標識符的圓括號。 確保順序計算運算符 (,) 按從左到右的順序計算其操作數。 (函數調用中的逗號運算符與順序計算運算符不同,不提供任何此類保證。)有關詳細信息,請參閱序列點

邏輯運算符還確保按從左至右的順序計算操作數。 但是,它們會計算確定表達式結果所需的最小數目的操作數。 這稱作 “短路” 計算。 因此,無法計算表達式的一些操作數。 例如,在下面的表達式中

x && y++

僅當 y++ 為 true(非零)時,才計算第二操作數 (x)。 因此,如果 y 為 false (0),則 x 不增加。

示例

以下列表顯示編譯器如何自動綁定多個示例表達式:

展開表

表達式自動綁定
a & b || c(a & b) || c
a = b || ca = (b || c)
q && r || s--(q && r) || s--

在第一個表達式中,按位 “與” 運算符 (&) 的優先級高於邏輯 “或” 運算符 (||) 的優先級,因此,a & b 構成了邏輯 “或” 運算的第一操作數。

在第二個表達式中,邏輯 “或” 運算符 (||) 的優先級高於簡單賦值運算符 (=) 的優先級,因此,b || c 在賦值中分組為右操作數。 請注意,賦給 a 的值為 0 或 1。

第三個表達式顯示可能會生成意外結果的格式正確的表達式。 邏輯 “與” 運算符 (&&) 的優先級高於邏輯 “或” 運算符 (||) 的優先級,因此,將 q && r 分組為操作數。 由於邏輯運算符確保按從左到右的順序計算操作數,因此 q && r 先於 s-- 被計算。 但是,如果邏輯運算的結果為非零值,則不計算 s--,並且 s 不會減少。 如果 s 未減少會導致程序出現問題,則 s-- 應顯示為表達式的第一操作數,或者在單獨的運算中應減少 s

以下表達式是非法的並會在編譯時生成診斷消息:

非法表達式默認分組
p == 0 ? p += 1: p += 2( p == 0 ? p += 1 : p ) += 2

在此表達式中,相等運算符 (==) 的優先級最高,因此,將 p == 0 分組為操作數。 條件表達式運算符 (? :) 具有下個最高級別的優先級。 其第一操作數是 p == 0,第二操作數是 p += 1。 但是,條件表達式運算符的最後一個操作數被視為 p 而不是 p += 2,因為與複合賦值運算符相比,p 的匹配項將更緊密地綁定到條件表達式運算符。 由於 += 2 沒有左操作數,因此發生語法錯誤。 您應使用括號以防止此類錯誤發生並生成可讀性更高的代碼。 例如,可以按如下所示使用括號來更正和闡明前面的示例:

( p == 0 ) ? ( p += 1 ) : ( p += 2 )

請參閱

C 運算符

#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(Result: %" PRId32 ", result);

	return 0;
}

一元操作符號中,& - + 等前綴 * 為解引用運算符

&按位與前綴&不一樣,後者優先級高!

第三章 分支與控制#

決策控制#

如果天氣晴朗 —— 選出去玩;

邏輯 && || ;

人是有決策能力的,邏輯的判斷讓程序去・選擇!

前提是:讓程序去判斷 ” 天氣狀態 “ 是否為 true , false


比如 一個天氣應用程序:

溫度 <30° 溫度> 30°


編程語言:

  • 要應用到實際環境
  • 靈活性,隨機
  • 不可能全面,若有完美 定是謊言

if 語句和 if else#

#include <stdio.h>
#include <stdint.h>
#include <inttype.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;
}

邏輯與或的短路行為#

#include <stdio.h>
#include <stdint.h>
#include <inttype.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_vueue_available) {
		printf("原因:沒有場地!\n")
		}

	return 0;
}
//自動販賣機
//只支持硬幣
#include <stdio.h>
#include <stdbool.h>
#include <inttypes.h>

int main(void)

{

	const uint32_t PRICE = 3;	//drink's price

	uint32_t balance = 0;		//now total_coin

	uint32_t coin;				//every coin in it

	puts("drink's price is $5 please!");

	while (balance < PRICE)	//在投夠數額前
	{
		puts("don't!");	//拒絕交易

		scanf_s("%" PRIu32, &coin);


		if (coin == 1 || coin == 2 || coin == 5) //確定金額類型
		{
		balance += coin;	//賦值 加等

		printf("你已經投入 $%" PRIu32 "\n", balance);
		}
		else {

		printf("sorry!我們不接受 $%" 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 Enter!\n");
	}
	else {
		puts("not_vip Exit!\n");
	}

	if (coins >= 10 || have_special_tool) {
		puts("Enter!\n")
	}
	else {
		puts("Exit!\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;	//drink's price

	uint32_t balance = 0;		//now total_coin

	uint32_t coin;				//every coin in it

	puts("drink's price is $5 please!");

	while (balance < price)	//在投夠數額前 只有在夠的時候才開始循環
	{
		puts("don't!");	//拒絕交易

		//模擬投幣
		scanf_s("%" priu32, &coin);


		if (coin == 1 || coin == 2 || coin == 5) //確定金額類型
		{ 
		balance += coin;	//賦值 加等 計數

		printf("你已經投入 $%" priu32 "\n", balance);
		}
		else {

		printf("sorry!我們不接受 $%" 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("red");
		traffic_state = 1;
		break;
	case 1:
		puts("yellow");
		traffic_state = 2;
		break;
	case 2:
		puts("green");
		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 mian(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
	//not write \n in it
	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)
{
	 /*
		目的為了計算從1到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 = 0;
				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 Space 同步更新至 xLog
原始鏈接為 https://hansblog.top/posts/study-book/c-study


載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。