C中的靜態變數

如何在C中使用靜態變數 在函數內部,您可以使用static關鍵字來初始化靜態變數。 “在函數內部”關鍵字是因為全局變數默認為靜態變數,所以不需要添加關鍵字。 那什麼是靜態變數呢?靜態變數如果沒有指定初始值,則初始化為0,並且在函數調用之間保留其值。 考慮以下函數: int incrementAge() { int age = 0; age++; return age; } 如果我們只調用incrementAge()一次,則返回值將為1。如果我們多次調用它,我們將始終得到1,因為age是一個局部變量,並且在每次函數調用時重新初始化為0。 如果我們將函數更改為: int incrementAge() { static int age = 0; age++; return age; } 現在每次調用此函數時,我們都會得到一個遞增的值: printf("%d\n", incrementAge()); printf("%d\n", incrementAge()); printf("%d\n", incrementAge()); 將會得到: 1 2 3 我們還可以省略在static int age = 0;中將age初始化為0,只需寫成static int age,因為靜態變數在創建時自動設置為0。 我們還可以擁有靜態數組。在這種情況下,數組中的每個單個項都初始化為0: int incrementAge() { static int ages[3]; ages[0]++; return ages[0]; }

C函數介紹

C函數介紹 函數是將代碼結構化為子程序的方法,我們可以: 為其命名 在需要的時候調用它們 從你的第一個程序開始,一個“Hello, World!”,你就立即使用了C函數: #include <stdio.h> int main(void) { printf("Hello, World!"); } main()函數是非常重要的函數,因為它是C程序的入口點。 下面是另一個函數: void doSomething(int value) { printf("%u", value); } 函數有4個重要方面: 它們有一個名字,所以我們可以在以後調用它們 它們指定了一個返回值 它們可以有參數 它們有一個用大括號包裹的函數體 函數體是在調用函數時執行的一組指令。 如果函數沒有返回值,你可以在函數名前使用關鍵字void。否則,你需要指定函數的返回值類型(int表示整數,float表示浮點數,const char *表示字符串等)。 你不能從一個函數中返回多個值。 函數可以有參數,它們是可選的。如果沒有參數,我們在括號內插入void,像這樣: void doSomething(void) { /* ... */ } 在這種情況下,當我們調用該函數時,括號內不帶任何內容: doSomething(); 如果我們有一個參數,我們需要指定參數的類型和名稱,像這樣: void doSomething(int value) { /* ... */ } 當我們調用該函數時,我們將在括號內傳遞該參數,像這樣: doSomething(3); 我們可以有多個參數,如果有多個參數,我們在聲明和調用時使用逗號分隔: void doSomething(int value1, int value2) { /* ... */ } doSomething(3, 4); 參數是按值傳遞的。這意味著如果你修改了value1的值,它的值只會在本地中被修改,並且在函數外部,它被傳入調用中的值不會改變。 如果你將一個指針作為參數傳遞,你可以修改該變量的值,因為你現在可以直接使用其內存地址訪問它。 你不能為參數定義默認值。C++可以這樣做(因此Arduino語言程序可以這樣做),但C不能。...

C指標介紹

C指標介紹 在我看來,指標是C語言中最令人困惑/挑戰的部分之一。特別是對於新手程序員來說,但即使你來自於像Python或JavaScript這樣的高階編程語言,也一樣如此。 在這篇文章中,我想以最簡單但不過度簡化的方式來介紹它們。 指標是一個存儲變量內容的內存塊的地址。 當你聲明一個整數變量,像這樣: int age = 37; 我們可以使用&操作符來獲取變量在內存中的地址: printf("%p", &age); /* 0x7ffeef7dcb9c */ 我在printf()中使用了%p格式來打印地址值。 我們可以將地址賦值給一個變量: int *address = &age; 在聲明中使用int *address,我們沒有聲明一個整數變量,而是聲明了一個指向整數的指標。 我們可以使用指標運算符*來獲取地址指向的變量的值: int age = 37; int *address = &age; printf("%u", *address); /* 37 */ 這一次我們再次使用了指標運算符,但這次不是聲明,而是表示“這個指針指向的變量的值”。 在這個例子中,我們聲明了一個age變量,並使用指標來初始化其值: int age; int *address = &age; *address = 37; printf("%u", *address); 在使用C語言時,你會發現很多事情都是基於這個簡單的概念構建的,所以請確保你能夠稍微熟悉一下,通過在你自己的編程環境中運行上述示例來實踐一下。 指標是一個很好的機會,因為它們強迫我們思考內存地址和數據的組織方式。 數組就是一個例子。當你聲明一個數組時: int prices[3] = { 5, 4, 3 }; prices變量實際上是指向數組第一個項目的指標。在這種情況下,你可以使用以下printf()函數獲取第一個項目的值: printf("%u", *prices); /* 5 */ 酷的是,我們可以通過將1添加到prices指針中來獲取第二個項目: printf("%u", *(prices + 1)); /* 4 */ 以此類推,對於所有其他的值也是如此。...

C陣列介紹

一個C陣列的介紹 陣列是一個可以儲存多個值的變數。 在C語言中,陣列中的每個值必須有相同的類型。這意味著你可以有int值的陣列,也可以有double值的陣列,以此類推。 你可以像這樣定義一個int值的陣列: int prices[5]; 你必須總是指定陣列的大小。C本身並不提供動態陣列(你需要使用像是鏈表之類的資料結構來實現)。 你可以使用常數來定義陣列的大小: const int SIZE = 5; int prices[SIZE]; 你可以在定義時初始化陣列,像是這樣: int prices[5] = { 1, 2, 3, 4, 5 }; 但你也可以在定義之後賦予值,像這樣: int prices[5]; prices[0] = 1; prices[1] = 2; prices[2] = 3; prices[3] = 4; prices[4] = 5; 或者,更實際的做法是使用迴圈: int prices[5]; for (int i = 0; i < 5; i++) { prices[i] = i + 1; } 你可以使用方括號在陣列變數名稱後面指定索引值,來參考陣列中的某個項目。像這樣: prices[0]; /* 陣列項目值:1 */ prices[1]; /* 陣列項目值:2 */ 陣列的索引從0開始,所以一個有5個項目的陣列,像上面的prices陣列,其項目範圍從prices[0]到prices[4]。...

C程式語言介紹

開始學習C,一種基礎的程式語言 C可能是最廣為人知的程式語言。它被全球各地的電腦科學課程作為參考語言,可能和Python和Java一起是人們在學校學得最多的語言。 我記得它是我學到的第二種程式語言,之後是Pascal。 C不僅僅是學生用來學習程式設計的語言,它不是一門學術語言。而且我會說它並不是最容易的語言,因為C是一種相對較低階的程式語言。 今天,在嵌入式設備中廣泛使用C,它也為大多數使用Linux的網絡服務器提供動力。Linux內核使用C編寫,這也意味著C動力了所有Android設備的核心。我們可以說C代碼運行了整個世界的很大一部分。真是令人驚訝。 在C誕生之時,它被認為是一種高階語言,因為它在各種機器上都可移植。今天我們基本上認為,可以在Mac上運行在Windows或Linux上的程序,也許可以使用Node.js或Python。但在過去,根本不是這種情況。C所帶來的是一種易於實現的語言,具有可以輕易移植到不同機器的編譯器。 我提到了編譯器:C是一種編譯語言,就像Go、Java、Swift或Rust一樣。其他流行的編程語言如Python、Ruby或JavaScript則是解釋語言。差異是顯著的:編譯語言會生成一個可以直接執行和分發的二進製文件。 C不會進行垃圾回收。這意味著我們必須自己管理內存。這是一個複雜的任務,需要非常小心地防止bug,但這也是C成為編寫Arduino等嵌入式設備程序的理想語言。 C不隱藏底層機器的複雜性和功能。一旦你知道你可以做什麼,你就擁有了很大的能力。 現在,我想介紹第一個C程序,我們將它稱為“Hello, World!” #include <stdio.h> int main(void) { printf("Hello, World!"); } 讓我們來描述一下程式原始碼:首先我們引入了stdio庫(這個名稱代表標準輸入輸出庫)。 這個庫讓我們可以使用輸入/輸出函數。 C本身非常小,除了核心部分,其他任何東西都是由庫提供的。這些庫有些是由普通程序員編寫的,並提供給其他人使用。還有一些庫是編譯器內建的,像stdio和其他庫一樣。 stdio是提供printf()函數的庫。 這個函數被包裝在main()函數中。main()函數是任何C程序的入口點。 那麼,什麼是函數? 函數是一個例程,它接受一個或多個參數並返回一個單一值。 在main()的情況下,該函數不接受任何參數,並返回一個整數。在參數中使用void關鍵字來識別,並使用int關鍵字來聲明返回值。 函數有一個函數體,它用大括號括起來,在函數體內我們放置了函數執行所需的所有代碼。 printf()函數的寫法不同,正如你所見。它沒有定義返回值,我們傳入一個用雙引號括起來的字符串。我們沒有指定參數的類型。 這是因為這是一個函數調用。在stdio庫的某個地方,printf被定義為 int printf(const char *format, ...); 你現在不需要理解這意味著什麼,但簡而言之,這就是函數的定義,當我們調用printf("Hello, World!");時,函數就執行了。 上面我們定義的main()函數: #include <stdio.h> int main(void) { printf("Hello, World!"); } 在執行程序時,將由操作系統來執行。 那麼,如何執行C程序? 如前所述,C是一種編譯語言。要運行程序,我們必須首先編譯它。任何Linux或macOS電腦都已內建了C編譯器。對於Windows,您可以使用Windows Subsystem for Linux(WSL)。 無論如何,當您打開終端窗口時,可以輸入gcc,這個命令應該會返回一個錯誤,說您沒有指定任何文件: 這很好。這意味著C編譯器存在,我們可以開始使用它。 現在輸入上面的程序到一個hello.c文件中。您可以使用任何編輯器,但出於簡單起見,我將使用終端中的nano編輯器: 輸入程序: 現在按下ctrl-X退出: 按下y鍵確認,然後按回車鍵確認文件名: 這樣,我們應該回到終端: 現在輸入 gcc hello.c -o hello 該程序不應該產生任何錯誤: 但它應該生成一個hello 可執行文件。現在輸入 ./hello 來運行它: 我在程序名前面加了./以告訴終端該指令位於當前文件夾...

C運算子

介紹C運算子和運算符優先順序 C提供了各種各樣的運算子,我們可以用它們來操作數據。 特別是,我們可以識別出各種組運算符: 算術運算符 比較運算符 邏輯運算符 複合賦值運算符 位運算符 指針運算符 結構運算符 其他運算符 在這篇博文中,我將詳細介紹它們,並使用兩個虛擬變量a和b作為例子。 我會專門為位運算符、結構運算符和指針運算符撰寫另一篇博文。 算術運算符 在這個大組中,我將區分二元運算符和一元運算符。 二元運算符使用兩個操作數進行運算: 運算符 名稱 示例 = 賦值 a = b + 加法 a + b - 減法 a - b * 乘法 a * b / 除法 a / b % 模除 a % b 一元運算符只需要一個操作數: 運算符 名稱 示例 + 正數 +a - 負數 -a ++ 自增 a++或++a -- 自減 a--或--a a++和++a的區別在於a++在使用後對a進行自增。++a在使用之前對a進行自增。 舉個例子: int a = 2; int b; b = a++ /* b為2,a為3 */ b = ++a /* b為4,a為4 */ 遞減運算符也是一樣的道理。...

C轉換格式符和修飾符

一個有用的 C 轉換格式符和修飾符的參考手冊。 在這篇文章中,我想為你列出所有可以在 printf()、scanf() 和類似的輸入輸出函數中使用的 轉換格式符。 格式符 含義 %d / %i 有符號十進制整數 %u 無符號十進制整數 %c 無符號 char %s 字符串 %p 指針的十六進制形式 %o 無符號八進制整數 %x / %X 無符號十六進制數 %e 科學記號表示的浮點數(使用小寫 e) %E 科學記號表示的浮點數(使用大寫 E) %f 十進制表示的 double 數字 %g / %G 根據值,在十進制或科學記號中表示的 double 數字 除了這些格式符,我們還有一組 修飾符。 首先是 數字。在 % 和格式符之間使用一個數字,你可以指定最小的字段寬度。例如:%3d 會佔用 3 個空間,不管打印的數字是什麼。 下面這段代碼: printf("%4d\n", 1); printf("%4d\n", 12); printf("%4d\n", 123); printf("%4d\n", 1234); 應該打印出: 1 12 123 1234 如果在數字之前加上一個點,則表示精度:即小數位數。這當然適用於十進制數字。例如: printf("%4.2f\n", 1.0); printf("%4....

C變數和類型

初次接觸C變數和基本類型的介紹 C是一種靜態類型語言。 這意味著任何變數都有一個關聯的類型,而且此類型在編譯時已知。 這與在Python、JavaScript、PHP和其他解釋語言中操作變量的方式非常不同。 在C中創建變量時,您必須在聲明時指定變量的類型。 在此示例中,我們使用類型int對變量age進行初始化: int age; 變量名可以包含任何大小寫字母,可以包含數字和下劃線字符,但不能以數字開頭。AGE和Age10都是有效的變量名,1age則無效。 您也可以在聲明時初始化變量,並指定初始值: int age = 37; 定義變量後,您可以在程序代碼中使用它,並隨時更改其值,例如使用=運算符,就像在age = 100;中一樣,前提是新值具有相同的類型。 在這種情況下: #include <stdio.h> int main(void) { int age = 0; age = 37.2; printf("%u", age); } 編譯器將在編譯時拋出警告,並將十進制數字轉換為整數值。 C的內置數據類型包括int、char、short、long、float、double和long double。讓我們更深入了解這些類型。 整數數字 C為我們提供以下類型來定義整數值: char int short long 大多數情況下,您可能會使用int來存儲整數。但在某些情況下,您可能希望選擇另外3個選項之一。 char類型通常用於存儲ASCII表中的字母,但也可以用來容納從-128到127的小整數。它至少占1個字節。 int至少占2個字節。 short至少占2個字節。 long至少占4個字節。 正如您所看到的,對於不同環境,我們無法保證相同的值。我們只有一個指示。問題在於每種數據類型中可以存儲的確切數字取決於實現和體系結構。 我們保證short不會比int長,并且保證long不會比int短。 美國國家標準協會(ANSI)的C規範標準確定了每種類型的最小值,幸運的是,由於此規範,我們至少可以知道我們可以期望擁有的最小值。 如果您在Arduino上編程C,不同的開發板將具有不同的限制。 在Arduino Uno板上,int存儲2字節值,範圍從-32,768到32,767。在Arduino MKR 1010上,int存儲4字節值,範圍從-2,147,483,648到2,147,483,647。相差很大。 在所有Arduino板上,short存儲2字節值,範圍從-32,768到32,767。long占用4字節,范围从-2,147,483,648到2,147,483,647。 無符號整數 對於上述所有數據類型,我們可以在前面加上unsigned以將範圍從0開始,而不是負數。在許多情況下,這可能是有意義的。 unsigned char的範圍從0到至少255 unsigned int的範圍從0到至少65,535 unsigned short的範圍從0到至少65,535 unsigned long的範圍從0到至少4,294,967,295 溢出問題 鑒於所有這些限制,可能會出現一個問題:我們如何確保我們的數字不超過限制?如果超過限制我們會得到什麼結果? 如果您具有unsigned int數字為255,並將其增加,您將得到256作為結果。這是可以預料的。 如果您的unsigned char數字為255,並將其增加,您將得到0作為結果。它會從初始可能值重新開始計算。...

DataView物件

了解DataView物件及其使用方法 DataView是一個對ArrayBuffer的視圖,類似於Typed Arrays,但是在這種情況下,數組中的項目可以具有不同的大小和類型。 以下是一個例子: const buffer = new ArrayBuffer(64) const view = new DataView(buffer) 因為這是對緩衝區的視圖,我們可以指定從哪個字節開始,以及長度: const view = new DataView(buffer, 10) //從第10個字節開始 const view = new DataView(buffer, 10, 30) //從第10個字節開始,添加30個項目 如果不添加這些附加參數,則該視圖從位置0開始,並加載緩衝區中的所有字節。 我們可以使用一組方法將數據添加到緩衝區中: setInt8() setInt16() setInt32() setUint8() setUint16() setUint32() setFloat32() setFloat64() 這是如何調用其中一個方法的示例: const buffer = new ArrayBuffer(64) const view = new DataView(buffer) view.setInt16(0, 2019) 默認情況下,數據使用big endian表示。您可以通過添加第三個帶有true值的參數來覆蓋此設置並使用little endian: const buffer = new ArrayBuffer(64) const view = new DataView(buffer) view.setInt16(0, 2019, true) 以下是如何從視圖中獲取數據:...

deno

#Deno手冊:Deno的簡潔介紹 🦕 快速了解Deno,這是一個現代的Node.js替代方案 我每週都會探索新的專案,而且很少有像Deno這樣引起我的注意。 Deno是什麼? 如果你熟悉Node.js,那麼Deno就像Node一樣。但在很多方面上有很大的改進。 讓我們從一個我最喜歡Deno的快速功能列表開始: 它是基於JavaScript語言的現代特性 它擁有豐富的標準庫 它的核心是TypeScript,這在很多方面都有很大的優勢,包括一流的TypeScript支持(您不需要單獨編譯TypeScript,Deno會自動處理) 它採用ES模塊 它沒有包管理器 它有一流的等待機制 它有內置的測試工具 它的目標是盡可能與瀏覽器兼容,例如提供了內置的fetch和全局的window對象。 在本指南中,我們將探索所有這些功能。 在您使用Deno並學會欣賞其功能之後,Node.js將會變得像一個“老古董”。 特別是因為Node.js的API是基於回調的,因為它是在Promises和async/await之前開發的。目前在Node中沒有這方面的改變,因為這樣的改變將是巨大的,所以我們只能使用回調或將API調用轉換為Promises形式。 Node.js是驚人的,並且將繼續是JavaScript世界中的事實標準。但我認為由於其對TypeScript的一流支持和現代的標準庫,我們會逐漸看到Deno更多地被採用。 Deno能夠采用現代技術編寫所有代碼,因為它不需要維護向後兼容性。當然,在十年後,無法保證Deno仍然如此,可能會出現一種新技術,但這正是目前的現實情況。 為什麼選擇Deno?為什麼現在? Deno在將近兩年前由Node.js的原始創作者Ryan Dahl在JSConf EU上宣布。觀看演講的YouTube視頻,它非常有趣,如果你從事Node.js和JavaScript開發,這是一個必看的。 每個項目經理都需要做出決策。Ryan後悔Node的一些早期決策。此外,技術是在不斷發展的,今天的JavaScript與2009年Node開始時完全不同。想想現代的ES6/2016/2017功能等等。 所以他開始了一個新項目,創建了第二波JavaScript驅動的服務器端應用程序。 我現在寫這篇指南,而不是之前,是因為技術需要很長時間成熟。我們終於到了Deno 1.0(Denno 1.0應在2020年5月13日發布),Deno的第一個官方穩定版本。 這只是一個數字,但1.0意味著在Deno 2.0之前不會有重大的破壞性變化,在開始學習一個新技術時這非常重要- 您不希望學習某些東西然後它就過時了。 您應該學習Deno嗎? 這是一個很大的問題。 學習與Deno這樣的新東西是一個很大的努力。我的建議是,如果您現在開始使用服務器端JavaScript,並且對Node還不熟悉,也從來沒有寫過任何TypeScript,那麼我會從Node開始。 沒有人因為選擇Node.js而失業(引用一個常見的說法)。 但是,如果你喜歡TypeScript,在項目中不依賴於大量的npm包,並且想要隨處使用await,那麼Deno可能是你在找的東西。 它能取代Node.js嗎? 不。Node.js是一個巨大的、很好支持的技術,在未來幾十年都會存在。 一流的TypeScript支持 Deno使用Rust和TypeScript編寫,這兩種語言在目前都有很大的增長。 特別是使用TypeScript編寫意味著,即使我們可能選擇使用純JavaScript編寫我們的代碼,但我們仍然能夠從TypeScript中獲得很多好處。 而使用Deno運行TypeScript代碼不需要編譯步驟- Deno會自動為您完成。 您不需要寫TypeScript,但Deno的核心部分是使用TypeScript編寫的。 首先,越來越多的JavaScript程序員喜歡TypeScript。 其次,您使用的工具可以從在TypeScript中編寫的軟件中推斷出很多信息,例如Deno等。 這意味著,當我們例如在VS Code中編寫代碼時,由於VS Code和TypeScript在MicroSoft開發,它們之間有緊密的集成,我們可以獲得類似於在編寫代碼時的類型檢查和高級的智能提示功能。換句話說,編輯器可以以非常有用的方式幫助我們。 與Node.js相似和不同的地方 因Deno基本上是Node.js的替代品,所以直接進行比較是有用的。 相似之處: 二者都是基於V8 Chromium Engine開發的 二者在使用JavaScript進行服務器端開發時都非常出色 不同之處: Node.js是使用C++和JavaScript編寫的,而Deno是使用Rust和TypeScript編寫的 Node具有一個名為npm的官方包管理器,而Deno則沒有,相反,它允許您從URL導入任何ES模塊 Node使用CommonJS標準來導入包,而Deno使用官方的ES模塊方式 Deno在其API和標準庫中使用了現代的ECMAScript特性,而Node.js使用基於回調的標準庫,並且沒有升級的計劃 Deno通過權限提供了一個沙箱安全層。程序只能訪問由用戶作為標誌設置的許可權。一個Node.js程序可以訪問用戶能夠訪問的任何內容 Deno長期以來一直設想將程序編譯為可執行文件,而不需要外部依賴,就像Go語言一樣,但目前還沒有實現。 這將是一個遊戲規則的改變。 沒有包管理器 沒有包管理器並且依賴於URL來主機和導入包是有優點和缺點的。我真的很喜歡這些優點:它是非常靈活的,我們可以創建包而無需將其發布到類似於npm的存儲庫。 我認為會出現某種類型的包管理器,但官方尚未公布任何消息。 Deno網站提供代碼主機(並通過URL進行分發)給第三方包: https://deno.land/x/...